@necrolab/dashboard 0.5.12 → 0.5.14

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 (54) hide show
  1. package/.playwright-mcp/verification-accounts-desktop.png +0 -0
  2. package/.playwright-mcp/verification-tasks-desktop.png +0 -0
  3. package/.playwright-mcp/verification-tasks-mobile.png +0 -0
  4. package/README.md +21 -0
  5. package/docs/plans/2026-02-08-tailwind-consolidation-results.md +476 -0
  6. package/docs/plans/2026-02-08-tailwind-consolidation.md +2416 -0
  7. package/package.json +1 -1
  8. package/src/App.vue +2 -163
  9. package/src/assets/css/components/buttons.scss +43 -95
  10. package/src/assets/css/components/forms.scss +10 -28
  11. package/src/assets/css/components/search-groups.scss +80 -0
  12. package/src/assets/css/components/tables.scss +0 -8
  13. package/src/assets/css/main.scss +2 -43
  14. package/src/components/Editors/Account/Account.vue +14 -220
  15. package/src/components/Editors/Account/AccountCreator.vue +0 -4
  16. package/src/components/Editors/Account/AccountView.vue +0 -1
  17. package/src/components/Editors/Account/CreateAccount.vue +36 -107
  18. package/src/components/Editors/Profile/CreateProfile.vue +46 -135
  19. package/src/components/Editors/Profile/Profile.vue +10 -213
  20. package/src/components/Editors/Profile/ProfileView.vue +0 -1
  21. package/src/components/Filter/Filter.vue +14 -17
  22. package/src/components/Filter/FilterPreview.vue +0 -6
  23. package/src/components/Table/Row.vue +1 -1
  24. package/src/components/Table/Table.vue +2 -16
  25. package/src/components/Tasks/CreateTaskAXS.vue +45 -104
  26. package/src/components/Tasks/CreateTaskTM.vue +58 -96
  27. package/src/components/Tasks/Task.vue +22 -209
  28. package/src/components/Tasks/TaskView.vue +5 -4
  29. package/src/components/Tasks/ViewTask.vue +201 -214
  30. package/src/components/icons/Copy.vue +6 -0
  31. package/src/components/icons/index.js +3 -1
  32. package/src/components/ui/ActionButtonGroup.vue +70 -0
  33. package/src/components/ui/FormField.vue +74 -0
  34. package/src/components/ui/InfoRow.vue +100 -0
  35. package/src/components/ui/Modal.vue +6 -47
  36. package/src/components/ui/Navbar.vue +15 -43
  37. package/src/components/ui/ReconnectIndicator.vue +4 -4
  38. package/src/components/ui/SectionCard.vue +24 -0
  39. package/src/components/ui/Splash.vue +1 -6
  40. package/src/components/ui/StatusBadge.vue +37 -0
  41. package/src/components/ui/controls/CountryChooser.vue +14 -58
  42. package/src/components/ui/controls/atomic/Dropdown.vue +16 -24
  43. package/src/components/ui/controls/atomic/MultiDropdown.vue +7 -1
  44. package/src/components/ui/controls/atomic/Switch.vue +13 -29
  45. package/src/composables/useCopyToClipboard.js +25 -0
  46. package/src/composables/useRowSelection.js +48 -0
  47. package/src/views/Accounts.vue +0 -81
  48. package/src/views/Console.vue +4 -21
  49. package/src/views/Editor.vue +48 -138
  50. package/src/views/FilterBuilder.vue +0 -23
  51. package/src/views/Login.vue +14 -63
  52. package/src/views/Profiles.vue +0 -82
  53. package/src/views/Tasks.vue +3 -24
  54. package/tailwind.config.js +47 -5
