fastapi-panel 0.1.0__py3-none-any.whl

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,420 @@
1
+ :root {
2
+ --accent: #0c6b58;
3
+ --accent-hover: #0a5a4a;
4
+ --accent-soft: #e4f3ef;
5
+ --on-accent: #ffffff;
6
+ --nav-head: var(--accent);
7
+ --nav-head-hover: var(--accent-hover);
8
+ --on-nav-head: var(--on-accent);
9
+ --bg: #f5f6f7;
10
+ --surface: #ffffff;
11
+ --surface-alt: #f9fafb;
12
+ --text: #1c2527;
13
+ --muted: #5f6b6e;
14
+ --border: #dde1e4;
15
+ --danger: #b3261e;
16
+ --danger-soft: #fbeaea;
17
+ --warning: #8a5a00;
18
+ --warning-soft: #fdf3dc;
19
+ --success: #17663c;
20
+ --success-soft: #e5f4ea;
21
+ --radius: 8px;
22
+ --font: -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;
23
+ color-scheme: light;
24
+ }
25
+
26
+ :root[data-theme="dark"] {
27
+ --accent: #3cc2a4;
28
+ --accent-hover: #5dd2b8;
29
+ --accent-soft: #15332c;
30
+ --on-accent: #06211b;
31
+ --nav-head: color-mix(in srgb, var(--accent) 45%, var(--surface));
32
+ --nav-head-hover: color-mix(in srgb, var(--accent) 55%, var(--surface));
33
+ --on-nav-head: var(--text);
34
+ --bg: #111516;
35
+ --surface: #1a2022;
36
+ --surface-alt: #20282a;
37
+ --text: #e5eaeb;
38
+ --muted: #9aa7aa;
39
+ --border: #2e383a;
40
+ --danger: #f28b82;
41
+ --danger-soft: #3a1d1b;
42
+ --warning: #e9b949;
43
+ --warning-soft: #33290f;
44
+ --success: #6fcf97;
45
+ --success-soft: #16301f;
46
+ color-scheme: dark;
47
+ }
48
+
49
+ @media (prefers-color-scheme: dark) {
50
+ :root:not([data-theme="light"]) {
51
+ --accent: #3cc2a4;
52
+ --accent-hover: #5dd2b8;
53
+ --accent-soft: #15332c;
54
+ --on-accent: #06211b;
55
+ --nav-head: color-mix(in srgb, var(--accent) 45%, var(--surface));
56
+ --nav-head-hover: color-mix(in srgb, var(--accent) 55%, var(--surface));
57
+ --on-nav-head: var(--text);
58
+ --bg: #111516;
59
+ --surface: #1a2022;
60
+ --surface-alt: #20282a;
61
+ --text: #e5eaeb;
62
+ --muted: #9aa7aa;
63
+ --border: #2e383a;
64
+ --danger: #f28b82;
65
+ --danger-soft: #3a1d1b;
66
+ --warning: #e9b949;
67
+ --warning-soft: #33290f;
68
+ --success: #6fcf97;
69
+ --success-soft: #16301f;
70
+ color-scheme: dark;
71
+ }
72
+ }
73
+
74
+ * { box-sizing: border-box; }
75
+ [hidden] { display: none !important; }
76
+
77
+ body {
78
+ margin: 0;
79
+ background: var(--bg);
80
+ color: var(--text);
81
+ font: 14px/1.5 var(--font);
82
+ }
83
+
84
+ a { color: var(--accent); text-decoration: none; }
85
+ a:hover { text-decoration: underline; }
86
+ h1, h2, p { margin: 0; }
87
+ ul { list-style: none; margin: 0; padding: 0; }
88
+ :focus-visible { outline: 2px solid var(--accent); outline-offset: 2px; }
89
+
90
+ .muted { color: var(--muted); }
91
+ .sr-only {
92
+ position: absolute; width: 1px; height: 1px; overflow: hidden;
93
+ clip-path: inset(50%); white-space: nowrap;
94
+ }
95
+
96
+ /* Adding a related record */
97
+ .related { display: flex; align-items: flex-start; gap: 6px; }
98
+ .related > :first-child { flex: 1; min-width: 0; }
99
+ .related-add { flex: none; width: 34px; padding: 0; font-size: 16px; }
100
+ .modal[open] {
101
+ display: flex; flex-direction: column; width: min(760px, 94vw); height: min(85vh, 760px); padding: 0;
102
+ border: 1px solid var(--border); border-radius: var(--radius); background: var(--surface); color: var(--text);
103
+ }
104
+ .modal::backdrop { background: #0007; }
105
+ .modal-head { display: flex; align-items: center; justify-content: space-between; padding: 10px 16px; border-bottom: 1px solid var(--border); }
106
+ .modal-head .link-button { font-size: 20px; line-height: 1; }
107
+ .modal iframe { flex: 1; width: 100%; border: 0; background: var(--bg); }
108
+
109
+ /* Files */
110
+ .thumb { display: block; width: 32px; height: 32px; object-fit: cover; border-radius: 4px; border: 1px solid var(--border); }
111
+ .thumb-large { display: block; max-width: 160px; max-height: 120px; border-radius: 6px; border: 1px solid var(--border); margin-bottom: 8px; }
112
+ .file-current { display: flex; flex-wrap: wrap; align-items: center; gap: 4px 12px; margin-bottom: 8px; }
113
+ .file-current .thumb-large { margin-bottom: 0; }
114
+ input[type="file"]::file-selector-button {
115
+ margin-inline-end: 10px; padding: 3px 10px; border: 1px solid var(--border); border-radius: 6px;
116
+ background: var(--surface-alt); color: var(--text); font: inherit; cursor: pointer;
117
+ }
118
+
119
+ /* Imports */
120
+ .import-summary { display: flex; flex-wrap: wrap; gap: 8px; margin-bottom: 12px; }
121
+ .import-errors { color: var(--danger); }
122
+ .import-note { margin-bottom: 12px; }
123
+ .import-preview { margin-block: 12px; }
124
+ .import-file { display: block; margin-bottom: 4px; font-weight: 600; }
125
+
126
+ /* Layout */
127
+ .layout { display: flex; min-height: 100vh; }
128
+ .main { flex: 1; min-width: 0; display: flex; flex-direction: column; }
129
+ .content { padding: 24px; flex: 1; min-width: 0; }
130
+
131
+ .sidebar { width: 264px; flex: none; background: var(--surface); border-inline-end: 1px solid var(--border); }
132
+ .sidebar-inner { position: sticky; top: 0; max-height: 100vh; overflow-y: auto; padding: 0 12px 16px; }
133
+ .brand {
134
+ display: flex; align-items: center; gap: 10px; min-height: 52px; margin: 0 -12px 12px; padding: 8px 16px;
135
+ border-bottom: 1px solid var(--border); color: var(--text); font-size: 15px; font-weight: 650; overflow-wrap: anywhere;
136
+ }
137
+ .brand:hover { text-decoration: none; }
138
+ .brand-mark {
139
+ display: grid; place-items: center; flex: none; width: 26px; height: 26px; border-radius: 7px;
140
+ background: var(--accent); color: var(--on-accent); font-size: 13px; text-transform: uppercase;
141
+ }
142
+ .sidebar .nav-search.nav-search {
143
+ width: 100%; margin-bottom: 12px; padding: 6px 10px; border-radius: 6px;
144
+ border: 1px solid var(--border); background: var(--bg); color: var(--text); font: inherit;
145
+ }
146
+ .sidebar .nav-search::placeholder { color: var(--muted); }
147
+ .nav-group { margin-bottom: 12px; border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden; }
148
+ .nav-title {
149
+ padding: 6px 12px; background: var(--nav-head); color: var(--on-nav-head);
150
+ font-size: 12px; font-weight: 650; letter-spacing: .02em;
151
+ display: flex; align-items: center; justify-content: space-between; cursor: pointer; list-style: none;
152
+ }
153
+ .nav-title::-webkit-details-marker { display: none; }
154
+ .nav-title::after {
155
+ content: ""; width: 6px; height: 6px; margin-inline-end: 2px;
156
+ border: solid currentColor; border-width: 0 1.5px 1.5px 0; transform: translateY(-1px) rotate(45deg);
157
+ }
158
+ .nav-group:not([open]) > .nav-title::after { transform: rotate(-45deg); }
159
+ .nav-title:hover { background: var(--nav-head-hover); }
160
+ .nav-title:focus-visible { outline-color: var(--on-nav-head); outline-offset: -3px; }
161
+ .nav-item { display: flex; align-items: center; border-top: 1px solid var(--border); }
162
+ .nav-item a { padding: 7px 12px; color: var(--text); }
163
+ .nav-item > a:first-child { flex: 1; min-width: 0; overflow-wrap: anywhere; }
164
+ .nav-item a:hover { text-decoration: none; }
165
+ .nav-item:hover { background: var(--surface-alt); }
166
+ .nav-item.active { background: var(--accent-soft); box-shadow: inset 3px 0 0 var(--accent); }
167
+ .nav-item.active > a:first-child { color: var(--accent); font-weight: 600; }
168
+ .nav-item .nav-add { flex: none; color: var(--muted); font-size: 12px; white-space: nowrap; }
169
+ .nav-item .nav-add:hover { color: var(--accent); }
170
+
171
+ .topbar {
172
+ display: flex; align-items: center; gap: 12px; min-height: 52px; padding: 8px 24px;
173
+ background: var(--surface); border-bottom: 1px solid var(--border);
174
+ }
175
+ .crumbs { display: flex; flex-wrap: wrap; gap: 6px; min-width: 0; flex: 1; color: var(--muted); }
176
+ .crumbs [aria-current] { color: var(--text); }
177
+ .topbar-end { display: flex; align-items: center; gap: 12px; flex: none; }
178
+ .user-name { font-weight: 550; }
179
+ .top-link {
180
+ color: var(--muted); background: none; border: 0; padding: 0; font: inherit; cursor: pointer;
181
+ }
182
+ .top-link:hover { color: var(--text); text-decoration: underline; }
183
+ .icon-button {
184
+ width: 32px; height: 32px; border: 1px solid var(--border); border-radius: 6px;
185
+ background: var(--surface); color: var(--text); cursor: pointer; font-size: 15px;
186
+ }
187
+ .menu-button { display: none; }
188
+
189
+ /* Common pieces */
190
+ .page-head { display: flex; align-items: center; justify-content: space-between; gap: 12px; flex-wrap: wrap; margin-bottom: 16px; }
191
+ .page-title { font-size: 22px; font-weight: 650; overflow-wrap: anywhere; }
192
+ .content > .page-title { margin-bottom: 16px; }
193
+ .page-links { display: flex; gap: 8px; }
194
+ .card { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); }
195
+ .card-padded { padding: 16px; }
196
+ .card-title {
197
+ font-size: 13px; font-weight: 650; padding: 10px 16px;
198
+ background: var(--surface-alt); border-bottom: 1px solid var(--border);
199
+ border-radius: var(--radius) var(--radius) 0 0;
200
+ }
201
+ .card-empty { padding: 16px; }
202
+ .narrow { max-width: 560px; }
203
+
204
+ .button {
205
+ display: inline-flex; align-items: center; justify-content: center; min-height: 34px; padding: 6px 12px;
206
+ border: 1px solid var(--border); border-radius: 6px; background: var(--surface); color: var(--text);
207
+ font: inherit; font-weight: 550; cursor: pointer; white-space: nowrap;
208
+ }
209
+ .button:hover { background: var(--surface-alt); text-decoration: none; }
210
+ .button-primary { background: var(--accent); border-color: var(--accent); color: var(--on-accent); }
211
+ .button-primary:hover { background: var(--accent-hover); }
212
+ .button-danger { color: var(--danger); border-color: var(--danger); }
213
+ .button-danger:hover { background: var(--danger-soft); }
214
+ .button-small { min-height: 24px; padding: 2px 10px; font-size: 12px; }
215
+ .link-button { background: none; border: 0; padding: 0; font: inherit; color: var(--accent); cursor: pointer; }
216
+ .link-button:hover { text-decoration: underline; }
217
+
218
+ input:not([type="checkbox"]):not([type="radio"]), select, textarea {
219
+ width: 100%; max-width: 480px; min-height: 34px; padding: 6px 10px;
220
+ border: 1px solid var(--border); border-radius: 6px;
221
+ background: var(--surface); color: var(--text); font: inherit;
222
+ }
223
+ textarea { max-width: 720px; resize: vertical; }
224
+ input[type="checkbox"] { width: 16px; height: 16px; accent-color: var(--accent); vertical-align: middle; }
225
+
226
+ .message {
227
+ padding: 10px 14px; margin-bottom: 12px; border-radius: 6px;
228
+ border: 1px solid transparent; font-weight: 500;
229
+ }
230
+ .message-success { background: var(--success-soft); color: var(--success); }
231
+ .message-error { background: var(--danger-soft); color: var(--danger); }
232
+ .message-warning { background: var(--warning-soft); color: var(--warning); }
233
+ .message-info { background: var(--accent-soft); color: var(--accent); }
234
+
235
+ .tag {
236
+ display: inline-block; padding: 0 7px; border-radius: 10px; font-size: 12px; font-weight: 600;
237
+ text-transform: capitalize;
238
+ }
239
+ .tag-add { background: var(--success-soft); color: var(--success); }
240
+ .tag-change { background: var(--accent-soft); color: var(--accent); }
241
+ .tag-delete { background: var(--danger-soft); color: var(--danger); }
242
+ .tag-restore { background: var(--success-soft); color: var(--success); }
243
+ .tag-same { background: var(--surface-alt); color: var(--muted); }
244
+ .tag-error { background: var(--danger-soft); color: var(--danger); }
245
+
246
+ .bool { font-weight: 700; }
247
+ .bool-yes { color: var(--success); }
248
+ .bool-no { color: var(--danger); }
249
+ .bool-none { color: var(--muted); }
250
+
251
+ /* Tables */
252
+ .table-wrap { overflow-x: auto; }
253
+ .table { width: 100%; border-collapse: collapse; font-size: 11px; }
254
+ .table th, .table td {
255
+ text-align: start; padding: 9px 12px; border-bottom: 1px solid var(--border); vertical-align: middle;
256
+ }
257
+ .table thead th {
258
+ font-weight: 650; color: var(--muted); background: var(--surface-alt); white-space: nowrap;
259
+ }
260
+ .table thead th a { color: inherit; }
261
+ .table thead th.sorted, .table thead th.sorted a { color: var(--accent); }
262
+ .table tbody th { font-weight: 500; }
263
+ .table tbody tr:last-child > * { border-bottom: 0; }
264
+ .table-list tbody tr:hover { background: var(--surface-alt); }
265
+ .table-list tbody tr.selected { background: var(--accent-soft); }
266
+ .table-list th, .table-list td { padding-block: 6px; }
267
+ .table-list input:not([type="checkbox"]), .table-list select { width: 120px; min-height: 24px; padding: 2px 6px; }
268
+ .align-end { text-align: end !important; white-space: nowrap; }
269
+ .col-check { width: 36px; padding-inline-end: 0 !important; }
270
+ .empty { text-align: center !important; padding: 40px 16px !important; color: var(--muted); }
271
+
272
+ /* Dashboard */
273
+ .dashboard { display: grid; grid-template-columns: minmax(0, 2fr) minmax(0, 1fr); gap: 16px; align-items: start; }
274
+ .dashboard-groups { display: grid; gap: 16px; grid-template-columns: repeat(auto-fill, minmax(280px, 1fr)); align-items: start; }
275
+ .activity li { display: grid; grid-template-columns: auto 1fr; gap: 0 8px; padding: 9px 16px; border-bottom: 1px solid var(--border); align-items: baseline; }
276
+ .activity li:last-child { border-bottom: 0; }
277
+ .activity small { grid-column: 2; }
278
+
279
+ /* Changelist */
280
+ .changelist { display: grid; gap: 16px; align-items: start; }
281
+ .changelist.has-filters { grid-template-columns: minmax(0, 1fr) 220px; }
282
+ .changelist-main { min-width: 0; }
283
+ .changelist-main > form[data-changelist] {
284
+ background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); overflow: hidden;
285
+ }
286
+ .search { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin-bottom: 12px; }
287
+ .search input { max-width: 340px; flex: 1 1 200px; }
288
+ .date-drill { display: flex; flex-wrap: wrap; gap: 10px; margin-bottom: 12px; }
289
+ .date-drill .current { font-weight: 650; color: var(--text); }
290
+ .actions-bar {
291
+ display: flex; flex-wrap: wrap; align-items: center; gap: 8px; padding: 10px 12px;
292
+ border-bottom: 1px solid var(--border);
293
+ }
294
+ .actions-bar select { width: auto; max-width: 260px; }
295
+ .table-foot {
296
+ display: flex; flex-wrap: wrap; align-items: center; gap: 12px; padding: 10px 12px;
297
+ border-top: 1px solid var(--border);
298
+ }
299
+ .pagination { display: flex; gap: 2px; margin-inline-start: auto; }
300
+ .pagination a, .pagination span {
301
+ display: inline-flex; align-items: center; justify-content: center; min-width: 30px; height: 30px;
302
+ padding: 0 6px; border-radius: 6px;
303
+ }
304
+ .pagination a:hover { background: var(--surface-alt); text-decoration: none; }
305
+ .pagination .current { background: var(--accent); color: var(--on-accent); font-weight: 650; }
306
+
307
+ .filters { background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius); }
308
+ .filters-head {
309
+ display: flex; align-items: center; justify-content: space-between; padding: 10px 14px;
310
+ border-bottom: 1px solid var(--border);
311
+ }
312
+ .filters-head h2 { font-size: 13px; font-weight: 650; }
313
+ .filter { padding: 8px 14px; border-bottom: 1px solid var(--border); }
314
+ .filter:last-child { border-bottom: 0; }
315
+ .filter summary { cursor: pointer; font-weight: 600; font-size: 13px; }
316
+ .filter ul { margin-top: 4px; max-height: 240px; overflow-y: auto; }
317
+ .filter a { display: block; color: var(--muted); padding: 2px 0; overflow-wrap: anywhere; }
318
+ .filter .selected a { color: var(--accent); font-weight: 650; }
319
+
320
+ /* Forms */
321
+ .fieldset { margin: 0 0 16px; padding: 0; min-width: 0; }
322
+ fieldset.fieldset { position: relative; padding-top: 0; }
323
+ fieldset.fieldset legend.card-title {
324
+ float: left; width: 100%; padding: 10px 16px;
325
+ }
326
+ fieldset.fieldset legend + * { clear: both; }
327
+ details.fieldset > summary { cursor: pointer; }
328
+ details.fieldset:not([open]) > summary { border-bottom: 0; border-radius: var(--radius); }
329
+ .fieldset-description { padding: 10px 16px 0; }
330
+ .form-row { border-bottom: 1px solid var(--border); clear: both; }
331
+ .form-row:last-child { border-bottom: 0; }
332
+ .form-row-split { display: grid; grid-template-columns: repeat(auto-fit, minmax(280px, 1fr)); }
333
+ .field { display: grid; grid-template-columns: 170px minmax(0, 1fr); gap: 4px 16px; padding: 12px 16px; align-items: start; }
334
+ .field > label { font-weight: 550; padding-top: 6px; overflow-wrap: anywhere; }
335
+ .field > label.required::after { content: " *"; color: var(--danger); }
336
+ .field-check > label { padding-top: 0; }
337
+ .field.has-error input:not([type="checkbox"]), .field.has-error select, .field.has-error textarea { border-color: var(--danger); }
338
+ .field-error { color: var(--danger); font-size: 13px; margin-top: 4px; }
339
+ .help { color: var(--muted); font-size: 12.5px; margin-top: 4px; }
340
+ .readonly { padding-top: 6px; overflow-wrap: anywhere; }
341
+
342
+ .submit-row {
343
+ display: flex; flex-wrap: wrap; gap: 8px; align-items: center; padding: 12px 16px; margin-bottom: 16px;
344
+ background: var(--surface); border: 1px solid var(--border); border-radius: var(--radius);
345
+ }
346
+ .card .submit-row { border: 0; padding: 12px 0 0; margin: 0; background: none; }
347
+ .push-end { margin-inline-start: auto; }
348
+
349
+ .inline { margin-bottom: 16px; }
350
+ .table-inline td { vertical-align: top; }
351
+ .table-inline input:not([type="checkbox"]), .table-inline select { min-width: 110px; }
352
+ .col-delete { width: 90px; white-space: nowrap; }
353
+ .delete-toggle { color: var(--danger); font-size: 13px; }
354
+ .stacked-row { border-bottom: 1px solid var(--border); }
355
+ .stacked-head { display: flex; justify-content: space-between; align-items: center; padding: 10px 16px 0; }
356
+ .inline-add { display: block; padding: 10px 16px; }
357
+
358
+ .autocomplete { position: relative; max-width: 480px; }
359
+ .autocomplete-chips { display: flex; flex-wrap: wrap; gap: 4px; }
360
+ .autocomplete-chips:not(:empty) { margin-bottom: 6px; }
361
+ .chip {
362
+ display: inline-flex; align-items: center; gap: 4px; padding: 1px 4px 1px 9px; border-radius: 12px;
363
+ background: var(--accent-soft); color: var(--accent); font-weight: 550;
364
+ }
365
+ .chip button { background: none; border: 0; color: inherit; cursor: pointer; font-size: 15px; padding: 0 3px; }
366
+ .autocomplete-results {
367
+ position: absolute; z-index: 20; inset-inline: 0; margin-top: 2px; max-height: 240px; overflow-y: auto;
368
+ background: var(--surface); border: 1px solid var(--border); border-radius: 6px;
369
+ box-shadow: 0 6px 18px #0002;
370
+ }
371
+ .autocomplete-results button {
372
+ display: block; width: 100%; text-align: start; padding: 7px 10px; background: none; border: 0;
373
+ color: var(--text); font: inherit; cursor: pointer;
374
+ }
375
+ .autocomplete-results button:hover { background: var(--accent-soft); }
376
+
377
+ .dual { display: flex; flex-wrap: wrap; align-items: center; gap: 10px; max-width: 720px; }
378
+ .dual-column { flex: 1 1 200px; display: grid; gap: 6px; min-width: 0; }
379
+ .dual-column select { max-width: none; }
380
+ .dual-buttons { display: flex; flex-direction: column; gap: 6px; }
381
+
382
+ .delete-list { list-style: disc; padding-inline-start: 22px; margin: 8px 0 14px; overflow-wrap: anywhere; }
383
+
384
+ /* Login and error pages */
385
+ .login { min-height: 100vh; display: grid; place-items: center; padding: 16px; }
386
+ .login-card {
387
+ width: 100%; max-width: 360px; padding: 28px; display: grid; gap: 8px;
388
+ background: var(--surface); border: 1px solid var(--border); border-radius: 12px;
389
+ }
390
+ .login-card h1 { font-size: 20px; margin-bottom: 8px; overflow-wrap: anywhere; }
391
+ .login-card label { font-weight: 550; margin-top: 4px; }
392
+ .login-card input { max-width: none; }
393
+ .login-card .button { margin-top: 12px; }
394
+ .login-card .message { margin-bottom: 4px; }
395
+
396
+ @media (max-width: 1000px) {
397
+ .changelist.has-filters, .dashboard { grid-template-columns: minmax(0, 1fr); }
398
+ .filters { order: -1; }
399
+ .filter:not([open]) { padding-block: 8px; }
400
+ }
401
+
402
+ @media (max-width: 760px) {
403
+ .sidebar {
404
+ position: fixed; z-index: 30; inset-block: 0; inset-inline-start: 0; transform: translateX(-100%);
405
+ transition: transform .15s ease;
406
+ }
407
+ .sidebar-inner { height: 100%; max-height: none; }
408
+ .sidebar.open { transform: none; box-shadow: 0 0 24px #0005; }
409
+ .menu-button { display: inline-block; flex: none; }
410
+ .topbar { padding: 8px 16px; flex-wrap: wrap; }
411
+ .user-name { display: none; }
412
+ .content { padding: 16px; }
413
+ .field { grid-template-columns: minmax(0, 1fr); }
414
+ .field > label { padding-top: 0; }
415
+ .field-check { grid-template-columns: auto 1fr; }
416
+ }
417
+
418
+ @media (prefers-reduced-motion: reduce) {
419
+ .sidebar { transition: none; }
420
+ }