aws-inventory-manager 0.17.12__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.
Files changed (152) hide show
  1. aws_inventory_manager-0.17.12.dist-info/LICENSE +21 -0
  2. aws_inventory_manager-0.17.12.dist-info/METADATA +1292 -0
  3. aws_inventory_manager-0.17.12.dist-info/RECORD +152 -0
  4. aws_inventory_manager-0.17.12.dist-info/WHEEL +5 -0
  5. aws_inventory_manager-0.17.12.dist-info/entry_points.txt +2 -0
  6. aws_inventory_manager-0.17.12.dist-info/top_level.txt +1 -0
  7. src/__init__.py +3 -0
  8. src/aws/__init__.py +11 -0
  9. src/aws/client.py +128 -0
  10. src/aws/credentials.py +191 -0
  11. src/aws/rate_limiter.py +177 -0
  12. src/cli/__init__.py +12 -0
  13. src/cli/config.py +130 -0
  14. src/cli/main.py +4046 -0
  15. src/cloudtrail/__init__.py +5 -0
  16. src/cloudtrail/query.py +642 -0
  17. src/config_service/__init__.py +21 -0
  18. src/config_service/collector.py +346 -0
  19. src/config_service/detector.py +256 -0
  20. src/config_service/resource_type_mapping.py +328 -0
  21. src/cost/__init__.py +5 -0
  22. src/cost/analyzer.py +226 -0
  23. src/cost/explorer.py +209 -0
  24. src/cost/reporter.py +237 -0
  25. src/delta/__init__.py +5 -0
  26. src/delta/calculator.py +206 -0
  27. src/delta/differ.py +185 -0
  28. src/delta/formatters.py +272 -0
  29. src/delta/models.py +154 -0
  30. src/delta/reporter.py +234 -0
  31. src/matching/__init__.py +6 -0
  32. src/matching/config.py +52 -0
  33. src/matching/normalizer.py +450 -0
  34. src/matching/prompts.py +33 -0
  35. src/models/__init__.py +21 -0
  36. src/models/config_diff.py +135 -0
  37. src/models/cost_report.py +87 -0
  38. src/models/deletion_operation.py +104 -0
  39. src/models/deletion_record.py +97 -0
  40. src/models/delta_report.py +122 -0
  41. src/models/efs_resource.py +80 -0
  42. src/models/elasticache_resource.py +90 -0
  43. src/models/group.py +318 -0
  44. src/models/inventory.py +133 -0
  45. src/models/protection_rule.py +123 -0
  46. src/models/report.py +288 -0
  47. src/models/resource.py +111 -0
  48. src/models/security_finding.py +102 -0
  49. src/models/snapshot.py +122 -0
  50. src/restore/__init__.py +20 -0
  51. src/restore/audit.py +175 -0
  52. src/restore/cleaner.py +461 -0
  53. src/restore/config.py +209 -0
  54. src/restore/deleter.py +976 -0
  55. src/restore/dependency.py +254 -0
  56. src/restore/safety.py +115 -0
  57. src/security/__init__.py +0 -0
  58. src/security/checks/__init__.py +0 -0
  59. src/security/checks/base.py +56 -0
  60. src/security/checks/ec2_checks.py +88 -0
  61. src/security/checks/elasticache_checks.py +149 -0
  62. src/security/checks/iam_checks.py +102 -0
  63. src/security/checks/rds_checks.py +140 -0
  64. src/security/checks/s3_checks.py +95 -0
  65. src/security/checks/secrets_checks.py +96 -0
  66. src/security/checks/sg_checks.py +142 -0
  67. src/security/cis_mapper.py +97 -0
  68. src/security/models.py +53 -0
  69. src/security/reporter.py +174 -0
  70. src/security/scanner.py +87 -0
  71. src/snapshot/__init__.py +6 -0
  72. src/snapshot/capturer.py +453 -0
  73. src/snapshot/filter.py +259 -0
  74. src/snapshot/inventory_storage.py +236 -0
  75. src/snapshot/report_formatter.py +250 -0
  76. src/snapshot/reporter.py +189 -0
  77. src/snapshot/resource_collectors/__init__.py +5 -0
  78. src/snapshot/resource_collectors/apigateway.py +140 -0
  79. src/snapshot/resource_collectors/backup.py +136 -0
  80. src/snapshot/resource_collectors/base.py +81 -0
  81. src/snapshot/resource_collectors/cloudformation.py +55 -0
  82. src/snapshot/resource_collectors/cloudwatch.py +109 -0
  83. src/snapshot/resource_collectors/codebuild.py +69 -0
  84. src/snapshot/resource_collectors/codepipeline.py +82 -0
  85. src/snapshot/resource_collectors/dynamodb.py +65 -0
  86. src/snapshot/resource_collectors/ec2.py +240 -0
  87. src/snapshot/resource_collectors/ecs.py +215 -0
  88. src/snapshot/resource_collectors/efs_collector.py +102 -0
  89. src/snapshot/resource_collectors/eks.py +200 -0
  90. src/snapshot/resource_collectors/elasticache_collector.py +79 -0
  91. src/snapshot/resource_collectors/elb.py +126 -0
  92. src/snapshot/resource_collectors/eventbridge.py +156 -0
  93. src/snapshot/resource_collectors/glue.py +199 -0
  94. src/snapshot/resource_collectors/iam.py +188 -0
  95. src/snapshot/resource_collectors/kms.py +111 -0
  96. src/snapshot/resource_collectors/lambda_func.py +139 -0
  97. src/snapshot/resource_collectors/rds.py +109 -0
  98. src/snapshot/resource_collectors/route53.py +86 -0
  99. src/snapshot/resource_collectors/s3.py +105 -0
  100. src/snapshot/resource_collectors/secretsmanager.py +70 -0
  101. src/snapshot/resource_collectors/sns.py +68 -0
  102. src/snapshot/resource_collectors/sqs.py +82 -0
  103. src/snapshot/resource_collectors/ssm.py +160 -0
  104. src/snapshot/resource_collectors/stepfunctions.py +74 -0
  105. src/snapshot/resource_collectors/vpcendpoints.py +79 -0
  106. src/snapshot/resource_collectors/waf.py +159 -0
  107. src/snapshot/storage.py +351 -0
  108. src/storage/__init__.py +21 -0
  109. src/storage/audit_store.py +419 -0
  110. src/storage/database.py +294 -0
  111. src/storage/group_store.py +763 -0
  112. src/storage/inventory_store.py +320 -0
  113. src/storage/resource_store.py +416 -0
  114. src/storage/schema.py +339 -0
  115. src/storage/snapshot_store.py +363 -0
  116. src/utils/__init__.py +12 -0
  117. src/utils/export.py +305 -0
  118. src/utils/hash.py +60 -0
  119. src/utils/logging.py +63 -0
  120. src/utils/pagination.py +41 -0
  121. src/utils/paths.py +51 -0
  122. src/utils/progress.py +41 -0
  123. src/utils/unsupported_resources.py +306 -0
  124. src/web/__init__.py +5 -0
  125. src/web/app.py +97 -0
  126. src/web/dependencies.py +69 -0
  127. src/web/routes/__init__.py +1 -0
  128. src/web/routes/api/__init__.py +18 -0
  129. src/web/routes/api/charts.py +156 -0
  130. src/web/routes/api/cleanup.py +186 -0
  131. src/web/routes/api/filters.py +253 -0
  132. src/web/routes/api/groups.py +305 -0
  133. src/web/routes/api/inventories.py +80 -0
  134. src/web/routes/api/queries.py +202 -0
  135. src/web/routes/api/resources.py +393 -0
  136. src/web/routes/api/snapshots.py +314 -0
  137. src/web/routes/api/views.py +260 -0
  138. src/web/routes/pages.py +198 -0
  139. src/web/services/__init__.py +1 -0
  140. src/web/templates/base.html +955 -0
  141. src/web/templates/components/navbar.html +31 -0
  142. src/web/templates/components/sidebar.html +104 -0
  143. src/web/templates/pages/audit_logs.html +86 -0
  144. src/web/templates/pages/cleanup.html +279 -0
  145. src/web/templates/pages/dashboard.html +227 -0
  146. src/web/templates/pages/diff.html +175 -0
  147. src/web/templates/pages/error.html +30 -0
  148. src/web/templates/pages/groups.html +721 -0
  149. src/web/templates/pages/queries.html +246 -0
  150. src/web/templates/pages/resources.html +2429 -0
  151. src/web/templates/pages/snapshot_detail.html +271 -0
  152. src/web/templates/pages/snapshots.html +429 -0