@@ -0,0 +1,74 @@
1
+ <template>
2
+ <div class="input-wrapper" :class="{ 'relative-positioned': zIndex, [`z-${zIndex}`]: zIndex }">
3
+ <slot name="label">
4
+ <label v-if="label" class="label-override mb-2">
5
+ {{ label }}
6
+ <component v-if="icon" :is="icon" />
7
+ </label>
8
+ </slot>
9
+ <div :class="`input-default ${required ? 'required' : ''} ${error ? 'error' : ''}`">
10
+ <slot />
11
+ <div v-if="incrementer" class="input-incrementer">
12
+ <button @click="$emit('increment')" type="button">
13
+ <UpIcon />
14
+ </button>
15
+ <button @click="$emit('decrement')" type="button">
16
+ <DownIcon />
17
+ </button>
18
+ </div>
19
+ </div>
20
+ </div>
21
+ </template>
22
+
23
+ <script setup>
24
+ import { UpIcon, DownIcon } from '@/components/icons';
25
+
26
+ defineProps({
27
+ label: { type: String, default: '' },
28
+ icon: { type: Object, default: null },
29
+ required: { type: Boolean, default: false },
30
+ error: { type: Boolean, default: false },
31
+ incrementer: { type: Boolean, default: false },
32
+ zIndex: { type: String, default: '' }
33
+ });
34
+
35
+ defineEmits(['increment', 'decrement']);
36
+ </script>
37
+
38
+ <style lang="scss" scoped>
39
+ .input-wrapper {
40
+ label {
41
+ @apply flex items-center;
42
+ }
43
+ }
44
+
45
+ .relative-positioned {
46
+ @apply relative;
47
+ }
48
+
49
+ .z-0 {
50
+ z-index: 0;
51
+ }
52
+
53
+ .z-1 {
54
+ z-index: 1;
55
+ }
56
+
57
+ .z-2 {
58
+ z-index: 2;
59
+ }
60
+
61
+ .z-dropdown {
62
+ z-index: 100;
63
+ }
64
+
65
+ .z-tooltip {
66
+ z-index: 2000;
67
+ }
68
+
69
+ .error {
70
+ @apply border-2 border-error-300;
71
+ }
72
+
73
+ // Incrementer styles handled by forms.scss
74
+ </style>
@@ -0,0 +1,100 @@
1
+ <template>
2
+ <div
3
+ class="info-row"
4
+ :class="{ 'items-start': alignStart, [customClass]: customClass }">
5
+ <component v-if="icon" :is="icon" class="info-icon" :class="{ 'flex-shrink-0': alignStart }" />
6
+ <span v-if="iconText" class="info-icon flex items-center justify-center text-base font-bold" :class="{ 'flex-shrink-0': alignStart }">
7
+ {{ iconText }}
8
+ </span>
9
+ <span class="info-label" :class="{ 'flex-shrink-0': alignStart }">{{ label }}</span>
10
+ <span class="info-value" :class="valueClass">
11
+ <slot>{{ value }}</slot>
12
+ </span>
13
+ <button
14
+ v-if="copyable"
15
+ @click="handleCopy"
16
+ class="copy-button"
17
+ :class="{ 'copied': showCopied }">
18
+ <component :is="showCopied ? CheckIcon : CopyIcon" class="w-4 h-4" />
19
+ </button>
20
+ <slot name="action" />
21
+ </div>
22
+ </template>
23
+
24
+ <script setup>
25
+ import { ref } from 'vue';
26
+ import { CopyIcon, CheckIcon } from '@/components/icons';
27
+
28
+ const props = defineProps({
29
+ icon: { type: Object, default: null },
30
+ iconText: { type: String, default: '' },
31
+ label: { type: String, required: true },
32
+ value: { type: String, default: '' },
33
+ copyable: { type: Boolean, default: false },
34
+ copyValue: { type: String, default: '' },
35
+ copyMessage: { type: String, default: 'Copied' },
36
+ customClass: { type: String, default: '' },
37
+ valueClass: { type: String, default: '' },
38
+ alignStart: { type: Boolean, default: false }
39
+ });
40
+
41
+ const showCopied = ref(false);
42
+
43
+ const handleCopy = () => {
44
+ const textToCopy = props.copyValue || props.value;
45
+ navigator.clipboard.writeText(textToCopy);
46
+
47
+ // Show checkmark for 2 seconds
48
+ showCopied.value = true;
49
+ setTimeout(() => {
50
+ showCopied.value = false;
51
+ }, 2000);
52
+ };
53
+ </script>
54
+
55
+ <style lang="scss" scoped>
56
+ .info-row {
57
+ @apply flex items-center gap-3 px-3 py-2 rounded-lg;
58
+ @apply bg-dark-500 border border-dark-600;
59
+ @apply min-h-10;
60
+ }
61
+
62
+ .info-icon {
63
+ @apply w-4 h-4 flex-shrink-0 text-light-300;
64
+
65
+ svg {
66
+ @apply text-light-300 w-4 h-4;
67
+ }
68
+ }
69
+
70
+ .info-label {
71
+ @apply text-xs font-medium flex-shrink-0 text-light-500;
72
+ @apply min-w-[100px];
73
+
74
+ @media (max-width: 768px) {
75
+ @apply min-w-20 text-[11px];
76
+ }
77
+ }
78
+
79
+ .info-value {
80
+ @apply text-sm flex-1 text-light-300;
81
+ @apply break-words overflow-hidden;
82
+ min-width: 0; // Allow flex item to shrink below content size
83
+
84
+ @media (max-width: 768px) {
85
+ @apply text-[13px];
86
+ }
87
+ }
88
+
89
+ .copy-button {
90
+ @apply flex items-center justify-center;
91
+ @apply w-8 h-8 rounded transition-all duration-150;
92
+ @apply bg-transparent text-light-500 hover:text-light-300 hover:bg-dark-600;
93
+ @apply flex-shrink-0;
94
+ @apply ml-2; // Ensure spacing from value
95
+
96
+ &.copied {
97
+ @apply text-accent-green;
98
+ }
99
+ }
100
+ </style>
@@ -1,6 +1,6 @@
1
1
  <template>
