Leojef commited on
Commit
31e3709
·
verified ·
1 Parent(s): c2ab933

crie uma plataforma de monitoramento com foco em tratamento de alarmes e cameras,crie abas dashboards, alarmes, clientes ,plantas e alertas

Browse files
README.md CHANGED
@@ -1,10 +1,13 @@
1
  ---
2
- title: Watchtower Sentinel
3
- emoji: 🐠
4
  colorFrom: gray
5
- colorTo: indigo
 
6
  sdk: static
7
  pinned: false
 
 
8
  ---
9
 
10
- Check out the configuration reference at https://huggingface.co/docs/hub/spaces-config-reference
 
 
1
  ---
2
+ title: WatchTower Sentinel 🚨
 
3
  colorFrom: gray
4
+ colorTo: gray
5
+ emoji: 🐳
6
  sdk: static
7
  pinned: false
8
+ tags:
9
+ - deepsite-v3
10
  ---
11
 
12
+ # Welcome to your new DeepSite project!
13
+ This project was created with [DeepSite](https://huggingface.co/deepsite).
components/alert-card.js ADDED
@@ -0,0 +1,71 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class CustomAlertCard extends HTMLElement {
2
+ connectedCallback() {
3
+ const type = this.getAttribute('type') || 'alert';
4
+ const location = this.getAttribute('location') || 'Unknown';
5
+ const time = this.getAttribute('time') || 'Just now';
6
+ const priority = this.getAttribute('priority') || 'medium';
7
+
8
+ const priorityColors = {
9
+ 'critical': 'red',
10
+ 'high': 'orange',
11
+ 'medium': 'yellow',
12
+ 'low': 'green'
13
+ };
14
+
15
+ const typeIcons = {
16
+ 'motion': 'activity',
17
+ 'door': 'unlock',
18
+ 'temperature': 'thermometer',
19
+ 'power': 'zap',
20
+ 'fire': 'flame',
21
+ 'water': 'droplet'
22
+ };
23
+
24
+ this.attachShadow({ mode: 'open' });
25
+ this.shadowRoot.innerHTML = `
26
+ <style>
27
+ .alert-card {
28
+ border-left: 4px solid;
29
+ transition: all 0.2s ease;
30
+ }
31
+ .alert-card:hover {
32
+ transform: translateX(2px);
33
+ }
34
+ .critical {
35
+ border-left-color: rgb(239, 68, 68);
36
+ }
37
+ .high {
38
+ border-left-color: rgb(249, 115, 22);
39
+ }
40
+ .medium {
41
+ border-left-color: rgb(234, 179, 8);
42
+ }
43
+ .low {
44
+ border-left-color: rgb(34, 197, 94);
45
+ }
46
+ </style>
47
+ <div class="alert-card bg-white p-4 rounded shadow-sm ${priority}">
48
+ <div class="flex items-start">
49
+ <div class="p-2 rounded-lg bg-${priorityColors[priority]}-100 text-${priorityColors[priority]}-600 mr-3">
50
+ <i data-feather="${typeIcons[type] || 'alert-circle'}"></i>
51
+ </div>
52
+ <div class="flex-1">
53
+ <div class="flex justify-between items-start">
54
+ <h4 class="font-medium text-gray-800 capitalize">${type} detected</h4>
55
+ <span class="text-xs text-gray-500">${time}</span>
56
+ </div>
57
+ <p class="text-sm text-gray-600 mt-1">Location: ${location}</p>
58
+ <div class="mt-2 flex justify-between items-center">
59
+ <span class="text-xs px-2 py-1 rounded bg-${priorityColors[priority]}-100 text-${priorityColors[priority]}-800 capitalize">${priority}</span>
60
+ <div class="flex space-x-2">
61
+ <button class="text-xs text-blue-600 hover:text-blue-800">Acknowledge</button>
62
+ <button class="text-xs text-gray-600 hover:text-gray-800">Details</button>
63
+ </div>
64
+ </div>
65
+ </div>
66
+ </div>
67
+ </div>
68
+ `;
69
+ }
70
+ }
71
+ customElements.define('custom-alert-card', CustomAlertCard);
components/camera-card.js ADDED
@@ -0,0 +1,54 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class CustomCameraCard extends HTMLElement {
2
+ connectedCallback() {
3
+ const name = this.getAttribute('name') || 'Camera';
4
+ const status = this.getAttribute('status') || 'active';
5
+ const lastAlert = this.getAttribute('last-alert') || 'Never';
6
+ const image = this.getAttribute('image') || 'http://static.photos/technology/640x360/1';
7
+
8
+ const statusColors = {
9
+ 'active': 'green',
10
+ 'alert': 'red',
11
+ 'offline': 'gray'
12
+ };
13
+
14
+ const statusTexts = {
15
+ 'active': 'Active',
16
+ 'alert': 'Alert',
17
+ 'offline': 'Offline'
18
+ };
19
+
20
+ this.attachShadow({ mode: 'open' });
21
+ this.shadowRoot.innerHTML = `
22
+ <style>
23
+ .camera-image {
24
+ height: 180px;
25
+ object-fit: cover;
26
+ }
27
+ .status-badge {
28
+ font-size: 0.75rem;
29
+ padding: 0.25rem 0.5rem;
30
+ }
31
+ </style>
32
+ <div class="bg-white rounded-lg overflow-hidden border border-gray-200">
33
+ <div class="relative">
34
+ <img src="${image}" alt="${name}" class="w-full camera-image">
35
+ <div class="absolute top-2 right-2">
36
+ <span class="status-badge rounded-full bg-${statusColors[status]}-100 text-${statusColors[status]}-800">
37
+ ${statusTexts[status]}
38
+ </span>
39
+ </div>
40
+ </div>
41
+ <div class="p-4">
42
+ <h3 class="font-medium text-gray-800">${name}</h3>
43
+ <div class="flex justify-between items-center mt-2">
44
+ <span class="text-sm text-gray-500">Last alert: ${lastAlert}</span>
45
+ <button class="text-blue-600 hover:text-blue-800 text-sm font-medium">
46
+ View
47
+ </button>
48
+ </div>
49
+ </div>
50
+ </div>
51
+ `;
52
+ }
53
+ }
54
+ customElements.define('custom-camera-card', CustomCameraCard);
components/navbar.js ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class CustomNavbar extends HTMLElement {
2
+ connectedCallback() {
3
+ this.attachShadow({ mode: 'open' });
4
+ this.shadowRoot.innerHTML = `
5
+ <style>
6
+ .navbar {
7
+ height: 60px;
8
+ box-shadow: 0 1px 3px 0 rgb(0 0 0 / 0.1), 0 1px 2px -1px rgb(0 0 0 / 0.1);
9
+ }
10
+ .user-menu {
11
+ transition: all 0.3s ease;
12
+ }
13
+ .user-menu:hover {
14
+ background-color: rgba(0, 0, 0, 0.05);
15
+ }
16
+ </style>
17
+ <nav class="navbar bg-white w-full fixed z-10">
18
+ <div class="flex items-center justify-between h-full px-6">
19
+ <div class="flex items-center space-x-4">
20
+ <span class="text-xl font-bold text-gray-800">WatchTower Sentinel</span>
21
+ </div>
22
+
23
+ <div class="flex items-center space-x-4">
24
+ <button class="p-2 rounded-full hover:bg-gray-100">
25
+ <i data-feather="bell"></i>
26
+ </button>
27
+ <button class="p-2 rounded-full hover:bg-gray-100">
28
+ <i data-feather="settings"></i>
29
+ </button>
30
+
31
+ <div class="flex items-center space-x-2 pl-4 user-menu rounded-full cursor-pointer">
32
+ <div class="w-8 h-8 rounded-full bg-blue-500 flex items-center justify-center text-white font-medium">
33
+ JS
34
+ </div>
35
+ <span class="text-sm font-medium text-gray-700">John Smith</span>
36
+ </div>
37
+ </div>
38
+ </div>
39
+ </nav>
40
+ `;
41
+ }
42
+ }
43
+ customElements.define('custom-navbar', CustomNavbar);
components/sidebar.js ADDED
@@ -0,0 +1,80 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class CustomSidebar extends HTMLElement {
2
+ connectedCallback() {
3
+ const active = this.getAttribute('active') || 'dashboard';
4
+ this.attachShadow({ mode: 'open' });
5
+ this.shadowRoot.innerHTML = `
6
+ <style>
7
+ .sidebar {
8
+ width: 240px;
9
+ height: calc(100vh - 60px);
10
+ top: 60px;
11
+ }
12
+ .nav-item {
13
+ transition: all 0.2s ease;
14
+ }
15
+ .nav-item:hover {
16
+ background-color: rgba(59, 130, 246, 0.1);
17
+ }
18
+ .nav-item.active {
19
+ background-color: rgba(59, 130, 246, 0.1);
20
+ border-left: 3px solid rgb(59, 130, 246);
21
+ }
22
+ .nav-item.active .nav-text {
23
+ color: rgb(59, 130, 246);
24
+ font-weight: 500;
25
+ }
26
+ .nav-item.active i {
27
+ color: rgb(59, 130, 246);
28
+ }
29
+ </style>
30
+ <aside class="sidebar bg-white fixed shadow-sm">
31
+ <div class="py-4">
32
+ <div class="px-4 mb-6">
33
+ <div class="text-xs font-semibold text-gray-500 uppercase tracking-wider mb-2">Monitoring</div>
34
+ <a href="index.html" class="nav-item flex items-center px-4 py-2 ${active === 'dashboard' ? 'active' : ''}">
35
+ <i data-feather="activity" class="mr-3"></i>
36
+ <span class="nav-text">Dashboard</span>
37
+ </a>
38
+ <a href="alarms.html" class="nav-item flex items-center px-4 py-2 ${active === 'alarms' ? 'active' : ''}">
39
+ <i data-feather="bell" class="mr-3"></i>
40
+ <span class="nav-text">Alarms</span>
41
+ </a>
42
+ <a href="alerts.html" class="nav-item flex items-center px-4 py-2 ${active === 'alerts' ? 'active' : ''}">
43
+ <i data-feather="alert-triangle" class="mr-3"></i>
44
+ <span class="nav-text">Alerts</span>
45
+ </a>
46
+ </div>
47
+
48
+ <div class="px-4 mb-6">
49
+ <div class="text-xs font-semibold text-gray-500 uppercase tracking-wider mb-2">Management</div>
50
+ <a href="cameras.html" class="nav-item flex items-center px-4 py-2 ${active === 'cameras' ? 'active' : ''}">
51
+ <i data-feather="video" class="mr-3"></i>
52
+ <span class="nav-text">Cameras</span>
53
+ </a>
54
+ <a href="clients.html" class="nav-item flex items-center px-4 py-2 ${active === 'clients' ? 'active' : ''}">
55
+ <i data-feather="users" class="mr-3"></i>
56
+ <span class="nav-text">Clients</span>
57
+ </a>
58
+ <a href="plants.html" class="nav-item flex items-center px-4 py-2 ${active === 'plants' ? 'active' : ''}">
59
+ <i data-feather="map" class="mr-3"></i>
60
+ <span class="nav-text">Plants</span>
61
+ </a>
62
+ </div>
63
+
64
+ <div class="px-4">
65
+ <div class="text-xs font-semibold text-gray-500 uppercase tracking-wider mb-2">Settings</div>
66
+ <a href="settings.html" class="nav-item flex items-center px-4 py-2 ${active === 'settings' ? 'active' : ''}">
67
+ <i data-feather="settings" class="mr-3"></i>
68
+ <span class="nav-text">System Settings</span>
69
+ </a>
70
+ <a href="users.html" class="nav-item flex items-center px-4 py-2 ${active === 'users' ? 'active' : ''}">
71
+ <i data-feather="user" class="mr-3"></i>
72
+ <span class="nav-text">User Management</span>
73
+ </a>
74
+ </div>
75
+ </div>
76
+ </aside>
77
+ `;
78
+ }
79
+ }
80
+ customElements.define('custom-sidebar', CustomSidebar);
components/status-card.js ADDED
@@ -0,0 +1,43 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ class CustomStatusCard extends HTMLElement {
2
+ connectedCallback() {
3
+ const title = this.getAttribute('title') || '';
4
+ const value = this.getAttribute('value') || '0';
5
+ const icon = this.getAttribute('icon') || 'activity';
6
+ const trend = this.getAttribute('trend') || 'same';
7
+ const change = this.getAttribute('change') || '0';
8
+ const color = this.getAttribute('color') || 'blue';
9
+
10
+ this.attachShadow({ mode: 'open' });
11
+ this.shadowRoot.innerHTML = `
12
+ <style>
13
+ .card {
14
+ transition: all 0.3s ease;
15
+ }
16
+ .card:hover {
17
+ transform: translateY(-2px);
18
+ box-shadow: 0 10px 15px -3px rgb(0 0 0 / 0.1), 0 4px 6px -4px rgb(0 0 0 / 0.1);
19
+ }
20
+ </style>
21
+ <div class="card bg-white rounded-xl shadow p-6">
22
+ <div class="flex justify-between items-start">
23
+ <div>
24
+ <p class="text-sm font-medium text-gray-500">${title}</p>
25
+ <h3 class="text-2xl font-bold text-gray-800 mt-1">${value}</h3>
26
+ </div>
27
+ <div class="p-3 rounded-lg bg-${color}-100 text-${color}-600">
28
+ <i data-feather="${icon}"></i>
29
+ </div>
30
+ </div>
31
+ <div class="mt-4 flex items-center text-sm ${trend === 'up' ? 'text-green-600' : trend === 'down' ? 'text-red-600' : 'text-gray-500'}">
32
+ ${trend === 'up' ?
33
+ '<i data-feather="trending-up" class="mr-1"></i>' :
34
+ trend === 'down' ?
35
+ '<i data-feather="trending-down" class="mr-1"></i>' :
36
+ '<i data-feather="minus" class="mr-1"></i>'}
37
+ <span>${change} ${trend === 'same' ? '' : trend === 'up' ? 'increase' : 'decrease'} from yesterday</span>
38
+ </div>
39
+ </div>
40
+ `;
41
+ }
42
+ }
43
+ customElements.define('custom-status-card', CustomStatusCard);
index.html CHANGED
@@ -1,19 +1,140 @@
1
- <!doctype html>
2
- <html>
3
- <head>
4
- <meta charset="utf-8" />
5
- <meta name="viewport" content="width=device-width" />
6
- <title>My static Space</title>
7
- <link rel="stylesheet" href="style.css" />
8
- </head>
9
- <body>
10
- <div class="card">
11
- <h1>Welcome to your static Space!</h1>
12
- <p>You can modify this app directly by editing <i>index.html</i> in the Files and versions tab.</p>
13
- <p>
14
- Also don't forget to check the
15
- <a href="https://huggingface.co/docs/hub/spaces" target="_blank">Spaces documentation</a>.
16
- </p>
17
- </div>
18
- </body>
19
- </html>
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
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.0">
6
+ <title>WatchTower Sentinel | Dashboard</title>
7
+ <link rel="stylesheet" href="style.css">
8
+ <script src="https://cdn.tailwindcss.com"></script>
9
+ <script src="https://cdn.jsdelivr.net/npm/feather-icons/dist/feather.min.js"></script>
10
+ <script src="https://unpkg.com/feather-icons"></script>
11
+ <script src="components/navbar.js"></script>
12
+ <script src="components/sidebar.js"></script>
13
+ <script src="components/status-card.js"></script>
14
+ <script src="components/camera-card.js"></script>
15
+ <script src="components/alert-card.js"></script>
16
+ </head>
17
+ <body class="bg-gray-100">
18
+ <custom-navbar></custom-navbar>
19
+ <div class="flex">
20
+ <custom-sidebar active="dashboard"></custom-sidebar>
21
+
22
+ <main class="flex-1 p-8">
23
+ <h1 class="text-3xl font-bold text-gray-800 mb-8">Monitoring Dashboard</h1>
24
+
25
+ <div class="grid grid-cols-1 md:grid-cols-2 lg:grid-cols-4 gap-6 mb-8">
26
+ <custom-status-card
27
+ title="Active Cameras"
28
+ value="24"
29
+ icon="video"
30
+ trend="up"
31
+ change="3"
32
+ color="blue">
33
+ </custom-status-card>
34
+
35
+ <custom-status-card
36
+ title="Active Alarms"
37
+ value="8"
38
+ icon="bell"
39
+ trend="down"
40
+ change="2"
41
+ color="red">
42
+ </custom-status-card>
43
+
44
+ <custom-status-card
45
+ title="Clients"
46
+ value="42"
47
+ icon="users"
48
+ trend="up"
49
+ change="5"
50
+ color="green">
51
+ </custom-status-card>
52
+
53
+ <custom-status-card
54
+ title="Critical Alerts"
55
+ value="3"
56
+ icon="alert-triangle"
57
+ trend="same"
58
+ change="0"
59
+ color="orange">
60
+ </custom-status-card>
61
+ </div>
62
+
63
+ <div class="grid grid-cols-1 lg:grid-cols-3 gap-6 mb-8">
64
+ <div class="lg:col-span-2 bg-white rounded-xl shadow p-6">
65
+ <div class="flex justify-between items-center mb-6">
66
+ <h2 class="text-xl font-semibold text-gray-800">Live Camera Feed</h2>
67
+ <div class="flex space-x-2">
68
+ <button class="px-3 py-1 bg-blue-100 text-blue-600 rounded-md text-sm">All</button>
69
+ <button class="px-3 py-1 bg-gray-100 text-gray-600 rounded-md text-sm">Critical</button>
70
+ <button class="px-3 py-1 bg-gray-100 text-gray-600 rounded-md text-sm">Offline</button>
71
+ </div>
72
+ </div>
73
+ <div class="grid grid-cols-1 sm:grid-cols-2 gap-4">
74
+ <custom-camera-card
75
+ name="Lobby Camera"
76
+ status="active"
77
+ last-alert="5m ago"
78
+ image="http://static.photos/technology/640x360/1">
79
+ </custom-camera-card>
80
+ <custom-camera-card
81
+ name="Parking Lot"
82
+ status="alert"
83
+ last-alert="2m ago"
84
+ image="http://static.photos/cityscape/640x360/2">
85
+ </custom-camera-card>
86
+ <custom-camera-card
87
+ name="Server Room"
88
+ status="active"
89
+ last-alert="1h ago"
90
+ image="http://static.photos/technology/640x360/3">
91
+ </custom-camera-card>
92
+ <custom-camera-card
93
+ name="Main Entrance"
94
+ status="offline"
95
+ last-alert="30m ago"
96
+ image="http://static.photos/office/640x360/4">
97
+ </custom-camera-card>
98
+ </div>
99
+ </div>
100
+
101
+ <div class="bg-white rounded-xl shadow p-6">
102
+ <h2 class="text-xl font-semibold text-gray-800 mb-6">Recent Alerts</h2>
103
+ <div class="space-y-4">
104
+ <custom-alert-card
105
+ type="motion"
106
+ location="Lobby Camera"
107
+ time="2 minutes ago"
108
+ priority="high">
109
+ </custom-alert-card>
110
+ <custom-alert-card
111
+ type="door"
112
+ location="Back Entrance"
113
+ time="15 minutes ago"
114
+ priority="medium">
115
+ </custom-alert-card>
116
+ <custom-alert-card
117
+ type="temperature"
118
+ location="Server Room"
119
+ time="45 minutes ago"
120
+ priority="low">
121
+ </custom-alert-card>
122
+ <custom-alert-card
123
+ type="power"
124
+ location="Main Building"
125
+ time="1 hour ago"
126
+ priority="critical">
127
+ </custom-alert-card>
128
+ </div>
129
+ </div>
130
+ </div>
131
+ </main>
132
+ </div>
133
+
134
+ <script>
135
+ feather.replace();
136
+ </script>
137
+ <script src="script.js"></script>
138
+ <script src="https://huggingface.co/deepsite/deepsite-badge.js"></script>
139
+ </body>
140
+ </html>
script.js ADDED
@@ -0,0 +1,59 @@
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
1
+ // Global functions and event listeners
2
+ document.addEventListener('DOMContentLoaded', function() {
3
+ // Initialize tooltips
4
+ const tooltipTriggerList = [].slice.call(document.querySelectorAll('[data-bs-toggle="tooltip"]'));
5
+ tooltipTriggerList.map(function (tooltipTriggerEl) {
6
+ return new bootstrap.Tooltip(tooltipTriggerEl);
7
+ });
8
+
9
+ // Handle dark mode toggle if needed
10
+ const darkModeToggle = document.getElementById('darkModeToggle');
11
+ if(darkModeToggle) {
12
+ darkModeToggle.addEventListener('click', function() {
13
+ document.documentElement.classList.toggle('dark');
14
+ });
15
+ }
16
+ });
17
+
18
+ // API functions
19
+ async function fetchAlarms() {
20
+ try {
21
+ const response = await fetch('/api/alarms');
22
+ return await response.json();
23
+ } catch (error) {
24
+ console.error('Error fetching alarms:', error);
25
+ return [];
26
+ }
27
+ }
28
+
29
+ async function fetchCameras() {
30
+ try {
31
+ const response = await fetch('/api/cameras');
32
+ return await response.json();
33
+ } catch (error) {
34
+ console.error('Error fetching cameras:', error);
35
+ return [];
36
+ }
37
+ }
38
+
39
+ // Utility functions
40
+ function formatDateTime(dateString) {
41
+ const options = {
42
+ year: 'numeric',
43
+ month: 'short',
44
+ day: 'numeric',
45
+ hour: '2-digit',
46
+ minute: '2-digit'
47
+ };
48
+ return new Date(dateString).toLocaleDateString('en-US', options);
49
+ }
50
+
51
+ function getPriorityColor(priority) {
52
+ const colors = {
53
+ 'critical': 'red',
54
+ 'high': 'orange',
55
+ 'medium': 'yellow',
56
+ 'low': 'green'
57
+ };
58
+ return colors[priority.toLowerCase()] || 'gray';
59
+ }
style.css CHANGED
@@ -1,28 +1,39 @@
 
 
1
  body {
2
- padding: 2rem;
3
- font-family: -apple-system, BlinkMacSystemFont, "Arial", sans-serif;
 
 
 
 
 
4
  }
5
 
6
- h1 {
7
- font-size: 16px;
8
- margin-top: 0;
9
  }
10
 
11
- p {
12
- color: rgb(107, 114, 128);
13
- font-size: 15px;
14
- margin-bottom: 10px;
15
- margin-top: 5px;
16
  }
17
 
18
- .card {
19
- max-width: 620px;
20
- margin: 0 auto;
21
- padding: 16px;
22
- border: 1px solid lightgray;
23
- border-radius: 16px;
24
  }
25
 
26
- .card p:last-child {
27
- margin-bottom: 0;
 
 
 
 
 
 
28
  }
 
 
 
 
 
1
+ @import url('https://fonts.googleapis.com/css2?family=Inter:wght@300;400;500;600;700&display=swap');
2
+
3
  body {
4
+ font-family: 'Inter', sans-serif;
5
+ }
6
+
7
+ /* Custom scrollbar */
8
+ ::-webkit-scrollbar {
9
+ width: 8px;
10
+ height: 8px;
11
  }
12
 
13
+ ::-webkit-scrollbar-track {
14
+ background: #f1f1f1;
15
+ border-radius: 10px;
16
  }
17
 
18
+ ::-webkit-scrollbar-thumb {
19
+ background: #888;
20
+ border-radius: 10px;
 
 
21
  }
22
 
23
+ ::-webkit-scrollbar-thumb:hover {
24
+ background: #555;
 
 
 
 
25
  }
26
 
27
+ /* Animation for alerts */
28
+ @keyframes pulse {
29
+ 0%, 100% {
30
+ opacity: 1;
31
+ }
32
+ 50% {
33
+ opacity: 0.5;
34
+ }
35
  }
36
+
37
+ .alert-pulse {
38
+ animation: pulse 2s infinite;
39
+ }