tecton 0.4.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/LICENSE +21 -0
- package/README.md +179 -0
- package/bin/tecton.js +417 -0
- package/brand/tecton-favicon.svg +2 -0
- package/package.json +60 -0
- package/src/diff.js +93 -0
- package/src/graph.js +130 -0
- package/src/layout.js +191 -0
- package/src/report/dist/client.js +3 -0
- package/src/report/fonts/Geist-Variable.woff2 +0 -0
- package/src/report/fonts/GeistMono-Variable.woff2 +0 -0
- package/src/report/fonts/OFL-LICENSE.txt +92 -0
- package/src/report/template.html +619 -0
- package/src/report.js +110 -0
- package/src/rules.js +82 -0
- package/src/scan.js +306 -0
- package/src/system.js +672 -0
- package/src/types.d.ts +207 -0
|
@@ -0,0 +1,619 @@
|
|
|
1
|
+
<!doctype html>
|
|
2
|
+
<html lang="en">
|
|
3
|
+
<head>
|
|
4
|
+
<meta charset="utf-8">
|
|
5
|
+
<meta name="viewport" content="width=device-width, initial-scale=1">
|
|
6
|
+
<title>__TITLE__</title>
|
|
7
|
+
<link rel="icon" type="image/svg+xml" href="__FAVICON__">
|
|
8
|
+
<meta name="theme-color" content="#0E0F13">
|
|
9
|
+
<style>__FONTS__</style>
|
|
10
|
+
<style>
|
|
11
|
+
/* ============ tokens ============ */
|
|
12
|
+
:root {
|
|
13
|
+
color-scheme: light;
|
|
14
|
+
--font: "Geist", "Inter", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto, sans-serif;
|
|
15
|
+
--mono: "Geist Mono", ui-monospace, SFMono-Regular, Menlo, Consolas, monospace;
|
|
16
|
+
--bg: #F6F5F2; --surface: #ffffff; --surface-2: #F3F2EF; --surface-3: #EAE8E3;
|
|
17
|
+
--border: #E6E3DC; --border-strong: #D5D2CA;
|
|
18
|
+
--text: #0E0F13; --text-2: #52525b; --text-3: #8a8a93;
|
|
19
|
+
--accent: #5b5bd6; --accent-2: #7b61ff; --accent-soft: #eeeefd; --accent-text: #4343b8;
|
|
20
|
+
--add: #16a34a; --add-soft: #e8f7ee; --add-text: #15803d;
|
|
21
|
+
--rem: #dc2626; --rem-soft: #fdeded; --rem-text: #b91c1c;
|
|
22
|
+
--viol: #ea580c; --viol-soft: #fff0e6; --viol-text: #c2410c;
|
|
23
|
+
--warn: #d99a00; --warn-soft: #fdf5dc; --warn-text: #92620a;
|
|
24
|
+
--chg: #2a78d6; --chg-soft: #e8f1fc; --chg-text: #1c5cab;
|
|
25
|
+
--neutral-bar: #d4d4d8;
|
|
26
|
+
--line: #a1a1aa; --dot: #d9d9de;
|
|
27
|
+
--shadow-sm: 0 1px 2px rgba(16,16,24,.05);
|
|
28
|
+
--shadow: 0 1px 2px rgba(16,16,24,.04), 0 4px 12px rgba(16,16,24,.06);
|
|
29
|
+
--shadow-lg: 0 12px 40px rgba(16,16,24,.14), 0 2px 6px rgba(16,16,24,.06);
|
|
30
|
+
--ring: 0 0 0 3px color-mix(in srgb, var(--accent) 28%, transparent);
|
|
31
|
+
--radius: 12px; --radius-sm: 8px;
|
|
32
|
+
--ease: cubic-bezier(.2,.8,.2,1); --ease-spring: cubic-bezier(.34,1.56,.64,1);
|
|
33
|
+
}
|
|
34
|
+
@media (prefers-color-scheme: dark) {
|
|
35
|
+
:root:not([data-theme="light"]) {
|
|
36
|
+
color-scheme: dark;
|
|
37
|
+
--bg: #09090b; --surface: #111113; --surface-2: #18181b; --surface-3: #202024;
|
|
38
|
+
--border: #232327; --border-strong: #303036;
|
|
39
|
+
--text: #fafafa; --text-2: #a1a1aa; --text-3: #71717a;
|
|
40
|
+
--accent: #7c7ff2; --accent-2: #9d8cff; --accent-soft: #1b1b36; --accent-text: #a5a7ff;
|
|
41
|
+
--add: #22c55e; --add-soft: #0e2216; --add-text: #4ade80;
|
|
42
|
+
--rem: #ef4444; --rem-soft: #2a1113; --rem-text: #f87171;
|
|
43
|
+
--viol: #f97316; --viol-soft: #2a1608; --viol-text: #fb923c;
|
|
44
|
+
--warn: #eab308; --warn-soft: #272008; --warn-text: #facc15;
|
|
45
|
+
--chg: #3987e5; --chg-soft: #0f1d31; --chg-text: #6aa8f0;
|
|
46
|
+
--neutral-bar: #3f3f46; --line: #5f5f68; --dot: #26262b;
|
|
47
|
+
--shadow-sm: none; --shadow: 0 1px 0 rgba(255,255,255,.03) inset, 0 6px 16px rgba(0,0,0,.35);
|
|
48
|
+
--shadow-lg: 0 16px 48px rgba(0,0,0,.6);
|
|
49
|
+
}
|
|
50
|
+
}
|
|
51
|
+
:root[data-theme="dark"] {
|
|
52
|
+
color-scheme: dark;
|
|
53
|
+
--bg: #09090b; --surface: #111113; --surface-2: #18181b; --surface-3: #202024;
|
|
54
|
+
--border: #232327; --border-strong: #303036;
|
|
55
|
+
--text: #fafafa; --text-2: #a1a1aa; --text-3: #71717a;
|
|
56
|
+
--accent: #7c7ff2; --accent-2: #9d8cff; --accent-soft: #1b1b36; --accent-text: #a5a7ff;
|
|
57
|
+
--add: #22c55e; --add-soft: #0e2216; --add-text: #4ade80;
|
|
58
|
+
--rem: #ef4444; --rem-soft: #2a1113; --rem-text: #f87171;
|
|
59
|
+
--viol: #f97316; --viol-soft: #2a1608; --viol-text: #fb923c;
|
|
60
|
+
--warn: #eab308; --warn-soft: #272008; --warn-text: #facc15;
|
|
61
|
+
--chg: #3987e5; --chg-soft: #0f1d31; --chg-text: #6aa8f0;
|
|
62
|
+
--neutral-bar: #3f3f46; --line: #5f5f68; --dot: #26262b;
|
|
63
|
+
--shadow-sm: none; --shadow: 0 1px 0 rgba(255,255,255,.03) inset, 0 6px 16px rgba(0,0,0,.35);
|
|
64
|
+
--shadow-lg: 0 16px 48px rgba(0,0,0,.6);
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
/* ============ base ============ */
|
|
68
|
+
* { box-sizing: border-box; }
|
|
69
|
+
html, body { margin: 0; height: 100%; }
|
|
70
|
+
body { background: var(--bg); color: var(--text); font: 14px/1.5 var(--font); -webkit-font-smoothing: antialiased; letter-spacing: -0.006em; font-feature-settings: "ss01", "cv11"; }
|
|
71
|
+
button, input { font: inherit; color: inherit; letter-spacing: inherit; }
|
|
72
|
+
button { cursor: pointer; }
|
|
73
|
+
a { color: inherit; }
|
|
74
|
+
code, .mono { font-family: var(--mono); font-size: 12.5px; letter-spacing: 0; }
|
|
75
|
+
svg.i { width: 16px; height: 16px; flex: none; stroke: currentColor; fill: none; stroke-width: 1.9; stroke-linecap: round; stroke-linejoin: round; }
|
|
76
|
+
:focus-visible { outline: none; box-shadow: var(--ring); border-radius: 6px; }
|
|
77
|
+
::selection { background: var(--accent-soft); }
|
|
78
|
+
.muted { color: var(--text-2); }
|
|
79
|
+
.faint { color: var(--text-3); }
|
|
80
|
+
.num { font-variant-numeric: tabular-nums; }
|
|
81
|
+
.sr { position: absolute; width: 1px; height: 1px; overflow: hidden; clip: rect(0 0 0 0); }
|
|
82
|
+
|
|
83
|
+
/* ============ shell ============ */
|
|
84
|
+
.app { display: grid; grid-template-columns: 248px minmax(0, 1fr); height: 100vh; }
|
|
85
|
+
.sidebar { background: var(--surface); border-right: 1px solid var(--border); display: flex; flex-direction: column; padding: 14px 12px; gap: 14px; min-height: 0; overflow-y: auto; z-index: 30; }
|
|
86
|
+
.logo { display: flex; align-items: center; gap: 10px; padding: 4px 6px; }
|
|
87
|
+
.logo-mark { width: 28px; height: 28px; border-radius: 6.3px; background: linear-gradient(180deg, #23242D, #0B0C10); display: grid; place-items: center; box-shadow: inset 0 0 0 1px rgba(255,255,255,.08), 0 2px 6px rgba(14,15,19,.18); flex: none; }
|
|
88
|
+
.logo-mark svg { width: 18px; height: 18px; }
|
|
89
|
+
.logo-mark .pl { fill: #FAFAF7; } .logo-mark .pl-shift { fill: #7C7FF2; }
|
|
90
|
+
.logo b { font-size: 17px; letter-spacing: -0.055em; font-weight: 600; }
|
|
91
|
+
.ver { font: 500 10.5px var(--mono); color: var(--text-3); border: 1px solid var(--border); border-radius: 999px; padding: 1px 6px; margin-left: auto; }
|
|
92
|
+
.project { border: 1px solid var(--border); border-radius: var(--radius-sm); padding: 10px; background: var(--surface-2); display: grid; gap: 6px; }
|
|
93
|
+
.project .name { font-weight: 600; display: flex; align-items: center; gap: 8px; min-width: 0; }
|
|
94
|
+
.project .name span { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
95
|
+
.avatar { width: 22px; height: 22px; border-radius: 6px; display: grid; place-items: center; font-size: 11px; font-weight: 700; color: var(--accent-text); background: var(--accent-soft); flex: none; }
|
|
96
|
+
.compare { display: grid; grid-template-columns: 16px minmax(0, 1fr); align-items: center; gap: 4px 6px; font-size: 12px; color: var(--text-2); }
|
|
97
|
+
.compare .vs { text-align: center; font-size: 11px; color: var(--text-3); }
|
|
98
|
+
.compare .ref { justify-self: start; }
|
|
99
|
+
.ref { font: 500 11.5px var(--mono); background: var(--surface); border: 1px solid var(--border); padding: 1px 6px; border-radius: 5px; color: var(--text); max-width: 100%; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
100
|
+
.nav-label { font-size: 11px; font-weight: 600; color: var(--text-3); text-transform: uppercase; letter-spacing: .06em; padding: 0 8px; margin-bottom: -6px; }
|
|
101
|
+
nav.nav { display: grid; gap: 2px; }
|
|
102
|
+
.nav a { display: flex; align-items: center; gap: 10px; padding: 7px 8px; border-radius: 7px; text-decoration: none; color: var(--text-2); font-weight: 500; position: relative; transition: background .15s, color .15s; }
|
|
103
|
+
.nav a:hover { background: var(--surface-2); color: var(--text); }
|
|
104
|
+
.nav a.active { background: var(--surface-2); color: var(--text); }
|
|
105
|
+
.nav a.active::before { content: ""; position: absolute; left: -12px; top: 8px; bottom: 8px; width: 3px; border-radius: 0 3px 3px 0; background: var(--accent); animation: grow .3s var(--ease); }
|
|
106
|
+
@keyframes grow { from { transform: scaleY(0); } }
|
|
107
|
+
.nav .count { margin-left: auto; font: 500 11px var(--mono); color: var(--text-3); background: var(--surface-2); border: 1px solid var(--border); border-radius: 999px; padding: 0 7px; min-width: 22px; text-align: center; }
|
|
108
|
+
.nav .count.bad { color: var(--viol-text); background: var(--viol-soft); border-color: transparent; }
|
|
109
|
+
.side-foot { margin-top: auto; display: grid; gap: 10px; }
|
|
110
|
+
.theme-seg { display: grid; grid-template-columns: repeat(3, 1fr); background: var(--surface-2); border: 1px solid var(--border); border-radius: 8px; padding: 2px; }
|
|
111
|
+
.theme-seg button { border: 0; background: none; padding: 5px; border-radius: 6px; color: var(--text-3); display: grid; place-items: center; transition: background .15s, color .15s; }
|
|
112
|
+
.theme-seg button[aria-pressed="true"] { background: var(--surface); color: var(--text); box-shadow: var(--shadow-sm); }
|
|
113
|
+
.gen { font-size: 12px; color: var(--text-3); display: flex; align-items: center; gap: 6px; padding: 0 4px; }
|
|
114
|
+
|
|
115
|
+
.main { display: flex; flex-direction: column; min-width: 0; min-height: 0; }
|
|
116
|
+
.topbar { height: 56px; flex: none; display: flex; align-items: center; gap: 12px; padding: 0 20px; border-bottom: 1px solid var(--border); background: color-mix(in srgb, var(--bg) 82%, transparent); backdrop-filter: saturate(1.4) blur(10px); position: sticky; top: 0; z-index: 20; }
|
|
117
|
+
.crumbs { display: flex; align-items: center; gap: 8px; min-width: 0; font-weight: 500; }
|
|
118
|
+
.crumbs .sep { color: var(--text-3); }
|
|
119
|
+
.crumbs .cur { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
120
|
+
.btn.menu-btn { display: none; }
|
|
121
|
+
.top-actions { margin-left: auto; display: flex; align-items: center; gap: 8px; }
|
|
122
|
+
.search-btn { display: flex; align-items: center; gap: 8px; height: 32px; padding: 0 8px 0 10px; border: 1px solid var(--border); background: var(--surface); border-radius: 8px; color: var(--text-3); min-width: 220px; box-shadow: var(--shadow-sm); transition: border-color .15s; }
|
|
123
|
+
.search-btn:hover { border-color: var(--border-strong); }
|
|
124
|
+
.kbd { font: 500 11px var(--mono); border: 1px solid var(--border); border-bottom-width: 2px; border-radius: 5px; padding: 0 5px; color: var(--text-2); background: var(--surface-2); margin-left: auto; }
|
|
125
|
+
.btn { display: inline-flex; align-items: center; gap: 7px; height: 32px; padding: 0 12px; border-radius: 8px; border: 1px solid var(--border); background: var(--surface); font-weight: 500; font-size: 13px; box-shadow: var(--shadow-sm); transition: background .15s, border-color .15s, transform .1s; white-space: nowrap; }
|
|
126
|
+
.btn:hover { background: var(--surface-2); border-color: var(--border-strong); }
|
|
127
|
+
.btn:active { transform: scale(.97); }
|
|
128
|
+
.btn.primary { background: var(--text); color: var(--bg); border-color: var(--text); }
|
|
129
|
+
.btn.primary:hover { opacity: .9; background: var(--text); }
|
|
130
|
+
.btn.icon { width: 32px; padding: 0; justify-content: center; }
|
|
131
|
+
.btn.ghost { border-color: transparent; background: transparent; box-shadow: none; }
|
|
132
|
+
.btn.ghost:hover { background: var(--surface-2); }
|
|
133
|
+
.status-pill { display: inline-flex; align-items: center; gap: 6px; height: 26px; padding: 0 10px; border-radius: 999px; font-size: 12.5px; font-weight: 600; white-space: nowrap; }
|
|
134
|
+
.status-pill.fail { background: var(--rem-soft); color: var(--rem-text); }
|
|
135
|
+
.status-pill.pass { background: var(--add-soft); color: var(--add-text); }
|
|
136
|
+
.status-pill .pulse { width: 7px; height: 7px; border-radius: 50%; background: currentColor; position: relative; }
|
|
137
|
+
.status-pill .pulse::after { content: ""; position: absolute; inset: 0; border-radius: 50%; background: currentColor; animation: ping 1.8s var(--ease) infinite; }
|
|
138
|
+
@keyframes ping { 0% { transform: scale(1); opacity: .6; } 100% { transform: scale(2.8); opacity: 0; } }
|
|
139
|
+
|
|
140
|
+
.content { flex: 1; overflow-y: auto; min-height: 0; }
|
|
141
|
+
.page { display: none; padding: 24px 28px 48px; max-width: 1240px; margin: 0 auto; }
|
|
142
|
+
.page.active { display: block; animation: pageIn .35s var(--ease); }
|
|
143
|
+
.page.full { max-width: none; padding: 16px; height: 100%; }
|
|
144
|
+
.page.full.active { display: flex; flex-direction: column; }
|
|
145
|
+
@keyframes pageIn { from { opacity: 0; transform: translateY(6px); } }
|
|
146
|
+
.page-head { display: flex; align-items: flex-end; gap: 16px; margin-bottom: 20px; flex-wrap: wrap; }
|
|
147
|
+
.page-head h1 { font-size: 22px; letter-spacing: -0.025em; margin: 0; font-weight: 650; }
|
|
148
|
+
.page-head p { margin: 2px 0 0; color: var(--text-2); }
|
|
149
|
+
.page-head .right { margin-left: auto; display: flex; gap: 8px; flex-wrap: wrap; }
|
|
150
|
+
|
|
151
|
+
/* ============ components ============ */
|
|
152
|
+
.card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); box-shadow: var(--shadow); }
|
|
153
|
+
.card-h { display: flex; align-items: center; gap: 10px; padding: 14px 16px 0; }
|
|
154
|
+
.card-h h3 { margin: 0; font-size: 14px; font-weight: 600; letter-spacing: -0.01em; }
|
|
155
|
+
.card-h .right { margin-left: auto; }
|
|
156
|
+
.card-b { padding: 14px 16px 16px; }
|
|
157
|
+
.link-btn { border: 0; background: none; color: var(--text-2); font-size: 12.5px; font-weight: 500; display: inline-flex; align-items: center; gap: 4px; padding: 2px 4px; border-radius: 6px; }
|
|
158
|
+
.link-btn:hover { color: var(--text); background: var(--surface-2); }
|
|
159
|
+
.stagger > * { animation: rise .5s var(--ease) both; }
|
|
160
|
+
.stagger > *:nth-child(2) { animation-delay: .05s; } .stagger > *:nth-child(3) { animation-delay: .1s; }
|
|
161
|
+
.stagger > *:nth-child(4) { animation-delay: .15s; } .stagger > *:nth-child(5) { animation-delay: .2s; }
|
|
162
|
+
.stagger > *:nth-child(6) { animation-delay: .25s; }
|
|
163
|
+
@keyframes rise { from { opacity: 0; transform: translateY(10px); } }
|
|
164
|
+
|
|
165
|
+
.hero { display: flex; gap: 16px; align-items: flex-start; padding: 20px; position: relative; overflow: hidden; }
|
|
166
|
+
.hero::after { content: ""; position: absolute; right: -80px; top: -120px; width: 320px; height: 320px; border-radius: 50%; background: radial-gradient(closest-side, color-mix(in srgb, var(--hero-c) 16%, transparent), transparent); pointer-events: none; }
|
|
167
|
+
.hero.fail { --hero-c: var(--rem); } .hero.pass { --hero-c: var(--add); }
|
|
168
|
+
.hero-icon { width: 44px; height: 44px; border-radius: 12px; display: grid; place-items: center; flex: none; }
|
|
169
|
+
.hero.fail .hero-icon { background: var(--rem-soft); color: var(--rem-text); }
|
|
170
|
+
.hero.pass .hero-icon { background: var(--add-soft); color: var(--add-text); }
|
|
171
|
+
.hero-icon svg.i { width: 22px; height: 22px; }
|
|
172
|
+
.hero h2 { margin: 0; font-size: 18px; letter-spacing: -0.02em; font-weight: 650; }
|
|
173
|
+
.hero p { margin: 4px 0 12px; color: var(--text-2); max-width: 640px; }
|
|
174
|
+
.hero .actions { display: flex; gap: 8px; flex-wrap: wrap; }
|
|
175
|
+
.hero .big { margin-left: auto; text-align: right; flex: none; }
|
|
176
|
+
.hero .big .v { font-size: 48px; font-weight: 650; letter-spacing: -0.04em; line-height: 1; }
|
|
177
|
+
.hero .big .l { color: var(--text-2); font-size: 12.5px; margin-top: 6px; }
|
|
178
|
+
|
|
179
|
+
.grid { display: grid; gap: 14px; }
|
|
180
|
+
.stats { grid-template-columns: repeat(4, minmax(0, 1fr)); margin: 14px 0; }
|
|
181
|
+
.stat { padding: 14px 16px; }
|
|
182
|
+
.stat .l { color: var(--text-2); font-size: 12.5px; font-weight: 500; display: flex; align-items: center; gap: 6px; }
|
|
183
|
+
.stat .l svg.i { width: 14px; height: 14px; color: var(--text-3); }
|
|
184
|
+
.stat .v { font-size: 28px; font-weight: 650; letter-spacing: -0.03em; margin: 6px 0 4px; line-height: 1.1; }
|
|
185
|
+
.delta { display: inline-flex; gap: 6px; flex-wrap: wrap; font-size: 12px; }
|
|
186
|
+
.chip { display: inline-flex; align-items: center; gap: 4px; border-radius: 6px; padding: 1px 6px; font: 500 11.5px var(--mono); background: var(--surface-2); color: var(--text-2); white-space: nowrap; }
|
|
187
|
+
.chip.add { background: var(--add-soft); color: var(--add-text); }
|
|
188
|
+
.chip.rem { background: var(--rem-soft); color: var(--rem-text); }
|
|
189
|
+
.chip.viol { background: var(--viol-soft); color: var(--viol-text); }
|
|
190
|
+
.chip.chg { background: var(--chg-soft); color: var(--chg-text); }
|
|
191
|
+
.chip.acc { background: var(--accent-soft); color: var(--accent-text); }
|
|
192
|
+
.chip-row { display: flex; flex-wrap: wrap; align-items: center; gap: 6px; margin: -8px 0 16px; }
|
|
193
|
+
.chip-row.tight { margin: 0; gap: 4px; flex-wrap: nowrap; }
|
|
194
|
+
.two { grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr); }
|
|
195
|
+
|
|
196
|
+
.badge { display: inline-flex; align-items: center; gap: 5px; font-size: 11.5px; font-weight: 600; padding: 1px 8px 1px 7px; border-radius: 999px; white-space: nowrap; border: 1px solid transparent; line-height: 18px; }
|
|
197
|
+
.badge::before { content: ""; width: 6px; height: 6px; border-radius: 50%; background: currentColor; }
|
|
198
|
+
.badge.add, .badge.good { background: var(--add-soft); color: var(--add-text); }
|
|
199
|
+
.badge.rem, .badge.critical { background: var(--rem-soft); color: var(--rem-text); }
|
|
200
|
+
.badge.viol, .badge.serious { background: var(--viol-soft); color: var(--viol-text); }
|
|
201
|
+
.badge.warn { background: var(--warn-soft); color: var(--warn-text); }
|
|
202
|
+
.badge.chg { background: var(--chg-soft); color: var(--chg-text); }
|
|
203
|
+
.badge.acc { background: var(--accent-soft); color: var(--accent-text); }
|
|
204
|
+
.badge.neutral { background: var(--surface-2); color: var(--text-2); border-color: var(--border); }
|
|
205
|
+
.badge.plain::before { display: none; }
|
|
206
|
+
|
|
207
|
+
.stackbar { display: flex; gap: 2px; height: 12px; margin: 6px 0 12px; }
|
|
208
|
+
.stackbar span { height: 100%; border-radius: 2px; transition: filter .15s, transform .15s; transform-origin: left; animation: barIn .8s var(--ease) both; cursor: default; }
|
|
209
|
+
.stackbar span:first-child { border-top-left-radius: 4px; border-bottom-left-radius: 4px; }
|
|
210
|
+
.stackbar span:last-child { border-top-right-radius: 4px; border-bottom-right-radius: 4px; }
|
|
211
|
+
.stackbar span:hover { filter: brightness(1.1); transform: scaleY(1.25); }
|
|
212
|
+
@keyframes barIn { from { transform: scaleX(0); } }
|
|
213
|
+
.legend-row { display: flex; flex-wrap: wrap; gap: 6px 16px; font-size: 12.5px; color: var(--text-2); }
|
|
214
|
+
.legend-row .sw { width: 9px; height: 9px; border-radius: 3px; display: inline-block; margin-right: 6px; vertical-align: -1px; }
|
|
215
|
+
.legend-row b { color: var(--text); font-weight: 600; margin-left: 4px; }
|
|
216
|
+
|
|
217
|
+
.list { display: grid; }
|
|
218
|
+
.row-btn { display: flex; align-items: center; gap: 10px; width: 100%; text-align: left; padding: 10px 16px; border: 0; border-top: 1px solid var(--border); background: none; transition: background .12s; min-width: 0; }
|
|
219
|
+
.row-btn:hover { background: var(--surface-2); }
|
|
220
|
+
.row-btn .main-t { flex: 1; min-width: 0; }
|
|
221
|
+
.row-btn .t1 { font-weight: 500; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
222
|
+
.row-btn .t2 { font-size: 12.5px; color: var(--text-2); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
223
|
+
.row-btn .chev { color: var(--text-3); transition: transform .15s; }
|
|
224
|
+
.row-btn:hover .chev { transform: translateX(2px); color: var(--text-2); }
|
|
225
|
+
.arrow { color: var(--text-3); margin: 0 2px; }
|
|
226
|
+
.ico-box { width: 30px; height: 30px; border-radius: 8px; display: grid; place-items: center; flex: none; background: var(--surface-2); color: var(--text-2); }
|
|
227
|
+
.ico-box.add { background: var(--add-soft); color: var(--add-text); }
|
|
228
|
+
.ico-box.rem { background: var(--rem-soft); color: var(--rem-text); }
|
|
229
|
+
.ico-box.viol { background: var(--viol-soft); color: var(--viol-text); }
|
|
230
|
+
.ico-box.warn { background: var(--warn-soft); color: var(--warn-text); }
|
|
231
|
+
.ico-box.chg { background: var(--chg-soft); color: var(--chg-text); }
|
|
232
|
+
.ico-box.good { background: var(--add-soft); color: var(--add-text); }
|
|
233
|
+
|
|
234
|
+
.bars { display: grid; gap: 9px; }
|
|
235
|
+
.bar-row { display: grid; grid-template-columns: minmax(70px, 130px) 1fr auto; align-items: center; gap: 10px; font-size: 13px; }
|
|
236
|
+
.bar-row .n { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
237
|
+
.bar-track { height: 8px; border-radius: 4px; background: var(--surface-2); overflow: hidden; }
|
|
238
|
+
.bar-fill { height: 100%; border-radius: 4px; background: var(--accent); transform-origin: left; animation: barIn .9s var(--ease) both; }
|
|
239
|
+
.bar-row.removed .bar-fill { background: var(--rem); opacity: .5; }
|
|
240
|
+
.bar-row.added .bar-fill { background: var(--add); }
|
|
241
|
+
|
|
242
|
+
.preview { position: relative; height: 260px; border-top: 1px solid var(--border); background-image: radial-gradient(var(--dot) 1px, transparent 1px); background-size: 16px 16px; border-radius: 0 0 var(--radius) var(--radius); overflow: hidden; cursor: pointer; }
|
|
243
|
+
.preview svg { width: 100%; height: 100%; display: block; pointer-events: none; }
|
|
244
|
+
.preview .open { position: absolute; right: 12px; bottom: 12px; opacity: 0; transform: translateY(4px); transition: opacity .2s, transform .2s var(--ease); }
|
|
245
|
+
.preview:hover .open { opacity: 1; transform: none; }
|
|
246
|
+
|
|
247
|
+
/* tables */
|
|
248
|
+
.toolbar { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; margin-bottom: 12px; }
|
|
249
|
+
.tabs { display: inline-flex; background: var(--surface-2); border: 1px solid var(--border); border-radius: 9px; padding: 3px; gap: 2px; position: relative; }
|
|
250
|
+
.tabs button { border: 0; background: none; padding: 4px 10px; border-radius: 6px; color: var(--text-2); font-weight: 500; font-size: 13px; display: inline-flex; gap: 6px; align-items: center; position: relative; z-index: 1; transition: color .15s; }
|
|
251
|
+
.tabs button[aria-pressed="true"] { color: var(--text); }
|
|
252
|
+
.tabs .ind { position: absolute; top: 3px; bottom: 3px; border-radius: 6px; background: var(--surface); box-shadow: var(--shadow-sm), 0 0 0 1px var(--border); transition: left .25s var(--ease), width .25s var(--ease); }
|
|
253
|
+
.tabs .c { font: 500 11px var(--mono); color: var(--text-3); }
|
|
254
|
+
.input { display: flex; align-items: center; gap: 8px; height: 34px; padding: 0 10px; border: 1px solid var(--border); border-radius: 8px; background: var(--surface); box-shadow: var(--shadow-sm); color: var(--text-3); min-width: 240px; transition: border-color .15s, box-shadow .15s; }
|
|
255
|
+
.input:focus-within { border-color: var(--accent); box-shadow: var(--ring); }
|
|
256
|
+
.input input { border: 0; outline: 0; background: none; width: 100%; color: var(--text); }
|
|
257
|
+
.tbl-wrap { overflow-x: auto; }
|
|
258
|
+
table { width: 100%; border-collapse: collapse; font-size: 13.5px; }
|
|
259
|
+
th { text-align: left; font-weight: 500; color: var(--text-2); font-size: 12.5px; padding: 10px 16px; border-bottom: 1px solid var(--border); white-space: nowrap; user-select: none; }
|
|
260
|
+
th.sortable { cursor: pointer; }
|
|
261
|
+
th.sortable:hover { color: var(--text); }
|
|
262
|
+
th .sort { opacity: .5; margin-left: 2px; }
|
|
263
|
+
td { padding: 11px 16px; border-bottom: 1px solid var(--border); vertical-align: middle; }
|
|
264
|
+
tbody tr { transition: background .12s; }
|
|
265
|
+
tbody tr.click { cursor: pointer; }
|
|
266
|
+
tbody tr.click:hover { background: var(--surface-2); }
|
|
267
|
+
tbody tr:last-child td { border-bottom: 0; }
|
|
268
|
+
tbody tr.enter { animation: rise .35s var(--ease) both; }
|
|
269
|
+
td.r, th.r { text-align: right; }
|
|
270
|
+
.cell-mod { display: flex; align-items: center; gap: 8px; font-weight: 500; }
|
|
271
|
+
.mini-bar { width: 80px; height: 6px; border-radius: 3px; background: var(--surface-2); overflow: hidden; display: inline-block; vertical-align: middle; margin-right: 8px; }
|
|
272
|
+
.mini-bar i { display: block; height: 100%; background: var(--accent); border-radius: 3px; }
|
|
273
|
+
.empty-state { text-align: center; padding: 48px 16px; color: var(--text-2); }
|
|
274
|
+
.empty-state .ico-box { margin: 0 auto 12px; width: 44px; height: 44px; border-radius: 12px; }
|
|
275
|
+
.empty-state h4 { margin: 0 0 4px; color: var(--text); font-size: 15px; }
|
|
276
|
+
|
|
277
|
+
/* rules accordion */
|
|
278
|
+
.rule { border-top: 1px solid var(--border); }
|
|
279
|
+
.rule:first-child { border-top: 0; }
|
|
280
|
+
.rule-h { display: grid; grid-template-columns: 30px minmax(0, 1.4fr) minmax(0, 1fr) auto auto 20px; gap: 14px; align-items: center; padding: 14px 16px; width: 100%; background: none; border: 0; text-align: left; transition: background .12s; }
|
|
281
|
+
.rule-h:hover { background: var(--surface-2); }
|
|
282
|
+
.rule-h .nm { font-weight: 600; }
|
|
283
|
+
.rule-h .sub { font-size: 12.5px; color: var(--text-2); }
|
|
284
|
+
.rule-h .chev { transition: transform .25s var(--ease); color: var(--text-3); }
|
|
285
|
+
.rule.open .rule-h .chev { transform: rotate(90deg); }
|
|
286
|
+
.rule-body { display: grid; grid-template-rows: 0fr; transition: grid-template-rows .3s var(--ease); }
|
|
287
|
+
.rule.open .rule-body { grid-template-rows: 1fr; }
|
|
288
|
+
.rule-body > div { overflow: hidden; }
|
|
289
|
+
.rule-inner { padding: 0 16px 16px 60px; display: grid; gap: 10px; }
|
|
290
|
+
.viol-card { border: 1px solid var(--border); border-radius: 10px; padding: 12px; background: var(--surface-2); }
|
|
291
|
+
.viol-card .top { display: flex; align-items: center; gap: 8px; flex-wrap: wrap; margin-bottom: 8px; font-weight: 500; }
|
|
292
|
+
.ev { display: grid; gap: 4px; }
|
|
293
|
+
.ev-row { display: flex; gap: 8px; align-items: baseline; flex-wrap: wrap; font-size: 12.5px; }
|
|
294
|
+
.ev-row code { background: var(--surface); border: 1px solid var(--border); border-radius: 5px; padding: 1px 6px; }
|
|
295
|
+
.ev-row .to { color: var(--text-2); font-family: var(--mono); font-size: 12px; }
|
|
296
|
+
.ev-row.plus code { border-color: color-mix(in srgb, var(--add) 45%, var(--border)); }
|
|
297
|
+
|
|
298
|
+
/* ============ map ============ */
|
|
299
|
+
.map-card { flex: 1; position: relative; overflow: hidden; min-height: 420px; background: var(--surface); }
|
|
300
|
+
#stage { position: absolute; inset: 0; cursor: grab; touch-action: none; }
|
|
301
|
+
#stage.dragging { cursor: grabbing; }
|
|
302
|
+
#stage > svg { width: 100%; height: 100%; display: block; }
|
|
303
|
+
.float { position: absolute; z-index: 5; display: flex; gap: 8px; align-items: center; }
|
|
304
|
+
.float.tl { left: 14px; top: 14px; flex-wrap: wrap; right: 14px; pointer-events: none; }
|
|
305
|
+
.float.tl > * { pointer-events: auto; }
|
|
306
|
+
.float.br { right: 14px; bottom: 14px; flex-direction: column; }
|
|
307
|
+
.float.bl { left: 14px; bottom: 14px; }
|
|
308
|
+
.glass { background: color-mix(in srgb, var(--surface) 86%, transparent); backdrop-filter: blur(12px) saturate(1.4); border: 1px solid var(--border); border-radius: 10px; box-shadow: var(--shadow); }
|
|
309
|
+
.zoom { display: grid; overflow: hidden; }
|
|
310
|
+
.zoom button { border: 0; background: none; width: 34px; height: 32px; display: grid; place-items: center; color: var(--text-2); transition: background .12s, color .12s; }
|
|
311
|
+
.zoom button:hover { background: var(--surface-2); color: var(--text); }
|
|
312
|
+
.zoom button + button { border-top: 1px solid var(--border); }
|
|
313
|
+
.zoom .pct { font: 500 10.5px var(--mono); color: var(--text-3); text-align: center; padding: 4px 0; border-top: 1px solid var(--border); }
|
|
314
|
+
.switch { display: inline-flex; align-items: center; gap: 8px; height: 34px; padding: 0 12px; font-size: 13px; font-weight: 500; cursor: pointer; user-select: none; }
|
|
315
|
+
.switch input { appearance: none; width: 28px; height: 16px; border-radius: 999px; background: var(--border-strong); position: relative; margin: 0; cursor: pointer; transition: background .2s; }
|
|
316
|
+
.switch input::after { content: ""; position: absolute; top: 2px; left: 2px; width: 12px; height: 12px; border-radius: 50%; background: #fff; box-shadow: 0 1px 2px rgba(0,0,0,.3); transition: transform .25s var(--ease-spring); }
|
|
317
|
+
.switch input:checked { background: var(--accent); }
|
|
318
|
+
.switch input:checked::after { transform: translateX(12px); }
|
|
319
|
+
.legend-float { display: flex; gap: 14px; padding: 8px 12px; font-size: 12px; color: var(--text-2); flex-wrap: wrap; margin-left: auto; }
|
|
320
|
+
.legend-float span { display: inline-flex; align-items: center; gap: 6px; }
|
|
321
|
+
.legend-float svg { width: 22px; height: 8px; overflow: visible; }
|
|
322
|
+
.minimap { width: 180px; height: 120px; padding: 6px; cursor: pointer; }
|
|
323
|
+
.minimap svg { width: 100%; height: 100%; display: block; }
|
|
324
|
+
.minimap .vp { fill: color-mix(in srgb, var(--accent) 5%, transparent); stroke: var(--accent); stroke-width: 1.5; vector-effect: non-scaling-stroke; }
|
|
325
|
+
.minimap rect.mn { fill: var(--surface-3); stroke: var(--border-strong); vector-effect: non-scaling-stroke; }
|
|
326
|
+
.minimap rect.mn.added { fill: var(--add-soft); stroke: var(--add); }
|
|
327
|
+
.minimap rect.mn.removed { fill: var(--rem-soft); stroke: var(--rem); }
|
|
328
|
+
.minimap rect.mn.inviol { stroke: var(--viol); }
|
|
329
|
+
|
|
330
|
+
.node .card-r { fill: var(--surface); stroke: var(--border-strong); stroke-width: 1; filter: url(#nshadow); transition: stroke .2s, fill .2s, stroke-width .2s; }
|
|
331
|
+
.node.external .card-r { fill: var(--surface-2); }
|
|
332
|
+
.node .n-name { fill: var(--text); font: 600 13px var(--font); letter-spacing: -0.01em; }
|
|
333
|
+
.node .n-meta { fill: var(--text-3); font: 500 10.5px var(--mono); }
|
|
334
|
+
.node .n-ico { transition: stroke .2s; stroke: var(--text-3); fill: none; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; }
|
|
335
|
+
.node { cursor: pointer; transition: opacity .35s var(--ease); }
|
|
336
|
+
.node:hover .card-r { stroke: var(--text-3); }
|
|
337
|
+
.node.added .card-r { stroke: var(--add); stroke-width: 1.6; fill: color-mix(in srgb, var(--add-soft) 70%, var(--surface)); }
|
|
338
|
+
.node.added .n-ico { stroke: var(--add); }
|
|
339
|
+
.node.removed .card-r { stroke: var(--rem); stroke-dasharray: 5 4; stroke-width: 1.4; fill: color-mix(in srgb, var(--rem-soft) 70%, var(--surface)); }
|
|
340
|
+
.node.removed .n-ico { stroke: var(--rem); }
|
|
341
|
+
.node.removed text { opacity: .75; }
|
|
342
|
+
.node.inviol .n-ico { stroke: var(--viol); }
|
|
343
|
+
.node.sel .n-ico { stroke: var(--accent); }
|
|
344
|
+
.node.sel .card-r { stroke: var(--accent); stroke-width: 2.4; }
|
|
345
|
+
.node .nbadge { opacity: 0; transition: opacity .2s; }
|
|
346
|
+
.node.added .nbadge.b-add, .node.removed .nbadge.b-rem { opacity: 1; }
|
|
347
|
+
.nbadge text { fill: #fff; font: 700 9px var(--font); letter-spacing: .06em; }
|
|
348
|
+
.nbadge.b-add rect { fill: var(--add); } .nbadge.b-rem rect { fill: var(--rem); }
|
|
349
|
+
.edge { transition: opacity .35s var(--ease); }
|
|
350
|
+
.edge path.hit { stroke: transparent; stroke-width: 14; fill: none; cursor: pointer; }
|
|
351
|
+
.edge path.line { fill: none; stroke: var(--line); stroke-width: 1.4; transition: stroke .2s, stroke-width .2s; }
|
|
352
|
+
.edge:hover path.line { stroke-width: 2.4; }
|
|
353
|
+
.edge.added path.line { stroke: var(--add); stroke-width: 2; }
|
|
354
|
+
.edge.removed path.line { stroke: var(--rem); stroke-width: 1.7; stroke-dasharray: 6 5; }
|
|
355
|
+
.edge.viol path.line { stroke: var(--viol); stroke-width: 2.6; stroke-dasharray: none; }
|
|
356
|
+
.edge.viol path.glow { stroke: var(--viol); stroke-width: 7; opacity: .13; fill: none; animation: glow 2.2s ease-in-out infinite; }
|
|
357
|
+
.edge:not(.viol) path.glow { display: none; }
|
|
358
|
+
@keyframes glow { 50% { opacity: .04; } }
|
|
359
|
+
.edge.sel path.line { stroke: var(--accent); stroke-width: 2.8; }
|
|
360
|
+
.edge .flow { fill: var(--add); opacity: 0; }
|
|
361
|
+
.edge.added .flow { opacity: 1; }
|
|
362
|
+
.edge.viol .flow { fill: var(--viol); opacity: 1; }
|
|
363
|
+
.lbl rect { fill: var(--surface); stroke: var(--border-strong); }
|
|
364
|
+
.lbl text { fill: var(--text-2); font: 600 10px var(--mono); }
|
|
365
|
+
.edge.added .lbl text { fill: var(--add-text); } .edge.added .lbl rect { stroke: color-mix(in srgb, var(--add) 50%, var(--border)); }
|
|
366
|
+
.edge.removed .lbl text { fill: var(--rem-text); }
|
|
367
|
+
.edge.viol .lbl rect { fill: var(--viol-soft); stroke: var(--viol); }
|
|
368
|
+
.edge.viol .lbl text { fill: var(--viol-text); }
|
|
369
|
+
/* files a change edits ("touched"): an indigo dot, the same accent as the moved plate */
|
|
370
|
+
.node .tdot { fill: var(--accent); opacity: 0; transition: opacity .2s; }
|
|
371
|
+
.node.touched .tdot { opacity: 1; }
|
|
372
|
+
.node:focus { outline: none; }
|
|
373
|
+
.node:focus-visible .card-r { stroke: var(--accent); stroke-width: 2.4; }
|
|
374
|
+
.node.touched:not(.sel) .card-r { stroke: color-mix(in srgb, var(--accent) 55%, var(--border-strong)); }
|
|
375
|
+
.tdot-key { display: inline-block; width: 7px; height: 7px; border-radius: 50%; background: var(--accent); flex: none; }
|
|
376
|
+
.gone { opacity: 0 !important; pointer-events: none; }
|
|
377
|
+
.dim { opacity: .12; }
|
|
378
|
+
.draw-in path.line { stroke-dasharray: var(--len) var(--len); stroke-dashoffset: var(--len); animation: draw 1s var(--ease) forwards; animation-delay: var(--d, 0s); }
|
|
379
|
+
.draw-in.removed path.line { animation-name: drawDash; }
|
|
380
|
+
@keyframes draw { to { stroke-dashoffset: 0; } }
|
|
381
|
+
@keyframes drawDash { 99% { stroke-dashoffset: 0; } 100% { stroke-dasharray: 6 5; stroke-dashoffset: 0; } }
|
|
382
|
+
.pop-in { animation: popIn .5s var(--ease-spring) both; animation-delay: var(--d, 0s); transform-box: fill-box; transform-origin: center; }
|
|
383
|
+
@keyframes popIn { from { opacity: 0; transform: scale(.85); } }
|
|
384
|
+
|
|
385
|
+
.tip { position: fixed; z-index: 60; pointer-events: none; min-width: 180px; max-width: 280px; padding: 10px 12px; border-radius: 10px; background: var(--surface); border: 1px solid var(--border); box-shadow: var(--shadow-lg); font-size: 12.5px; opacity: 0; transform: translateY(4px) scale(.98); transition: opacity .12s, transform .12s var(--ease); }
|
|
386
|
+
.tip.show { opacity: 1; transform: none; }
|
|
387
|
+
.tip .tt { font-weight: 600; font-size: 13px; margin-bottom: 4px; display: flex; gap: 6px; align-items: center; flex-wrap: wrap; }
|
|
388
|
+
.tip .kv { display: flex; justify-content: space-between; gap: 16px; color: var(--text-2); }
|
|
389
|
+
.tip .kv b { color: var(--text); font-weight: 500; font-variant-numeric: tabular-nums; }
|
|
390
|
+
|
|
391
|
+
.drawer { position: absolute; top: 0; right: 0; bottom: 0; width: min(400px, 100%); z-index: 8; background: var(--surface); border-left: 1px solid var(--border); box-shadow: var(--shadow-lg); transform: translateX(104%); transition: transform .35s var(--ease); display: flex; flex-direction: column; }
|
|
392
|
+
.drawer.open { transform: none; }
|
|
393
|
+
.drawer-h { display: flex; align-items: flex-start; gap: 10px; padding: 16px; border-bottom: 1px solid var(--border); }
|
|
394
|
+
.drawer-h h3 { margin: 0; font-size: 16px; letter-spacing: -0.015em; overflow-wrap: anywhere; }
|
|
395
|
+
.drawer-h .sub { color: var(--text-2); font-size: 12.5px; margin-top: 2px; }
|
|
396
|
+
.drawer-b { padding: 16px; overflow-y: auto; display: grid; gap: 18px; align-content: start; }
|
|
397
|
+
.drawer-b > * { animation: rise .4s var(--ease) both; }
|
|
398
|
+
.drawer-b > *:nth-child(2) { animation-delay: .04s; } .drawer-b > *:nth-child(3) { animation-delay: .08s; } .drawer-b > *:nth-child(4) { animation-delay: .12s; }
|
|
399
|
+
.sec-t { font-size: 11.5px; font-weight: 600; text-transform: uppercase; letter-spacing: .06em; color: var(--text-3); margin-bottom: 8px; }
|
|
400
|
+
.kv-grid { display: grid; grid-template-columns: repeat(3, 1fr); gap: 8px; }
|
|
401
|
+
.kv-grid > div { border: 1px solid var(--border); border-radius: 9px; padding: 8px 10px; }
|
|
402
|
+
.kv-grid .k { font-size: 11.5px; color: var(--text-3); }
|
|
403
|
+
.kv-grid .v { font-weight: 600; font-size: 16px; font-variant-numeric: tabular-nums; }
|
|
404
|
+
.callout { display: flex; gap: 10px; border-radius: 10px; padding: 10px 12px; font-size: 13px; background: var(--viol-soft); color: var(--viol-text); }
|
|
405
|
+
.callout svg.i { margin-top: 2px; }
|
|
406
|
+
.callout.info { background: var(--accent-soft); color: var(--accent-text); }
|
|
407
|
+
.ell { overflow: hidden; text-overflow: ellipsis; white-space: nowrap; min-width: 0; }
|
|
408
|
+
/* module drill-down */
|
|
409
|
+
.link-btn.back { margin: 0 0 6px -4px; }
|
|
410
|
+
.fmap-card { overflow: hidden; }
|
|
411
|
+
.fmap-card .card-h { flex-wrap: wrap; row-gap: 8px; }
|
|
412
|
+
.fmap-legend { display: flex; flex-wrap: wrap; gap: 12px; font-size: 12px; color: var(--text-2); }
|
|
413
|
+
.fmap-legend > span { display: inline-flex; align-items: center; gap: 6px; }
|
|
414
|
+
.fmap-legend svg { width: 22px; height: 8px; overflow: visible; }
|
|
415
|
+
.fmap { overflow: auto; max-height: min(70vh, 720px); margin-top: 12px; border-top: 1px solid var(--border); overscroll-behavior: contain;
|
|
416
|
+
background-image: radial-gradient(var(--dot) 1px, transparent 1px); background-size: 18px 18px; }
|
|
417
|
+
.fmap-svg { display: block; margin: 0 auto; }
|
|
418
|
+
.fmap-svg .node .card-r { filter: url(#fm-shadow); }
|
|
419
|
+
.toolbar-t { margin: 0 auto 0 0; font-size: 15px; font-weight: 600; letter-spacing: -0.01em; }
|
|
420
|
+
tbody tr.sel, tbody tr.sel:hover { background: var(--accent-soft); }
|
|
421
|
+
tbody tr.sel td:first-child { box-shadow: inset 2px 0 0 var(--accent); }
|
|
422
|
+
.file-list .click { cursor: pointer; }
|
|
423
|
+
.file-list .click:hover { background: var(--surface-2); color: var(--text); }
|
|
424
|
+
.file-list .edit { color: var(--accent-text); background: var(--accent-soft); }
|
|
425
|
+
.pill-list { display: grid; gap: 4px; }
|
|
426
|
+
.pill-item { display: flex; align-items: center; gap: 8px; padding: 7px 10px; border: 1px solid var(--border); border-radius: 8px; background: none; width: 100%; text-align: left; transition: background .12s, border-color .12s; }
|
|
427
|
+
.pill-item:hover { background: var(--surface-2); border-color: var(--border-strong); }
|
|
428
|
+
.pill-item .grow { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
429
|
+
.file-list { display: grid; gap: 2px; font-family: var(--mono); font-size: 12px; }
|
|
430
|
+
.file-list div { padding: 3px 6px; border-radius: 5px; overflow-wrap: anywhere; color: var(--text-2); }
|
|
431
|
+
.file-list .plus { color: var(--add-text); background: var(--add-soft); }
|
|
432
|
+
.file-list .minus { color: var(--rem-text); background: var(--rem-soft); text-decoration: line-through; }
|
|
433
|
+
|
|
434
|
+
|
|
435
|
+
/* ============ system architecture ============ */
|
|
436
|
+
.page.wide { max-width: 1480px; }
|
|
437
|
+
.sys-stats { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 14px; }
|
|
438
|
+
.sys-stat { display: inline-flex; align-items: center; gap: 8px; padding: 8px 12px; border: 1px solid var(--border); border-radius: 10px; background: var(--surface); box-shadow: var(--shadow-sm); color: var(--text-2); }
|
|
439
|
+
.sys-stat b { color: var(--text); font-size: 15px; }
|
|
440
|
+
.sys-stat svg.i { color: var(--text-3); }
|
|
441
|
+
.app-ico { background: color-mix(in srgb, var(--app) 14%, transparent) !important; color: var(--app) !important; }
|
|
442
|
+
.mono-s { font-family: var(--mono); font-size: 11.5px; letter-spacing: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
|
|
443
|
+
.sys-tech { display: flex; flex-wrap: wrap; gap: 4px; padding: 10px 14px 0; }
|
|
444
|
+
.count-s { font: 500 11px var(--mono); color: var(--text-3); background: var(--surface-2); border: 1px solid var(--border); border-radius: 999px; padding: 0 6px; }
|
|
445
|
+
.grow-r { margin-left: auto; }
|
|
446
|
+
.meth { font: 600 9.5px var(--mono); padding: 1px 5px; border-radius: 4px; background: var(--surface-3); color: var(--text-2); flex: none; letter-spacing: .02em; }
|
|
447
|
+
.meth.m-get { background: var(--chg-soft); color: var(--chg-text); } .meth.m-post { background: var(--add-soft); color: var(--add-text); }
|
|
448
|
+
.meth.m-put, .meth.m-patch { background: var(--warn-soft); color: var(--warn-text); } .meth.m-delete { background: var(--rem-soft); color: var(--rem-text); }
|
|
449
|
+
.chip.mod { border: 1px solid var(--border); cursor: pointer; }
|
|
450
|
+
.chip.mod:hover { border-color: var(--border-strong); color: var(--text); }
|
|
451
|
+
.chip svg.i { width: 12px; height: 12px; }
|
|
452
|
+
i.dot { width: 9px; height: 9px; border-radius: 50%; display: inline-block; flex: none; }
|
|
453
|
+
.sys-note { font-size: 12.5px; margin: 10px 2px 0; }
|
|
454
|
+
/* ---- architecture: C4 container diagram (people, a dashed system boundary, containers, stores, outside systems) */
|
|
455
|
+
.arch-card { position: relative; height: clamp(520px, calc(100vh - 300px), 940px); overflow: hidden; padding: 0; background: var(--surface); }
|
|
456
|
+
#arch-stage { position: absolute; inset: 0; cursor: grab; touch-action: none; }
|
|
457
|
+
#arch-stage.dragging { cursor: grabbing; }
|
|
458
|
+
#arch-stage > svg { width: 100%; height: 100%; display: block; }
|
|
459
|
+
.arch-hint { display: inline-flex; align-items: center; gap: 7px; padding: 6px 11px; font-size: 12px; color: var(--text-2); }
|
|
460
|
+
.arch-hint svg.i { width: 14px; height: 14px; color: var(--accent); }
|
|
461
|
+
.arch-legend { display: flex; flex-wrap: wrap; align-items: center; gap: 8px 16px; margin-top: 12px; padding: 0 2px; font-size: 12px; color: var(--text-2); }
|
|
462
|
+
.arch-legend .ak { display: inline-flex; align-items: center; gap: 6px; }
|
|
463
|
+
.arch-legend svg { width: 26px; height: 16px; overflow: visible; }
|
|
464
|
+
.ak-shape { fill: var(--surface); stroke: var(--text-3); stroke-width: 1.2; }
|
|
465
|
+
.ak-shape.ext { fill: var(--surface-3); stroke: var(--border-strong); }
|
|
466
|
+
.ak-shape.bnd { fill: none; stroke: color-mix(in srgb, var(--accent) 55%, var(--border-strong)); stroke-dasharray: 3 2.5; }
|
|
467
|
+
.ak-sep { width: 1px; height: 16px; background: var(--border); }
|
|
468
|
+
.arch-boundary rect { fill: color-mix(in srgb, var(--accent-soft) 38%, transparent); stroke: color-mix(in srgb, var(--accent) 45%, var(--border-strong)); stroke-width: 1.4; stroke-dasharray: 8 6; }
|
|
469
|
+
.arch-boundary .ab-name { font: 650 14px var(--font); fill: var(--text); letter-spacing: -0.01em; }
|
|
470
|
+
.arch-boundary .ab-kind { font: 500 11px var(--mono); fill: var(--text-3); }
|
|
471
|
+
.arch-boundary .an-ico { color: var(--accent); }
|
|
472
|
+
.an-ico { fill: none; stroke: currentColor; stroke-width: 1.8; stroke-linecap: round; stroke-linejoin: round; color: var(--text-2); }
|
|
473
|
+
.an { cursor: pointer; transition: opacity .25s var(--ease); outline: none; }
|
|
474
|
+
.an.t-person { cursor: default; }
|
|
475
|
+
.an .an-shape { fill: var(--surface); stroke: var(--border-strong); stroke-width: 1.2; filter: url(#an-shadow); transition: stroke .2s, stroke-width .2s, fill .2s; }
|
|
476
|
+
.an .an-lid { fill: var(--surface-2); stroke: var(--border-strong); stroke-width: 1.2; transition: stroke .2s; }
|
|
477
|
+
.an:hover .an-shape, .an:hover .an-lid { stroke: var(--text-3); }
|
|
478
|
+
.an:focus-visible .an-shape { stroke: var(--accent); stroke-width: 2.2; }
|
|
479
|
+
.an-name { font: 600 13.5px var(--font); fill: var(--text); letter-spacing: -0.01em; }
|
|
480
|
+
.an-kind { font: 500 10.5px var(--mono); fill: var(--text-3); }
|
|
481
|
+
.an-sub { font: 500 12px var(--font); fill: var(--text-2); }
|
|
482
|
+
.an-sub.mono, .an-tech { font: 500 10.5px var(--mono); fill: var(--text-3); }
|
|
483
|
+
.an-rule { stroke: var(--border); }
|
|
484
|
+
.t-person .an-shape { fill: color-mix(in srgb, var(--accent-soft) 70%, var(--surface)); stroke: color-mix(in srgb, var(--accent) 45%, var(--border-strong)); }
|
|
485
|
+
.t-person .an-name { fill: var(--accent-text); font-size: 14px; }
|
|
486
|
+
.t-app .an-shape { stroke: color-mix(in srgb, var(--app) 40%, var(--border-strong)); stroke-width: 1.4; }
|
|
487
|
+
.t-app:hover .an-shape { stroke: var(--app); }
|
|
488
|
+
.t-app .an-band { fill: var(--app); }
|
|
489
|
+
.t-app .an-ibox { fill: color-mix(in srgb, var(--app) 14%, transparent); }
|
|
490
|
+
.t-app .an-ico { color: var(--app); }
|
|
491
|
+
.t-app .an-name { font-size: 15px; }
|
|
492
|
+
.t-db .an-ico { color: var(--chg); }
|
|
493
|
+
.t-queue .an-ico { color: var(--warn); }
|
|
494
|
+
.t-ext .an-shape { fill: var(--surface-2); }
|
|
495
|
+
.t-ext .an-shape.env { stroke-dasharray: 2 3; }
|
|
496
|
+
.an.added .an-shape { stroke: var(--add); stroke-width: 1.8; fill: color-mix(in srgb, var(--add-soft) 55%, var(--surface)); }
|
|
497
|
+
.an.added .an-lid { stroke: var(--add); }
|
|
498
|
+
.an.removed { opacity: .8; }
|
|
499
|
+
.an.removed .an-shape, .an.removed .an-lid { stroke: var(--rem); stroke-dasharray: 6 4; fill: color-mix(in srgb, var(--rem-soft) 55%, var(--surface)); }
|
|
500
|
+
.an-badge { opacity: 0; transition: opacity .2s; }
|
|
501
|
+
.an.added .an-badge.b-add, .an.removed .an-badge.b-rem { opacity: 1; }
|
|
502
|
+
.an-badge text { fill: #fff; font: 700 9.5px var(--font); letter-spacing: .06em; }
|
|
503
|
+
.an-badge.b-add rect { fill: var(--add); } .an-badge.b-rem rect { fill: var(--rem); }
|
|
504
|
+
.aw .wl { fill: none; stroke: var(--text-3); stroke-width: 1.5; transition: stroke .2s, stroke-width .2s; }
|
|
505
|
+
.aw.k-app .wl { stroke: var(--accent); stroke-width: 2; }
|
|
506
|
+
.aw.k-data .wl { stroke: var(--text-2); }
|
|
507
|
+
.aw.async .wl { stroke-dasharray: 1.5 5; stroke-linecap: round; stroke-width: 2.2; }
|
|
508
|
+
.aw.added .wl { stroke: var(--add); stroke-width: 2; }
|
|
509
|
+
.aw.removed .wl { stroke: var(--rem); stroke-dasharray: 6 5; stroke-linecap: butt; }
|
|
510
|
+
.aw .whit { fill: none; stroke: transparent; stroke-width: 14; }
|
|
511
|
+
.aw .wdot { fill: var(--accent); } .aw.added .wdot { fill: var(--add); }
|
|
512
|
+
.aw, .aw-lbl { transition: opacity .25s var(--ease); }
|
|
513
|
+
.aw-lbl rect { fill: var(--surface); stroke: var(--border-strong); }
|
|
514
|
+
.aw-lbl text { font: 600 10.5px var(--mono); fill: var(--text-2); }
|
|
515
|
+
.aw-lbl.k-app rect { stroke: color-mix(in srgb, var(--accent) 45%, var(--border)); } .aw-lbl.k-app text { fill: var(--accent-text); }
|
|
516
|
+
.aw-lbl.added rect { stroke: color-mix(in srgb, var(--add) 55%, var(--border)); } .aw-lbl.added text { fill: var(--add-text); }
|
|
517
|
+
.aw-lbl.removed text { fill: var(--rem-text); }
|
|
518
|
+
#arch-card.hl-on .an:not(.hl) { opacity: .28; }
|
|
519
|
+
#arch-card.hl-on .aw:not(.hl), #arch-card.hl-on .aw-lbl:not(.hl) { opacity: .08; }
|
|
520
|
+
#arch-card .aw.hl .wl { stroke: var(--accent); stroke-width: 2.4; }
|
|
521
|
+
#arch-card .aw-lbl.hl rect { stroke: var(--accent); }
|
|
522
|
+
@media (max-width: 860px) { .arch-card { height: 68vh; } .arch-hint { display: none; } }
|
|
523
|
+
.sheet { position: fixed; top: 0; right: 0; bottom: 0; width: min(420px, 100%); z-index: 65; border-left: 1px solid var(--border); }
|
|
524
|
+
.glance { display: flex; align-items: center; flex-wrap: wrap; gap: 10px; }
|
|
525
|
+
.g-node { display: inline-flex; align-items: center; gap: 10px; padding: 8px 12px 8px 8px; border: 1px solid var(--border); border-radius: 12px; background: var(--surface); text-align: left; transition: border-color .15s, transform .15s; }
|
|
526
|
+
.g-node.users { padding: 8px 14px; border-radius: 999px; color: var(--text-2); font-weight: 500; }
|
|
527
|
+
.g-node.g-app { border-top: 3px solid var(--app); }
|
|
528
|
+
.g-node.g-app:hover { border-color: var(--border-strong); transform: translateY(-1px); }
|
|
529
|
+
.g-node b { display: block; font-size: 14px; }
|
|
530
|
+
.g-node .faint { font-size: 12px; }
|
|
531
|
+
.g-arrow { color: var(--text-3); display: inline-flex; }
|
|
532
|
+
.g-tail { display: flex; flex-wrap: wrap; gap: 6px; margin-top: 12px; }
|
|
533
|
+
@media (max-width: 860px) { .g-arrow { transform: rotate(90deg); } .glance { flex-direction: column; align-items: stretch; } }
|
|
534
|
+
|
|
535
|
+
/* ============ overlays ============ */
|
|
536
|
+
.overlay { position: fixed; inset: 0; z-index: 70; background: rgba(9,9,11,.4); backdrop-filter: blur(3px); display: none; align-items: flex-start; justify-content: center; padding: 12vh 16px 16px; }
|
|
537
|
+
.overlay.open { display: flex; animation: fade .15s ease; }
|
|
538
|
+
@keyframes fade { from { opacity: 0; } }
|
|
539
|
+
.palette { width: min(620px, 100%); background: var(--surface); border: 1px solid var(--border); border-radius: 14px; box-shadow: var(--shadow-lg); overflow: hidden; animation: palIn .22s var(--ease-spring); }
|
|
540
|
+
@keyframes palIn { from { opacity: 0; transform: translateY(-8px) scale(.97); } }
|
|
541
|
+
.pal-in { display: flex; align-items: center; gap: 10px; padding: 14px 16px; border-bottom: 1px solid var(--border); color: var(--text-3); }
|
|
542
|
+
.pal-in input:focus-visible, .input input:focus-visible { box-shadow: none; }
|
|
543
|
+
.pal-in input { flex: 1; border: 0; outline: 0; background: none; font-size: 15px; color: var(--text); }
|
|
544
|
+
.pal-list { max-height: 50vh; overflow-y: auto; padding: 6px; }
|
|
545
|
+
.pal-group { font-size: 11px; font-weight: 600; color: var(--text-3); text-transform: uppercase; letter-spacing: .06em; padding: 10px 10px 4px; }
|
|
546
|
+
.pal-item { display: flex; align-items: center; gap: 10px; padding: 8px 10px; border-radius: 8px; cursor: pointer; color: var(--text-2); }
|
|
547
|
+
.pal-item .grow { flex: 1; min-width: 0; overflow: hidden; text-overflow: ellipsis; white-space: nowrap; color: var(--text); }
|
|
548
|
+
.pal-item.on { background: var(--surface-2); }
|
|
549
|
+
.pal-item.on .enter { opacity: 1; }
|
|
550
|
+
.pal-item .enter { opacity: 0; font: 500 11px var(--mono); color: var(--text-3); }
|
|
551
|
+
.pal-foot { display: flex; gap: 14px; padding: 8px 14px; border-top: 1px solid var(--border); font-size: 12px; color: var(--text-3); }
|
|
552
|
+
.pal-foot .kbd { margin: 0 4px 0 0; }
|
|
553
|
+
.pal-empty { padding: 28px; text-align: center; color: var(--text-3); }
|
|
554
|
+
|
|
555
|
+
.toasts { position: fixed; right: 16px; bottom: 16px; z-index: 80; display: grid; gap: 8px; }
|
|
556
|
+
.toast { display: flex; align-items: center; gap: 10px; padding: 10px 14px; border-radius: 10px; background: var(--text); color: var(--bg); box-shadow: var(--shadow-lg); font-weight: 500; font-size: 13px; animation: toastIn .35s var(--ease-spring); }
|
|
557
|
+
.toast.out { animation: toastOut .25s var(--ease) forwards; }
|
|
558
|
+
@keyframes toastIn { from { opacity: 0; transform: translateY(12px) scale(.96); } }
|
|
559
|
+
@keyframes toastOut { to { opacity: 0; transform: translateY(6px); } }
|
|
560
|
+
|
|
561
|
+
[data-tip] { position: relative; }
|
|
562
|
+
[data-tip]::after { content: attr(data-tip); position: absolute; right: calc(100% + 8px); top: 50%; transform: translateY(-50%) translateX(4px); background: var(--text); color: var(--bg); font-size: 12px; font-weight: 500; padding: 4px 8px; border-radius: 6px; white-space: nowrap; opacity: 0; pointer-events: none; transition: opacity .12s, transform .12s; }
|
|
563
|
+
[data-tip]:hover::after { opacity: 1; transform: translateY(-50%); }
|
|
564
|
+
|
|
565
|
+
.scrim { display: none; }
|
|
566
|
+
|
|
567
|
+
/* ============ responsive ============ */
|
|
568
|
+
@media (max-width: 1100px) { .stats { grid-template-columns: repeat(2, minmax(0, 1fr)); } .two { grid-template-columns: 1fr; } .search-btn { min-width: 0; } .search-btn .lbl-t { display: none; } }
|
|
569
|
+
@media (max-width: 860px) {
|
|
570
|
+
.app { grid-template-columns: 1fr; }
|
|
571
|
+
.sidebar { position: fixed; left: 0; top: 0; bottom: 0; width: 272px; transform: translateX(-102%); transition: transform .3s var(--ease); box-shadow: var(--shadow-lg); }
|
|
572
|
+
.sidebar.open { transform: none; }
|
|
573
|
+
.scrim.open { display: block; position: fixed; inset: 0; z-index: 25; background: rgba(0,0,0,.35); animation: fade .2s; }
|
|
574
|
+
.btn.menu-btn { display: inline-flex; }
|
|
575
|
+
.topbar { padding: 0 12px; }
|
|
576
|
+
.page { padding: 18px 16px 40px; }
|
|
577
|
+
.page.full { padding: 10px; }
|
|
578
|
+
.hero { flex-wrap: wrap; }
|
|
579
|
+
.hero .big { margin-left: 0; text-align: left; width: 100%; order: -1; display: none; }
|
|
580
|
+
.top-actions .btn .lbl-t, .status-pill .lbl-t { display: none; }
|
|
581
|
+
.minimap, .legend-float { display: none; }
|
|
582
|
+
.rule-h { grid-template-columns: 30px minmax(0, 1fr) auto 20px; }
|
|
583
|
+
.rule-h .scope, .rule-h .cnt { display: none; }
|
|
584
|
+
.rule-inner { padding-left: 16px; }
|
|
585
|
+
.input { min-width: 0; flex: 1; }
|
|
586
|
+
.hide-sm, .search-btn .kbd { display: none; }
|
|
587
|
+
}
|
|
588
|
+
@media (max-width: 520px) { .stats { grid-template-columns: 1fr 1fr; } .stat .v { font-size: 22px; } .bar-row { grid-template-columns: 80px 1fr auto; } }
|
|
589
|
+
@media (prefers-reduced-motion: reduce) {
|
|
590
|
+
*, *::before, *::after { animation-duration: .001ms !important; animation-delay: 0s !important; transition-duration: .001ms !important; }
|
|
591
|
+
.edge .flow { display: none; }
|
|
592
|
+
}
|
|
593
|
+
</style>
|
|
594
|
+
</head>
|
|
595
|
+
<body>
|
|
596
|
+
<div class="app">
|
|
597
|
+
<aside class="sidebar" id="sidebar"></aside>
|
|
598
|
+
<div class="scrim" id="scrim"></div>
|
|
599
|
+
<div class="main">
|
|
600
|
+
<header class="topbar" id="topbar"></header>
|
|
601
|
+
<div class="content" id="content">
|
|
602
|
+
<section class="page" id="page-overview"></section>
|
|
603
|
+
<section class="page wide" id="page-system"></section>
|
|
604
|
+
<section class="page full" id="page-map"></section>
|
|
605
|
+
<section class="page" id="page-rules"></section>
|
|
606
|
+
<section class="page" id="page-changes"></section>
|
|
607
|
+
<section class="page" id="page-modules"></section>
|
|
608
|
+
<section class="page wide" id="page-module"></section>
|
|
609
|
+
</div>
|
|
610
|
+
</div>
|
|
611
|
+
</div>
|
|
612
|
+
<aside class="sheet drawer" id="sheet" aria-label="Details"></aside>
|
|
613
|
+
<div class="tip" id="tip" role="tooltip"></div>
|
|
614
|
+
<div class="overlay" id="palette-ov"></div>
|
|
615
|
+
<div class="toasts" id="toasts" aria-live="polite"></div>
|
|
616
|
+
<script id="data" type="application/json">__DATA__</script>
|
|
617
|
+
<script>__CLIENT__</script>
|
|
618
|
+
</body>
|
|
619
|
+
</html>
|