2
- <div class="modal-mask scrollable overflow-y-auto" role="dialog" @touchmove.stop>
3
- <div class="component-modal" ref="target">
2
+ <div class="modal-mask fixed inset-0 z-modal bg-overlay-dark backdrop-blur-xs flex pt-14 scrollable overflow-y-auto" role="dialog" @touchmove.stop>
3
+ <div class="component-modal mb-8 mobile-portrait:mb-16" ref="target">
4
4
  <div class="modal-header">
5
5
  <slot name="header" />
6
6
  <button @click="ui.toggleModal()" class="btn-icon border-none hover:bg-dark-400">
@@ -56,14 +56,10 @@ onClickOutside(target, (event) => {
56
56
  </script>
57
57
  <style lang="scss" scoped>
58
58
  .modal-mask {
59
- @apply fixed left-0 top-0 flex w-screen duration-300 ease-in-out;
60
- z-index: 25000;
59
+ @apply w-screen duration-300 ease-in-out;
61
60
  align-items: flex-start;
62
61
  justify-content: center;
63
62
  padding: 1rem;
64
- padding-top: 3.5rem;
65
- background-color: rgba(17, 17, 17, 0.85);
66
- backdrop-filter: blur(4px);
67
63
  height: 100dvh;
68
64
  overflow-y: auto;
69
65
  -webkit-overflow-scrolling: touch;
@@ -76,14 +72,9 @@ onClickOutside(target, (event) => {
76
72
 
77
73
  .component-modal {
78
74
  width: 640px;
79
- margin-bottom: 20rem;
80
75
  overflow-y: visible;
81
76
  @apply flex flex-col rounded-lg bg-dark-300 px-5 py-5;
82
77
 
83
- @media (max-width: 480px) {
84
- margin-bottom: 25rem;
85
- }
86
-
87
78
  .modal-header {
88
79
  @apply flex font-bold text-white;
89
80
 
@@ -93,7 +84,7 @@ onClickOutside(target, (event) => {
93
84
  }
94
85
 
95
86
  .modal-body {
96
- @apply flex flex-col;
87
+ @apply flex flex-col pb-6 md:pb-4;
97
88
  flex: 1;
98
89
  }
99
90
  }
@@ -104,16 +95,14 @@ onClickOutside(target, (event) => {
104
95
  justify-content: center;
105
96
  padding: 1rem;
106
97
  padding-top: 3rem;
107
- padding-bottom: 15rem !important;
108
98
  }
109
99
 
110
100
  .component-modal {
111
101
  width: calc(100vw - 2rem);
112
- margin-bottom: 10rem;
102
+ margin-bottom: 3rem;
113
103
  }
114
104
 
115
105
  .modal-body {
116
- padding-bottom: 4rem;
117
106
  overflow-y: visible;
118
107
  flex: 1;
119
108
  min-height: 0;
@@ -122,38 +111,8 @@ onClickOutside(target, (event) => {
122
111
 
123
112
  /* iPhone portrait mode - extra spacing for create button */
124
113
  @media (max-width: 480px) and (orientation: portrait) {
125
- .modal-mask {
126
- padding-bottom: 3rem !important;
127
- }
128
-
129
114
  .component-modal {
130
- margin-bottom: 3rem !important;
131
- max-height: none !important;
132
- }
133
-
134
- .modal-body {
135
- padding-bottom: 1rem !important;
115
+ max-height: none;
136
116
  }
137
117
  }
138
-
139
- // iPhone landscape mode - proper modal centering with consistent spacing
140
- // @media (max-height: 500px) and (orientation: landscape) {
141
- // .modal-mask {
142
- // padding: 1rem 1rem 4rem 1rem !important;
143
- // align-items: center !important;
144
- // justify-content: center !important;
145
- // padding-bottom: 3rem !important; // Extra padding for create button on iPhone
146
- // }
147
-
148
- // .component-modal {
149
- // max-height: calc(100vh - 10rem) !important;
150
- // overflow-y: auto !important;
151
- // margin: 0 !important;
152
- // margin-bottom: 30px;
153
- // }
154
-
155
- // .modal-body {
156
- // padding-bottom: 1rem !important;
157
- // }
158
- // }
159
118
  </style>
@@ -1,6 +1,6 @@
1
1
  <template>
2
- <div class="navbar" :class="{ 'force-z': menuOpen }">
3
- <div :class="['component-container ios-wrapper flex items-center relative', { 'ios-wrapper': landscapeIos }]">
2
+ <div class="navbar" :class="{ 'z-max': menuOpen }">
3
+ <div :class="['component-container ios-wrapper flex items-center relative px-2 sm:px-3 lg:px-4', { 'ios-wrapper': landscapeIos }]">
4
4
  <router-link to="/">
5
5
  <img src="@/assets/img/logo_trans.png" class="h-6 lg:h-8 mr-4 z-30 object-cover cursor-pointer" alt="Logo: Necro" />
6
6
  </router-link>
@@ -111,14 +111,10 @@
111
111
  </template>
112
112
  <style lang="scss" scoped>
113
113
  .navbar {
114
- @apply border-b py-5 fixed w-full;
114
+ @apply border-b py-5 fixed w-full bg-dark-300/95 backdrop-blur border-dark-600;
115
115
  top: 0;
116
116
  left: 0;
117
117
  z-index: 1000;
118
- background: oklch(0.1822 0 0 / 0.95);
119
- backdrop-filter: blur(23px);
120
- -webkit-backdrop-filter: blur(23px);
121
- border-color: oklch(0.26 0 0);
122
118
 
123
119
  // Consistent padding base
124
120
  padding-top: 1.25rem;
@@ -148,25 +144,14 @@
148
144
  ul {
149
145
  @apply gap-x-4;
150
146
 
151
- // Global SVG normalization - force all icons to be identical
152
- svg {
153
- width: 20px !important;
154
- height: 20px !important;
155
- display: block !important;
156
- flex-shrink: 0 !important;
157
- box-sizing: border-box !important;
158
- }
159
-
160
147
  li a {
161
- @apply flex text-white text-sm items-center rounded-lg;
162
- height: 40px;
163
- border: 1px solid transparent;
148
+ @apply flex text-white text-sm items-center rounded-lg h-10 border-b-2 border-transparent;
164
149
  border-left: 3px solid transparent;
165
150
  transition: all 0.25s cubic-bezier(0.4, 0, 0.2, 1);
166
151
  position: relative;
167
152
 
168
153
  svg {
169
- @apply mr-2;
154
+ @apply mr-2 w-5 h-5;
170
155
  }
171
156
 
172
157
  &.router-link-exact-active {
@@ -190,35 +175,26 @@
190
175
  // Desktop mode (lg to xl): icon-only view with perfect centering
191
176
  @media (min-width: 1024px) and (max-width: 1279px) {
192
177
  li a {
193
- width: 40px;
194
- height: 40px;
195
- padding: 0 !important;
196
- display: flex !important;
197
- align-items: center !important;
198
- justify-content: center !important;
178
+ @apply w-10 h-10 flex items-center justify-center;
179
+ padding: 0;
199
180
  position: relative;
200
181
 
201
182
  // Hide text completely in icon-only mode
202
183
  span {
203
- display: none !important;
184
+ display: none;
204
185
  }
205
186
 
206
187
  svg {
207
- margin: 0 !important;
208
- padding: 0 !important;
209
- position: absolute;
210
- top: 50%;
211
- left: 50%;
188
+ @apply m-0 absolute top-1/2 left-1/2;
212
189
  transform: translate(-50%, -50%);
190
+ padding: 0;
213
191
  }
214
192
 
215
193
  &.router-link-exact-active {
216
- border: 2px solid oklch(0.72 0.15 145) !important;
217
- color: oklch(0.72 0.15 145) !important;
218
- background: oklch(0.72 0.15 145 / 0.08) !important;
219
- border-radius: 0.5rem !important;
220
- width: 40px !important;
221
- height: 40px !important;
194
+ @apply border-2 w-10 h-10 rounded-lg;
195
+ border-color: oklch(0.72 0.15 145);
196
+ color: oklch(0.72 0.15 145);
197
+ background: oklch(0.72 0.15 145 / 0.08);
222
198
  }
223
199
  }
224
200
  }
@@ -226,8 +202,7 @@
226
202
  // XL and above: full width with text
227
203
  @media (min-width: 1280px) {
228
204
  li a {
229
- @apply px-4;
230
- height: 40px;
205
+ @apply px-4 h-10;
231
206
 
232
207
  svg {
233
208
  @apply mr-2;
@@ -237,9 +212,6 @@
237
212
  }
238
213
  }
239
214
 
240
- .force-z {
241
- z-index: 20000;
242
- }
243
215
 
244
216
  .mobile-menu {
245
217
  margin-top: 0;
@@ -166,7 +166,7 @@ const props = defineProps({
166
166
  width: 160px;
167
167
  height: 160px;
168
168
  border-top: 4px solid oklch(0.78 0.12 145);
169
- border-right: 4px solid rgba(157, 211, 168, 0.3);
169
+ @apply border-r-4 border-r-accent-green/30;
170
170
  border-bottom: 4px solid transparent;
171
171
  border-left: 4px solid transparent;
172
172
  animation: breathe-slow 3s ease-in-out infinite;
@@ -178,7 +178,7 @@ const props = defineProps({
178
178
  width: 120px;
179
179
  height: 120px;
180
180
  border-top: 4px solid oklch(0.72 0.15 145);
181
- border-right: 4px solid rgba(136, 201, 153, 0.4);
181
+ @apply border-r-4 border-r-accent-green/40;
182
182
  border-bottom: 4px solid transparent;
183
183
  border-left: 4px solid transparent;
184
184
  animation: breathe-medium 2.5s ease-in-out infinite reverse;
@@ -190,7 +190,7 @@ const props = defineProps({
190
190
  width: 80px;
191
191
  height: 80px;
192
192
  border-top: 4px solid oklch(0.68 0.15 145);
193
- border-right: 4px solid rgba(123, 193, 135, 0.5);
193
+ @apply border-r-4 border-r-accent-green/50;
194
194
  border-bottom: 4px solid transparent;
195
195
  border-left: 4px solid transparent;
196
196
  animation: breathe-fast 2s ease-in-out infinite;
@@ -204,7 +204,7 @@ const props = defineProps({
204
204
  border-radius: 50%;
205
205
  object-fit: cover;
206
206
  z-index: 10;
207
- border: 2px solid rgba(136, 201, 153, 0.3);
207
+ @apply border-2 border-accent-green/30;
208
208
  box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.1);
209
209
  }
210
210
 
@@ -0,0 +1,24 @@
1
+ <template>
2
+ <div class="section-card">
3
+ <h3 v-if="title" class="section-title">{{ title }}</h3>
4
+ <slot />
5
+ </div>
6
+ </template>
7
+
8
+ <script setup>
9
+ defineProps({
10
+ title: { type: String, default: '' }
11
+ });
12
+ </script>
13
+
14
+ <style lang="scss" scoped>
15
+ .section-card {
16
+ @apply rounded-lg border p-4;
17
+ @apply bg-dark-400 border-dark-600;
18
+ }
19
+
20
+ .section-title {
21
+ @apply text-sm font-semibold mb-3;
22
+ @apply text-light-300;
23
+ }
24
+ </style>
@@ -1,5 +1,5 @@
1
1
  <template>
2
- <div class="darkBG h-screen w-screen z-[1000] fixed flex items-center flex-col justify-center gap-y-3">
2
+ <div class="bg-dark-300 h-screen w-screen z-[1000] fixed flex items-center flex-col justify-center gap-y-3">
3
3
  <img src="@/assets/img/logo_trans.png" class="w-[300px] mx-auto" />
4
4
  <SpinnerIcon />
5
5
  </div>
@@ -8,11 +8,6 @@
8
8
  <script setup>
9
9
  import { SpinnerIcon } from "@/components/icons";
10
10
  </script>
11
- <style lang="scss" scoped>
12
- .darkBG {
13
- background-color: rgba(28, 28, 49, 100%);
14
- }
15
- </style>
16
11
 
17
12
  <style>
18
13
  img {
@@ -0,0 +1,37 @@
1
+ <script setup>
2
+ import { defineProps } from 'vue'
3
+ import { CheckmarkIcon, CloseXIcon } from '@/components/icons'
4
+
5
+ const props = defineProps({
6
+ enabled: {
7
+ type: Boolean,
8
+ required: true
9
+ },
10
+ size: {
11
+ type: String,
12
+ default: 'small',
13
+ validator: (value) => ['small', 'large'].includes(value)
14
+ }
15
+ })
16
+
17
+ const sizeClasses = {
18
+ small: 'w-6 h-6',
19
+ large: 'w-[26px] h-[26px]'
20
+ }
21
+
22
+ const enabledClasses = 'bg-accent-green/20 border-accent-green/30'
23
+ const disabledClasses = 'bg-red-500/20 border-red-500/30'
24
+ </script>
25
+
26
+ <template>
27
+ <div
28
+ :class="[
29
+ 'flex items-center justify-center rounded-full border-2 transition-all duration-200',
30
+ enabled ? enabledClasses : disabledClasses,
31
+ sizeClasses[size]
32
+ ]"
33
+ >
34
+ <CheckmarkIcon v-if="enabled" class="w-3 h-3 text-accent-green" />
35
+ <CloseXIcon v-else class="w-3 h-3 text-red-500" />
36
+ </div>
37
+ </template>
@@ -113,24 +113,16 @@ const selectCountry = (country, module) => {
113
113
  }
114
114
 
115
115
  .small-dropdown {
116
- background-clip: border-box !important;
117
- border-radius: 100% !important;
116
+ @apply bg-clip-border rounded-full w-12 h-12 flex justify-center items-center;
118
117
  padding: 0;
119
- width: 3em !important;
120
- height: 3em !important;
121
- display: flex;
122
- justify-items: center;
123
- justify-content: center;
124
118
  }
125
119
 
126
120
  .dropdown-content-portal {
127
121
  @apply bg-dark-400 border border-dark-650 rounded-lg shadow-2xl z-50;
122
+ @apply max-h-52 overflow-y-auto touch-pan-y;
128
123
  padding: 0.5rem;
129
- max-height: 208px !important;
130
- overflow-y: auto !important;
131
- overscroll-behavior: contain !important;
132
- touch-action: pan-y !important;
133
- -webkit-overflow-scrolling: touch !important;
124
+ overscroll-behavior: contain;
125
+ -webkit-overflow-scrolling: touch;
134
126
  scrollbar-width: none;
135
127
  -ms-overflow-style: none;
136
128
  min-width: 100px;
@@ -142,32 +134,24 @@ const selectCountry = (country, module) => {
142
134
  }
143
135
 
144
136
  .header-item {
145
- text-align: center !important;
146
- font-size: 0.75rem;
147
- font-weight: 700;
148
- color: white;
149
- padding: 8px 4px;
150
- cursor: default;
151
- pointer-events: none;
152
- margin: 4px 2px;
153
- position: relative;
137
+ @apply text-center text-xs font-bold text-white;
138
+ @apply py-2 px-1 mx-0.5 my-1 rounded-md;
139
+ @apply flex items-center justify-center;
140
+ @apply pointer-events-none cursor-default;
141
+ @apply relative;
154
142
  background: linear-gradient(
155
143
  135deg,
156
144
  rgba(255, 255, 255, 0.08) 0%,
157
145
  rgba(255, 255, 255, 0.04) 100%
158
146
  );
159
147
  border: 1px solid rgba(255, 255, 255, 0.1);
160
- border-radius: 6px;
161
148
  letter-spacing: 0.5px;
162
149
  text-shadow: 0 1px 2px rgba(0, 0, 0, 0.3);
163
150
  box-shadow: 0 2px 4px rgba(0, 0, 0, 0.1);
164
- display: flex;
165
- align-items: center;
166
- justify-content: center;
167
151
  }
168
152
 
169
153
  .header-item:first-child {
170
- margin-top: 2px;
154
+ @apply mt-0.5;
171
155
  }
172
156
 
173
157
  .header-item::before {
@@ -187,39 +171,11 @@ const selectCountry = (country, module) => {
187
171
  }
188
172
 
189
173
  .country-item {
190
- padding: 8px 4px;
191
- font-size: 0.875rem;
192
- color: white;
193
- cursor: pointer;
194
- border-radius: 6px;
195
- margin: 1px 2px;
174
+ @apply py-2 px-1 mx-0.5 my-px text-sm text-white;
175
+ @apply cursor-pointer rounded-md min-h-8;
176
+ @apply flex items-center justify-center;
196
177
  transition: all 0.15s ease;
197
- display: flex;
198
- align-items: center;
199
- justify-content: center;
200
- min-height: 32px;
201
178
  }
202
179
 
203
- .country-item .flex {
204
- display: flex;
205
- align-items: center;
206
- justify-content: center;
207
- gap: 6px;
208
- width: 100%;
209
- flex-wrap: nowrap;
210
- }
211
-
212
- .country-item .flex span {
213
- font-size: 0.75rem;
214
- font-weight: 500;
215
- white-space: nowrap;
216
- flex-shrink: 0;
217
- text-align: center;
218
- }
219
-
220
- .country-item .flex img {
221
- width: 18px;
222
- height: 14px;
223
- flex-shrink: 0;
224
- }
180
+ /* Flex layout already defined in template with Tailwind classes */
225
181
  </style>