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.
@@ -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>