@@ -0,0 +1,955 @@
1
+ <!DOCTYPE html>
2
+ <html lang="en" class="h-full bg-gray-50">
3
+ <head>
4
+ <meta charset="UTF-8">
5
+ <meta name="viewport" content="width=device-width, initial-scale=1.0">
6
+ <title>{% block title %}AWS Inventory Browser{% endblock %}</title>
7
+
8
+ <!-- Tailwind CSS -->
9
+ <script src="https://cdn.tailwindcss.com"></script>
10
+ <script>
11
+ tailwind.config = {
12
+ theme: {
13
+ extend: {
14
+ colors: {
15
+ 'aws-orange': '#FF9900',
16
+ 'aws-dark': '#232F3E',
17
+ 'aws-blue': '#146EB4',
18
+ }
19
+ }
20
+ }
21
+ }
22
+ </script>
23
+
24
+ <!-- HTMX -->
25
+ <script src="https://unpkg.com/htmx.org@1.9.10"></script>
26
+
27
+ <!-- Chart.js -->
28
+ <script src="https://cdn.jsdelivr.net/npm/chart.js@4.4.1"></script>
29
+
30
+ <!-- Tabulator CSS & JS -->
31
+ <link href="https://unpkg.com/tabulator-tables@6.3.0/dist/css/tabulator_simple.min.css" rel="stylesheet">
32
+ <script type="text/javascript" src="https://unpkg.com/tabulator-tables@6.3.0/dist/js/tabulator.min.js"></script>
33
+
34
+ <!-- Alpine.js -->
35
+ <script defer src="https://unpkg.com/alpinejs@3.x.x/dist/cdn.min.js"></script>
36
+
37
+ <!-- Plus Jakarta Sans - Premium Modern Font -->
38
+ <link rel="preconnect" href="https://fonts.googleapis.com">
39
+ <link rel="preconnect" href="https://fonts.gstatic.com" crossorigin>
40
+ <link href="https://fonts.googleapis.com/css2?family=Plus+Jakarta+Sans:wght@400;500;600;700;800&display=swap" rel="stylesheet">
41
+
42
+ <!-- Custom styles -->
43
+ <style>
44
+ :root {
45
+ /* Vibrant color palette */
46
+ --color-primary: #6366f1;
47
+ --color-primary-dark: #4f46e5;
48
+ --color-primary-light: #a5b4fc;
49
+ --color-success: #10b981;
50
+ --color-danger: #f43f5e;
51
+ --color-warning: #f59e0b;
52
+ --color-info: #06b6d4;
53
+
54
+ /* Gradients */
55
+ --gradient-primary: linear-gradient(135deg, #667eea 0%, #764ba2 100%);
56
+ --gradient-success: linear-gradient(135deg, #11998e 0%, #38ef7d 100%);
57
+ --gradient-danger: linear-gradient(135deg, #f093fb 0%, #f5576c 100%);
58
+ --gradient-info: linear-gradient(135deg, #4facfe 0%, #00f2fe 100%);
59
+ --gradient-dark: linear-gradient(135deg, #1e3a5f 0%, #0f172a 100%);
60
+
61
+ /* Shadows */
62
+ --shadow-sm: 0 1px 2px 0 rgb(0 0 0 / 0.05);
63
+ --shadow-md: 0 4px 6px -1px rgb(0 0 0 / 0.1), 0 2px 4px -2px rgb(0 0 0 / 0.1);
64
+ --shadow-lg: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
65
+ --shadow-xl: 0 20px 25px -5px rgb(0 0 0 / 0.1), 0 8px 10px -6px rgb(0 0 0 / 0.1);
66
+ --shadow-glow: 0 0 20px rgba(99, 102, 241, 0.3);
67
+
68
+ /* Radius */
69
+ --radius-sm: 0.5rem;
70
+ --radius-md: 0.75rem;
71
+ --radius-lg: 1rem;
72
+ --radius-xl: 1.25rem;
73
+ }
74
+
75
+ * {
76
+ font-family: 'Plus Jakarta Sans', system-ui, -apple-system, sans-serif;
77
+ }
78
+
79
+ body {
80
+ background: linear-gradient(180deg, #f8fafc 0%, #f1f5f9 100%);
81
+ min-height: 100vh;
82
+ }
83
+
84
+ [x-cloak] { display: none !important; }
85
+ .htmx-indicator { opacity: 0; transition: opacity 200ms ease-in; pointer-events: none; }
86
+ .htmx-request .htmx-indicator { opacity: 1; pointer-events: auto; }
87
+ .htmx-request.htmx-indicator { opacity: 1; pointer-events: auto; }
88
+
89
+ /* ===== FORM ELEMENTS - Premium Styling ===== */
90
+ input[type="text"],
91
+ input[type="email"],
92
+ input[type="password"],
93
+ input[type="search"],
94
+ input[type="number"],
95
+ input[type="url"],
96
+ input[type="tel"],
97
+ textarea,
98
+ select,
99
+ .input {
100
+ padding: 0.75rem 1rem;
101
+ font-size: 0.9375rem;
102
+ font-weight: 500;
103
+ line-height: 1.5;
104
+ color: #1e293b;
105
+ background: linear-gradient(180deg, #ffffff 0%, #f8fafc 100%);
106
+ border: 2px solid #e2e8f0;
107
+ border-radius: var(--radius-md);
108
+ box-shadow: var(--shadow-sm), inset 0 1px 2px rgba(0, 0, 0, 0.02);
109
+ transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
110
+ width: 100%;
111
+ }
112
+
113
+ input[type="text"]:hover,
114
+ input[type="email"]:hover,
115
+ input[type="password"]:hover,
116
+ input[type="search"]:hover,
117
+ input[type="number"]:hover,
118
+ input[type="url"]:hover,
119
+ input[type="tel"]:hover,
120
+ textarea:hover,
121
+ select:hover,
122
+ .input:hover {
123
+ border-color: #cbd5e1;
124
+ background: #ffffff;
125
+ }
126
+
127
+ input[type="text"]:focus,
128
+ input[type="email"]:focus,
129
+ input[type="password"]:focus,
130
+ input[type="search"]:focus,
131
+ input[type="number"]:focus,
132
+ input[type="url"]:focus,
133
+ input[type="tel"]:focus,
134
+ textarea:focus,
135
+ select:focus,
136
+ .input:focus {
137
+ outline: none;
138
+ border-color: var(--color-primary);
139
+ background: #ffffff;
140
+ box-shadow: 0 0 0 4px rgba(99, 102, 241, 0.12), var(--shadow-md);
141
+ }
142
+
143
+ input::placeholder,
144
+ textarea::placeholder {
145
+ color: #94a3b8;
146
+ font-weight: 400;
147
+ }
148
+
149
+ /* Select with custom arrow - ensure consistent styling */
150
+ select,
151
+ select.border-gray-300,
152
+ select[class*="border-gray"] {
153
+ padding: 0.75rem 2.75rem 0.75rem 1rem !important;
154
+ font-size: 0.9375rem !important;
155
+ font-weight: 500 !important;
156
+ color: #1e293b !important;
157
+ background-color: #ffffff !important;
158
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236366f1' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M6 8l4 4 4-4'/%3e%3c/svg%3e") !important;
159
+ background-position: right 0.875rem center !important;
160
+ background-repeat: no-repeat !important;
161
+ background-size: 1.25em 1.25em !important;
162
+ border: 2px solid #e2e8f0 !important;
163
+ border-radius: var(--radius-md) !important;
164
+ box-shadow: var(--shadow-sm), inset 0 1px 2px rgba(0, 0, 0, 0.02) !important;
165
+ min-height: 46px !important;
166
+ appearance: none !important;
167
+ cursor: pointer !important;
168
+ }
169
+
170
+ select:hover,
171
+ select.border-gray-300:hover {
172
+ border-color: #cbd5e1 !important;
173
+ }
174
+
175
+ select:focus,
176
+ select.border-gray-300:focus {
177
+ outline: none !important;
178
+ border-color: var(--color-primary) !important;
179
+ box-shadow: 0 0 0 4px rgba(99, 102, 241, 0.12), var(--shadow-md) !important;
180
+ }
181
+
182
+ /* Remove mt-1 margin adjustment for better alignment */
183
+ select.mt-1 {
184
+ margin-top: 0 !important;
185
+ background-size: 1.25em 1.25em;
186
+ appearance: none;
187
+ cursor: pointer;
188
+ }
189
+
190
+ /* ===== ADVANCED FILTER ELEMENTS ===== */
191
+ /* Small selects in filter conditions */
192
+ select.text-xs,
193
+ select.sm\:text-sm,
194
+ select[class*="rounded"][class*="border-gray"],
195
+ .border-gray-200 select,
196
+ .bg-white select {
197
+ padding: 0.5rem 2.25rem 0.5rem 0.75rem !important;
198
+ font-size: 0.8125rem !important;
199
+ font-weight: 600 !important;
200
+ color: #334155 !important;
201
+ background-color: #ffffff !important;
202
+ background-image: url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' fill='none' viewBox='0 0 20 20'%3e%3cpath stroke='%236366f1' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M6 8l4 4 4-4'/%3e%3c/svg%3e") !important;
203
+ background-position: right 0.5rem center !important;
204
+ background-repeat: no-repeat !important;
205
+ background-size: 1em 1em !important;
206
+ border: 2px solid #e2e8f0 !important;
207
+ border-radius: 0.5rem !important;
208
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05) !important;
209
+ min-height: 36px !important;
210
+ appearance: none !important;
211
+ cursor: pointer !important;
212
+ width: auto !important;
213
+ min-width: 100px !important;
214
+ }
215
+
216
+ select.text-xs:hover,
217
+ select.sm\:text-sm:hover,
218
+ .border-gray-200 select:hover {
219
+ border-color: #a5b4fc !important;
220
+ background-color: #fafaff !important;
221
+ }
222
+
223
+ select.text-xs:focus,
224
+ select.sm\:text-sm:focus,
225
+ .border-gray-200 select:focus {
226
+ outline: none !important;
227
+ border-color: var(--color-primary) !important;
228
+ box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05) !important;
229
+ }
230
+
231
+ /* Logic selectors (AND/OR) with colored backgrounds */
232
+ select.text-blue-700,
233
+ select.text-purple-700,
234
+ select.bg-blue-50,
235
+ select.bg-purple-50,
236
+ select[class*="text-blue-700"],
237
+ select[class*="text-purple-700"] {
238
+ font-weight: 700 !important;
239
+ border-radius: 0.5rem !important;
240
+ border: 2px solid transparent !important;
241
+ }
242
+
243
+ select.bg-blue-50,
244
+ select[class*="bg-blue-50"] {
245
+ background-color: #eff6ff !important;
246
+ border-color: #bfdbfe !important;
247
+ color: #1d4ed8 !important;
248
+ }
249
+
250
+ select.bg-purple-50,
251
+ select[class*="bg-purple-50"] {
252
+ background-color: #faf5ff !important;
253
+ border-color: #ddd6fe !important;
254
+ color: #7c3aed !important;
255
+ }
256
+
257
+ /* Small text inputs in filter conditions */
258
+ input[type="text"].text-xs,
259
+ input[type="text"].sm\:text-sm,
260
+ .border-gray-200 input[type="text"],
261
+ input[type="text"][class*="rounded"][class*="border-gray"],
262
+ input[type="text"][placeholder*="Value"],
263
+ input[type="text"][placeholder*="Enter"] {
264
+ padding: 0.5rem 0.75rem !important;
265
+ font-size: 0.8125rem !important;
266
+ font-weight: 500 !important;
267
+ color: #334155 !important;
268
+ background: #ffffff !important;
269
+ border: 2px solid #e2e8f0 !important;
270
+ border-radius: 0.5rem !important;
271
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05) !important;
272
+ min-height: 36px !important;
273
+ width: auto !important;
274
+ flex: 1 !important;
275
+ }
276
+
277
+ input[type="text"].text-xs:hover,
278
+ input[type="text"].sm\:text-sm:hover,
279
+ .border-gray-200 input[type="text"]:hover {
280
+ border-color: #a5b4fc !important;
281
+ }
282
+
283
+ input[type="text"].text-xs:focus,
284
+ input[type="text"].sm\:text-sm:focus,
285
+ .border-gray-200 input[type="text"]:focus {
286
+ outline: none !important;
287
+ border-color: var(--color-primary) !important;
288
+ box-shadow: 0 0 0 3px rgba(99, 102, 241, 0.15), 0 1px 2px rgba(0, 0, 0, 0.05) !important;
289
+ }
290
+
291
+ /* Multi-select dropdown trigger buttons in conditions */
292
+ .border-gray-200 button[type="button"],
293
+ button.w-full[class*="border"][class*="border-gray"],
294
+ div.relative button[class*="rounded"][class*="border"] {
295
+ padding: 0.5rem 2rem 0.5rem 0.75rem !important;
296
+ font-size: 0.8125rem !important;
297
+ font-weight: 500 !important;
298
+ color: #334155 !important;
299
+ background: #ffffff !important;
300
+ border: 2px solid #e2e8f0 !important;
301
+ border-radius: 0.5rem !important;
302
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.05) !important;
303
+ min-height: 36px !important;
304
+ text-align: left !important;
305
+ cursor: pointer !important;
306
+ }
307
+
308
+ .border-gray-200 button[type="button"]:hover,
309
+ button.w-full[class*="border"][class*="border-gray"]:hover {
310
+ border-color: #a5b4fc !important;
311
+ background: #fafaff !important;
312
+ }
313
+
314
+ /* Filter condition row styling */
315
+ .flex.items-center.space-x-2.p-2.bg-white.rounded-lg,
316
+ .flex.items-center.space-x-2.p-3.bg-white.rounded-lg {
317
+ background: linear-gradient(180deg, #ffffff 0%, #fafbfc 100%) !important;
318
+ border: 2px solid #e2e8f0 !important;
319
+ border-radius: 0.75rem !important;
320
+ box-shadow: 0 1px 3px rgba(0, 0, 0, 0.04) !important;
321
+ padding: 0.75rem !important;
322
+ }
323
+
324
+ .flex.items-center.space-x-2.p-2.bg-white.rounded-lg:hover,
325
+ .flex.items-center.space-x-2.p-3.bg-white.rounded-lg:hover {
326
+ border-color: #cbd5e1 !important;
327
+ box-shadow: 0 2px 4px rgba(0, 0, 0, 0.06) !important;
328
+ }
329
+
330
+ /* Filter group containers */
331
+ .border-2.rounded-lg.p-3.ml-4[class*="border-blue"],
332
+ .border-2.rounded-lg.p-3.ml-4[class*="border-purple"],
333
+ div[class*="border-blue-200"][class*="bg-blue-50"],
334
+ div[class*="border-purple-200"][class*="bg-purple-50"] {
335
+ border-radius: 0.75rem !important;
336
+ border-width: 2px !important;
337
+ }
338
+
339
+ /* Root filter group */
340
+ .border-2.border-gray-200.rounded-lg.p-4.bg-gray-50 {
341
+ background: linear-gradient(180deg, #f8fafc 0%, #f1f5f9 100%) !important;
342
+ border: 2px solid #e2e8f0 !important;
343
+ border-radius: 1rem !important;
344
+ box-shadow: inset 0 1px 2px rgba(0, 0, 0, 0.02) !important;
345
+ }
346
+
347
+ /* Add/Remove condition buttons */
348
+ button.inline-flex[class*="px-2"][class*="py-1"][class*="text-xs"] {
349
+ padding: 0.375rem 0.75rem !important;
350
+ font-size: 0.75rem !important;
351
+ font-weight: 600 !important;
352
+ background: linear-gradient(180deg, #ffffff 0%, #f8fafc 100%) !important;
353
+ border: 1.5px solid #e2e8f0 !important;
354
+ border-radius: 0.5rem !important;
355
+ color: #475569 !important;
356
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.04) !important;
357
+ transition: all 0.2s ease !important;
358
+ }
359
+
360
+ button.inline-flex[class*="px-2"][class*="py-1"][class*="text-xs"]:hover {
361
+ border-color: #a5b4fc !important;
362
+ background: #ffffff !important;
363
+ color: var(--color-primary) !important;
364
+ box-shadow: 0 2px 4px rgba(99, 102, 241, 0.12) !important;
365
+ }
366
+
367
+ /* Remove button (X) */
368
+ button.p-1.text-red-500,
369
+ button.p-2.text-red-600,
370
+ button[class*="text-red"][class*="hover:text-red"] {
371
+ color: #f43f5e !important;
372
+ border-radius: 0.375rem !important;
373
+ transition: all 0.2s ease !important;
374
+ }
375
+
376
+ button.p-1.text-red-500:hover,
377
+ button.p-2.text-red-600:hover,
378
+ button[class*="text-red"][class*="hover:text-red"]:hover {
379
+ background: linear-gradient(135deg, #fef2f2 0%, #fee2e2 100%) !important;
380
+ color: #dc2626 !important;
381
+ }
382
+
383
+ /* Logic connector pills */
384
+ span.px-2.py-0\.5.text-xs.font-bold.rounded-full,
385
+ span.px-1\.5.py-0\.5.text-xs.font-bold.rounded-full {
386
+ font-weight: 800 !important;
387
+ letter-spacing: 0.05em !important;
388
+ box-shadow: 0 1px 2px rgba(0, 0, 0, 0.1) !important;
389
+ }
390
+
391
+ span.bg-blue-100.text-blue-700 {
392
+ background: linear-gradient(135deg, #dbeafe 0%, #bfdbfe 100%) !important;
393
+ color: #1d4ed8 !important;
394
+ }
395
+
396
+ span.bg-purple-100.text-purple-700 {
397
+ background: linear-gradient(135deg, #f3e8ff 0%, #e9d5ff 100%) !important;
398
+ color: #7c3aed !important;
399
+ }
400
+
401
+ /* Checkbox and Radio - Modern Style */
402
+ input[type="checkbox"],
403
+ input[type="radio"] {
404
+ width: 1.25rem;
405
+ height: 1.25rem;
406
+ color: var(--color-primary);
407
+ border: 2px solid #cbd5e1;
408
+ border-radius: 0.375rem;
409
+ cursor: pointer;
410
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
411
+ accent-color: var(--color-primary);
412
+ }
413
+
414
+ input[type="radio"] {
415
+ border-radius: 50%;
416
+ }
417
+
418
+ input[type="checkbox"]:hover,
419
+ input[type="radio"]:hover {
420
+ border-color: var(--color-primary-light);
421
+ }
422
+
423
+ input[type="checkbox"]:checked,
424
+ input[type="radio"]:checked {
425
+ background-color: var(--color-primary);
426
+ border-color: var(--color-primary);
427
+ box-shadow: 0 0 0 2px rgba(99, 102, 241, 0.2);
428
+ }
429
+
430
+ input[type="checkbox"]:focus,
431
+ input[type="radio"]:focus {
432
+ outline: none;
433
+ box-shadow: 0 0 0 4px rgba(99, 102, 241, 0.15);
434
+ }
435
+
436
+ /* Form Labels */
437
+ label {
438
+ display: block;
439
+ font-size: 0.8125rem;
440
+ font-weight: 600;
441
+ color: #475569;
442
+ margin-bottom: 0.5rem;
443
+ letter-spacing: 0.01em;
444
+ }
445
+
446
+ /* ===== DROPDOWN BUTTONS & SELECTS ===== */
447
+ /* Multi-select dropdown buttons */
448
+ .relative button[type="button"] {
449
+ padding: 0.75rem 2.5rem 0.75rem 1rem;
450
+ font-size: 0.9375rem;
451
+ font-weight: 500;
452
+ color: #1e293b;
453
+ background: linear-gradient(180deg, #ffffff 0%, #f8fafc 100%);
454
+ border: 2px solid #e2e8f0;
455
+ border-radius: var(--radius-md);
456
+ box-shadow: var(--shadow-sm), inset 0 1px 2px rgba(0, 0, 0, 0.02);
457
+ transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
458
+ min-height: 46px;
459
+ }
460
+
461
+ .relative button[type="button"]:hover {
462
+ border-color: #cbd5e1;
463
+ background: #ffffff;
464
+ }
465
+
466
+ .relative button[type="button"]:focus {
467
+ outline: none;
468
+ border-color: var(--color-primary);
469
+ background: #ffffff;
470
+ box-shadow: 0 0 0 4px rgba(99, 102, 241, 0.12), var(--shadow-md);
471
+ }
472
+
473
+ /* Active filter state */
474
+ .relative button[type="button"].border-green-500 {
475
+ border-color: #10b981 !important;
476
+ background: linear-gradient(180deg, #ecfdf5 0%, #d1fae5 100%) !important;
477
+ }
478
+
479
+ /* Dropdown Menu Panels */
480
+ .absolute[class*="shadow-lg"],
481
+ [x-show][class*="bg-white"][class*="shadow-lg"] {
482
+ background: #ffffff;
483
+ border: 1px solid #e2e8f0;
484
+ border-radius: var(--radius-md);
485
+ box-shadow: var(--shadow-xl);
486
+ margin-top: 0.5rem;
487
+ }
488
+
489
+ /* Dropdown Menu Items - checkbox labels */
490
+ .absolute label,
491
+ [x-show] label.flex {
492
+ padding: 0.625rem 1rem;
493
+ font-size: 0.875rem;
494
+ font-weight: 500;
495
+ cursor: pointer;
496
+ transition: all 0.15s ease;
497
+ }
498
+
499
+ .absolute label:hover,
500
+ [x-show] label.flex:hover {
501
+ background: linear-gradient(90deg, rgba(99, 102, 241, 0.06) 0%, rgba(139, 92, 246, 0.06) 100%);
502
+ }
503
+
504
+ /* Dropdown Header */
505
+ .absolute .border-b,
506
+ [x-show] .border-b {
507
+ padding: 0.75rem 1rem;
508
+ background: #f8fafc;
509
+ border-bottom: 1px solid #e2e8f0;
510
+ }
511
+
512
+ /* ===== BUTTONS - Compact ===== */
513
+ .btn {
514
+ display: inline-flex;
515
+ align-items: center;
516
+ justify-content: center;
517
+ gap: 0.25rem;
518
+ padding: 0.375rem 0.75rem;
519
+ font-size: 0.75rem;
520
+ font-weight: 600;
521
+ line-height: 1.25;
522
+ letter-spacing: 0.01em;
523
+ border-radius: var(--radius-sm);
524
+ transition: all 0.2s ease;
525
+ cursor: pointer;
526
+ border: none;
527
+ text-decoration: none;
528
+ position: relative;
529
+ overflow: hidden;
530
+ }
531
+
532
+ .btn::before {
533
+ content: '';
534
+ position: absolute;
535
+ top: 0;
536
+ left: -100%;
537
+ width: 100%;
538
+ height: 100%;
539
+ background: linear-gradient(90deg, transparent, rgba(255,255,255,0.2), transparent);
540
+ transition: left 0.5s ease;
541
+ }
542
+
543
+ .btn:hover::before {
544
+ left: 100%;
545
+ }
546
+
547
+ .btn:focus {
548
+ outline: none;
549
+ }
550
+
551
+ .btn:disabled {
552
+ opacity: 0.5;
553
+ cursor: not-allowed;
554
+ transform: none !important;
555
+ }
556
+
557
+ .btn-primary {
558
+ background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
559
+ color: white;
560
+ box-shadow: 0 4px 14px 0 rgba(99, 102, 241, 0.4), inset 0 1px 0 0 rgba(255, 255, 255, 0.15);
561
+ }
562
+
563
+ .btn-primary:hover:not(:disabled) {
564
+ background: linear-gradient(135deg, #4f46e5 0%, #7c3aed 100%);
565
+ box-shadow: 0 6px 20px 0 rgba(99, 102, 241, 0.5), inset 0 1px 0 0 rgba(255, 255, 255, 0.15);
566
+ transform: translateY(-2px);
567
+ }
568
+
569
+ .btn-primary:active:not(:disabled) {
570
+ transform: translateY(0);
571
+ box-shadow: 0 2px 8px 0 rgba(99, 102, 241, 0.4);
572
+ }
573
+
574
+ .btn-primary:focus {
575
+ box-shadow: 0 4px 14px 0 rgba(99, 102, 241, 0.4), 0 0 0 4px rgba(99, 102, 241, 0.2);
576
+ }
577
+
578
+ .btn-secondary {
579
+ background: linear-gradient(180deg, #ffffff 0%, #f8fafc 100%);
580
+ color: #475569;
581
+ border: 2px solid #e2e8f0;
582
+ box-shadow: var(--shadow-sm);
583
+ }
584
+
585
+ .btn-secondary:hover:not(:disabled) {
586
+ background: linear-gradient(180deg, #f8fafc 0%, #f1f5f9 100%);
587
+ border-color: #cbd5e1;
588
+ transform: translateY(-2px);
589
+ box-shadow: var(--shadow-md);
590
+ }
591
+
592
+ .btn-secondary:focus {
593
+ box-shadow: var(--shadow-sm), 0 0 0 4px rgba(99, 102, 241, 0.1);
594
+ }
595
+
596
+ .btn-success {
597
+ background: linear-gradient(135deg, #10b981 0%, #06d6a0 100%);
598
+ color: white;
599
+ box-shadow: 0 4px 14px 0 rgba(16, 185, 129, 0.4), inset 0 1px 0 0 rgba(255, 255, 255, 0.15);
600
+ }
601
+
602
+ .btn-success:hover:not(:disabled) {
603
+ background: linear-gradient(135deg, #059669 0%, #10b981 100%);
604
+ box-shadow: 0 6px 20px 0 rgba(16, 185, 129, 0.5);
605
+ transform: translateY(-2px);
606
+ }
607
+
608
+ .btn-danger {
609
+ background: linear-gradient(135deg, #f43f5e 0%, #ec4899 100%);
610
+ color: white;
611
+ box-shadow: 0 4px 14px 0 rgba(244, 63, 94, 0.4), inset 0 1px 0 0 rgba(255, 255, 255, 0.15);
612
+ }
613
+
614
+ .btn-danger:hover:not(:disabled) {
615
+ background: linear-gradient(135deg, #e11d48 0%, #db2777 100%);
616
+ box-shadow: 0 6px 20px 0 rgba(244, 63, 94, 0.5);
617
+ transform: translateY(-2px);
618
+ }
619
+
620
+ .btn-ghost {
621
+ background: transparent;
622
+ color: #64748b;
623
+ box-shadow: none;
624
+ }
625
+
626
+ .btn-ghost:hover:not(:disabled) {
627
+ background: rgba(99, 102, 241, 0.08);
628
+ color: var(--color-primary);
629
+ }
630
+
631
+ .btn-sm {
632
+ padding: 0.5rem 1rem;
633
+ font-size: 0.8125rem;
634
+ }
635
+
636
+ .btn-lg {
637
+ padding: 1rem 2rem;
638
+ font-size: 1rem;
639
+ }
640
+
641
+ .btn-icon {
642
+ padding: 0.625rem;
643
+ border-radius: var(--radius-sm);
644
+ }
645
+
646
+ /* Table container - ensure full width */
647
+ #resource-table {
648
+ width: 100% !important;
649
+ overflow-x: auto;
650
+ }
651
+
652
+ /* Tabulator - Modern Premium Style */
653
+ .tabulator {
654
+ border: none;
655
+ border-radius: var(--radius-lg);
656
+ font-family: inherit;
657
+ background: #ffffff;
658
+ box-shadow: var(--shadow-lg);
659
+ overflow: hidden;
660
+ }
661
+
662
+ .tabulator .tabulator-tableholder {
663
+ overflow-x: auto;
664
+ overflow-y: auto;
665
+ }
666
+
667
+ .tabulator-table {
668
+ table-layout: auto;
669
+ }
670
+
671
+ .tabulator .tabulator-header {
672
+ background: linear-gradient(180deg, #f8fafc 0%, #f1f5f9 100%);
673
+ border-bottom: 2px solid #e2e8f0;
674
+ font-weight: 600;
675
+ overflow: visible;
676
+ }
677
+
678
+ .tabulator .tabulator-header .tabulator-headers {
679
+ overflow: visible;
680
+ }
681
+
682
+ .tabulator .tabulator-header .tabulator-col {
683
+ background: transparent;
684
+ border-right: 1px solid #e2e8f0;
685
+ }
686
+
687
+ .tabulator .tabulator-header .tabulator-col.tabulator-sortable:hover {
688
+ background: linear-gradient(180deg, rgba(99, 102, 241, 0.05) 0%, rgba(99, 102, 241, 0.1) 100%);
689
+ }
690
+
691
+ .tabulator .tabulator-header .tabulator-col .tabulator-col-content {
692
+ padding: 6px 10px;
693
+ }
694
+
695
+ .tabulator .tabulator-header .tabulator-col .tabulator-col-title {
696
+ font-size: 0.625rem;
697
+ text-transform: uppercase;
698
+ letter-spacing: 0.05em;
699
+ color: #64748b;
700
+ font-weight: 700;
701
+ }
702
+
703
+ .tabulator .tabulator-header .tabulator-frozen {
704
+ background: linear-gradient(180deg, #f8fafc 0%, #f1f5f9 100%);
705
+ border-right: 2px solid var(--color-primary-light) !important;
706
+ }
707
+
708
+ .tabulator-row {
709
+ border-bottom: 1px solid #f1f5f9;
710
+ transition: all 0.2s cubic-bezier(0.4, 0, 0.2, 1);
711
+ }
712
+
713
+ .tabulator-row:nth-child(even) {
714
+ background-color: #fafbfc;
715
+ }
716
+
717
+ .tabulator-row:hover {
718
+ background: linear-gradient(90deg, rgba(99, 102, 241, 0.04) 0%, rgba(139, 92, 246, 0.04) 100%) !important;
719
+ transform: scale(1.001);
720
+ }
721
+
722
+ .tabulator-row .tabulator-cell {
723
+ padding: 6px 10px;
724
+ font-size: 0.75rem;
725
+ font-weight: 500;
726
+ color: #334155;
727
+ border-right: 1px solid #f1f5f9;
728
+ overflow: hidden;
729
+ text-overflow: ellipsis;
730
+ white-space: nowrap;
731
+ }
732
+
733
+ .tabulator-row .tabulator-frozen {
734
+ background: inherit;
735
+ border-right: 2px solid var(--color-primary-light) !important;
736
+ }
737
+
738
+ .tabulator-row:nth-child(even) .tabulator-frozen {
739
+ background-color: #fafbfc;
740
+ }
741
+
742
+ .tabulator-row:hover .tabulator-frozen {
743
+ background: linear-gradient(90deg, rgba(99, 102, 241, 0.04) 0%, rgba(139, 92, 246, 0.04) 100%) !important;
744
+ }
745
+
746
+ /* Column resize handle */
747
+ .tabulator .tabulator-header .tabulator-col .tabulator-col-resize-handle {
748
+ width: 4px;
749
+ background: transparent;
750
+ transition: background-color 0.2s ease;
751
+ }
752
+
753
+ .tabulator .tabulator-header .tabulator-col .tabulator-col-resize-handle:hover {
754
+ background: var(--color-primary);
755
+ }
756
+
757
+ /* Frozen column shadow */
758
+ .tabulator .tabulator-frozen-right {
759
+ box-shadow: -4px 0 12px rgba(0, 0, 0, 0.08);
760
+ }
761
+
762
+ .tabulator .tabulator-frozen-left {
763
+ box-shadow: 4px 0 12px rgba(0, 0, 0, 0.08);
764
+ }
765
+
766
+ /* Sort arrows */
767
+ .tabulator .tabulator-header .tabulator-col.tabulator-sortable .tabulator-col-sorter {
768
+ color: #94a3b8;
769
+ }
770
+
771
+ .tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort="asc"] .tabulator-col-sorter,
772
+ .tabulator .tabulator-header .tabulator-col.tabulator-sortable[aria-sort="desc"] .tabulator-col-sorter {
773
+ color: var(--color-primary);
774
+ }
775
+
776
+ /* Move handle styling */
777
+ .tabulator .tabulator-header .tabulator-col .tabulator-col-resize-guide {
778
+ background: var(--color-primary);
779
+ }
780
+
781
+ /* Footer/pagination */
782
+ .tabulator .tabulator-footer {
783
+ background: linear-gradient(180deg, #f8fafc 0%, #f1f5f9 100%);
784
+ border-top: 2px solid #e2e8f0;
785
+ padding: 14px 16px;
786
+ }
787
+
788
+ .tabulator .tabulator-footer .tabulator-page {
789
+ background: white;
790
+ border: 2px solid #e2e8f0;
791
+ border-radius: var(--radius-sm);
792
+ padding: 8px 14px;
793
+ margin: 0 3px;
794
+ font-weight: 600;
795
+ transition: all 0.2s ease;
796
+ }
797
+
798
+ .tabulator .tabulator-footer .tabulator-page:hover {
799
+ border-color: var(--color-primary-light);
800
+ background: rgba(99, 102, 241, 0.05);
801
+ }
802
+
803
+ .tabulator .tabulator-footer .tabulator-page.active {
804
+ background: linear-gradient(135deg, #6366f1 0%, #8b5cf6 100%);
805
+ border-color: transparent;
806
+ color: white;
807
+ box-shadow: 0 2px 8px rgba(99, 102, 241, 0.4);
808
+ }
809
+
810
+ /* Row selection */
811
+ .tabulator-row.tabulator-selected {
812
+ background: linear-gradient(90deg, rgba(99, 102, 241, 0.1) 0%, rgba(139, 92, 246, 0.1) 100%) !important;
813
+ }
814
+
815
+ .tabulator-row.tabulator-selected:hover {
816
+ background: linear-gradient(90deg, rgba(99, 102, 241, 0.15) 0%, rgba(139, 92, 246, 0.15) 100%) !important;
817
+ }
818
+
819
+ /* Resource type badges - Vibrant */
820
+ .badge {
821
+ display: inline-flex;
822
+ align-items: center;
823
+ padding: 0.375rem 0.75rem;
824
+ font-size: 0.6875rem;
825
+ font-weight: 700;
826
+ border-radius: var(--radius-sm);
827
+ text-transform: uppercase;
828
+ letter-spacing: 0.05em;
829
+ box-shadow: var(--shadow-sm);
830
+ }
831
+
832
+ .badge-s3 { background: linear-gradient(135deg, #d1fae5 0%, #a7f3d0 100%); color: #065f46; border: 1px solid #6ee7b7; }
833
+ .badge-ec2 { background: linear-gradient(135deg, #ffedd5 0%, #fed7aa 100%); color: #9a3412; border: 1px solid #fdba74; }
834
+ .badge-lambda { background: linear-gradient(135deg, #fef3c7 0%, #fde68a 100%); color: #92400e; border: 1px solid #fcd34d; }
835
+ .badge-iam { background: linear-gradient(135deg, #fce7f3 0%, #fbcfe8 100%); color: #9d174d; border: 1px solid #f9a8d4; }
836
+ .badge-rds { background: linear-gradient(135deg, #dbeafe 0%, #bfdbfe 100%); color: #1e40af; border: 1px solid #93c5fd; }
837
+ .badge-dynamodb { background: linear-gradient(135deg, #e0e7ff 0%, #c7d2fe 100%); color: #3730a3; border: 1px solid #a5b4fc; }
838
+ .badge-kms { background: linear-gradient(135deg, #f3e8ff 0%, #e9d5ff 100%); color: #6b21a8; border: 1px solid #d8b4fe; }
839
+ .badge-logs { background: linear-gradient(135deg, #cffafe 0%, #a5f3fc 100%); color: #155e75; border: 1px solid #67e8f9; }
840
+ .badge-default { background: linear-gradient(135deg, #f1f5f9 0%, #e2e8f0 100%); color: #475569; border: 1px solid #cbd5e1; }
841
+
842
+ /* Custom scrollbar */
843
+ .table-container {
844
+ scrollbar-width: thin;
845
+ scrollbar-color: #c7d2fe #f1f5f9;
846
+ }
847
+
848
+ .table-container::-webkit-scrollbar {
849
+ width: 10px;
850
+ height: 10px;
851
+ }
852
+
853
+ .table-container::-webkit-scrollbar-track {
854
+ background: #f1f5f9;
855
+ border-radius: 10px;
856
+ }
857
+
858
+ .table-container::-webkit-scrollbar-thumb {
859
+ background: linear-gradient(180deg, #a5b4fc 0%, #818cf8 100%);
860
+ border-radius: 10px;
861
+ border: 2px solid #f1f5f9;
862
+ }
863
+
864
+ .table-container::-webkit-scrollbar-thumb:hover {
865
+ background: linear-gradient(180deg, #818cf8 0%, #6366f1 100%);
866
+ }
867
+
868
+ /* Card styling - Premium */
869
+ .card {
870
+ background: #ffffff;
871
+ border-radius: var(--radius-lg);
872
+ box-shadow: var(--shadow-md);
873
+ border: 1px solid rgba(226, 232, 240, 0.8);
874
+ width: 100%;
875
+ max-width: 100%;
876
+ }
877
+
878
+ .card-header {
879
+ padding: 1.25rem 1.5rem;
880
+ border-bottom: 1px solid #f1f5f9;
881
+ background: linear-gradient(180deg, #ffffff 0%, #fafbfc 100%);
882
+ border-radius: var(--radius-lg) var(--radius-lg) 0 0;
883
+ }
884
+
885
+ /* Section headers */
886
+ h1, h2, h3 {
887
+ font-weight: 700;
888
+ letter-spacing: -0.02em;
889
+ color: #1e293b;
890
+ }
891
+
892
+ /* Page headings with gradient */
893
+ .page-title {
894
+ font-size: 1.875rem;
895
+ font-weight: 800;
896
+ background: linear-gradient(135deg, #1e293b 0%, #475569 100%);
897
+ -webkit-background-clip: text;
898
+ -webkit-text-fill-color: transparent;
899
+ background-clip: text;
900
+ }
901
+
902
+ /* Subtle animations */
903
+ @keyframes fadeIn {
904
+ from { opacity: 0; transform: translateY(10px); }
905
+ to { opacity: 1; transform: translateY(0); }
906
+ }
907
+
908
+ .animate-fade-in {
909
+ animation: fadeIn 0.4s ease-out;
910
+ }
911
+
912
+ /* Glass effect for modals */
913
+ .glass {
914
+ background: rgba(255, 255, 255, 0.9);
915
+ backdrop-filter: blur(10px);
916
+ -webkit-backdrop-filter: blur(10px);
917
+ }
918
+
919
+ /* Hover lift effect */
920
+ .hover-lift {
921
+ transition: all 0.3s cubic-bezier(0.4, 0, 0.2, 1);
922
+ }
923
+
924
+ .hover-lift:hover {
925
+ transform: translateY(-4px);
926
+ box-shadow: var(--shadow-xl);
927
+ }
928
+ </style>
929
+
930
+ {% block head %}{% endblock %}
931
+ </head>
932
+ <body class="h-full antialiased overflow-hidden">
933
+ <div class="h-screen flex overflow-hidden">
934
+ {% include "components/sidebar.html" %}
935
+
936
+ <div class="flex-1 flex flex-col h-screen overflow-hidden">
937
+ {% include "components/navbar.html" %}
938
+
939
+ <main class="flex-1 p-4 overflow-auto min-h-0">
940
+ <!-- Loading indicator -->
941
+ <div class="htmx-indicator fixed top-20 right-6 z-50">
942
+ <div class="flex items-center space-x-2 bg-white px-4 py-2 rounded-full shadow-lg border border-gray-100">
943
+ <div class="animate-spin rounded-full h-5 w-5 border-2 border-indigo-600 border-t-transparent"></div>
944
+ <span class="text-sm font-medium text-gray-600">Loading...</span>
945
+ </div>
946
+ </div>
947
+
948
+ {% block content %}{% endblock %}
949
+ </main>
950
+ </div>
951
+ </div>
952
+
953
+ {% block scripts %}{% endblock %}
954
+ </body>
955
+ </html>