@el7ven/cookie-kit 0.2.20 → 0.3.1
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.
- package/package.json +4 -2
- package/src/core/analytics.js +311 -0
- package/src/core/index.js +7 -7
- package/src/index.js +5 -0
- package/src/js/CookieConsent.js +0 -1
- package/src/vue/CookieConsent.vue +165 -62
- package/src/vue/CookieConsentDebug.vue +168 -0
- package/src/vue/CookieDrawer.vue +274 -104
- package/src/vue/index.js +5 -1
|
@@ -1,13 +1,12 @@
|
|
|
1
1
|
<template>
|
|
2
2
|
<div class="cookie-consent" :data-cookie-kit-theme="theme" :style="themeVarsStyle">
|
|
3
|
-
<!-- Cookie Drawer (Unified Component) -->
|
|
4
3
|
<CookieDrawer
|
|
5
4
|
ref="drawerRef"
|
|
6
5
|
:is-visible="isVisible"
|
|
7
6
|
:is-settings-mode="isSettingsMode"
|
|
8
7
|
:current-tab="currentTab"
|
|
9
8
|
:categories="categories"
|
|
10
|
-
:config="
|
|
9
|
+
:config="mergedConfig"
|
|
11
10
|
:consent-version="consentVersion"
|
|
12
11
|
:capabilities="capabilities"
|
|
13
12
|
:is-v2="isV2"
|
|
@@ -23,88 +22,129 @@
|
|
|
23
22
|
</template>
|
|
24
23
|
|
|
25
24
|
<script setup>
|
|
26
|
-
import { computed, nextTick, onUnmounted, ref, watch } from 'vue'
|
|
25
|
+
import { computed, nextTick, onMounted, onUnmounted, ref, watch } from 'vue'
|
|
27
26
|
import { useCookieKitVue, DEFAULT_CONFIG } from './index.js'
|
|
28
27
|
import CookieDrawer from './CookieDrawer.vue'
|
|
29
28
|
|
|
30
|
-
// Props for customization
|
|
31
29
|
const props = defineProps({
|
|
32
30
|
config: {
|
|
33
31
|
type: Object,
|
|
34
|
-
default: () =>
|
|
32
|
+
default: () => ({})
|
|
35
33
|
}
|
|
36
34
|
})
|
|
37
35
|
|
|
38
|
-
|
|
36
|
+
const emit = defineEmits(['consentChanged', 'consentCleared'])
|
|
37
|
+
|
|
38
|
+
// Deep merge user config with defaults
|
|
39
|
+
const mergedConfig = computed(() => deepMerge(DEFAULT_CONFIG, props.config))
|
|
40
|
+
|
|
41
|
+
// Use the Vue composable with merged config
|
|
39
42
|
const {
|
|
43
|
+
core,
|
|
40
44
|
consent,
|
|
45
|
+
categories: coreCategories,
|
|
41
46
|
acceptAll,
|
|
42
47
|
rejectAll,
|
|
43
48
|
acceptSelected,
|
|
44
49
|
hasConsented,
|
|
45
|
-
hasCategoryConsent
|
|
46
|
-
|
|
50
|
+
hasCategoryConsent,
|
|
51
|
+
resetConsent
|
|
52
|
+
} = useCookieKitVue(mergedConfig.value)
|
|
47
53
|
|
|
48
|
-
// Local state
|
|
49
|
-
const isVisible =
|
|
54
|
+
// Local UI state
|
|
55
|
+
const isVisible = ref(false)
|
|
50
56
|
const isSettingsMode = ref(false)
|
|
51
57
|
const currentTab = ref('privacy')
|
|
52
|
-
const
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
const
|
|
56
|
-
|
|
58
|
+
const drawerRef = ref(null)
|
|
59
|
+
|
|
60
|
+
// Category state (reactive, for toggles)
|
|
61
|
+
const categories = ref(buildCategoryState(mergedConfig.value.categories))
|
|
62
|
+
|
|
63
|
+
// Computed
|
|
64
|
+
const consentVersion = computed(() => mergedConfig.value.version || 'v2')
|
|
65
|
+
const capabilities = computed(() => mergedConfig.value.capabilities || {})
|
|
66
|
+
const isV2 = computed(() => consentVersion.value === 'v2')
|
|
67
|
+
const theme = computed(() => mergedConfig.value.theme || 'light')
|
|
57
68
|
const themeVarsStyle = computed(() => {
|
|
58
|
-
const vars =
|
|
69
|
+
const vars = mergedConfig.value.themeVars || {}
|
|
59
70
|
return Object.fromEntries(
|
|
60
|
-
Object.entries(vars).map(([
|
|
71
|
+
Object.entries(vars).map(([k, v]) => [k.startsWith('--') ? k : `--${k}`, v])
|
|
61
72
|
)
|
|
62
73
|
})
|
|
63
|
-
const drawerRef = ref(null)
|
|
64
74
|
|
|
65
75
|
const enabledSettingsTabs = computed(() => {
|
|
66
|
-
return Object.keys(
|
|
67
|
-
|
|
68
|
-
|
|
76
|
+
return Object.keys(mergedConfig.value.categories || {}).filter(id =>
|
|
77
|
+
mergedConfig.value.categories[id]?.enabled
|
|
78
|
+
)
|
|
69
79
|
})
|
|
70
80
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
const closeSettings = () => {
|
|
76
|
-
if (consent.value?.hasConsented) {
|
|
77
|
-
return
|
|
81
|
+
// Show/hide logic
|
|
82
|
+
onMounted(() => {
|
|
83
|
+
if (!hasConsented.value && mergedConfig.value.autoShow !== false) {
|
|
84
|
+
isVisible.value = true
|
|
78
85
|
}
|
|
79
|
-
isSettingsMode.value = false
|
|
80
|
-
}
|
|
81
86
|
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
|
|
87
|
+
// Listen for consent events from core
|
|
88
|
+
if (core) {
|
|
89
|
+
core.on('consentChanged', (data) => {
|
|
90
|
+
emit('consentChanged', data)
|
|
91
|
+
})
|
|
92
|
+
core.on('consentCleared', () => {
|
|
93
|
+
isVisible.value = true
|
|
94
|
+
emit('consentCleared')
|
|
95
|
+
})
|
|
96
|
+
}
|
|
89
97
|
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
98
|
+
// Expose global API
|
|
99
|
+
if (typeof window !== 'undefined' && mergedConfig.value.mountGlobal !== false) {
|
|
100
|
+
window.CookieConsent = {
|
|
101
|
+
hasConsent: () => hasConsented.value,
|
|
102
|
+
hasCategoryConsent: (cat) => hasCategoryConsent(cat),
|
|
103
|
+
getConsent: () => consent.value,
|
|
104
|
+
acceptAll: () => handleAcceptAll(),
|
|
105
|
+
rejectAll: () => handleRejectAll(),
|
|
106
|
+
acceptSelected: (ids) => acceptSelected(ids),
|
|
107
|
+
clearConsent: () => { resetConsent(); isVisible.value = true },
|
|
108
|
+
show: () => { isVisible.value = true },
|
|
109
|
+
showSettings: () => { isSettingsMode.value = true; isVisible.value = true },
|
|
110
|
+
on: (event, cb) => core?.on(event, cb),
|
|
111
|
+
off: (event, cb) => core?.off(event, cb)
|
|
112
|
+
}
|
|
113
|
+
}
|
|
114
|
+
})
|
|
93
115
|
|
|
116
|
+
// Focus trap
|
|
94
117
|
const getDrawerDialogElement = () => {
|
|
95
118
|
const exposed = drawerRef.value?.dialogElement
|
|
96
|
-
|
|
97
|
-
return result
|
|
119
|
+
return typeof exposed === 'function' ? exposed() : exposed?.value || exposed || null
|
|
98
120
|
}
|
|
99
121
|
|
|
100
|
-
|
|
122
|
+
let previousActiveElement = null
|
|
123
|
+
|
|
101
124
|
watch(isVisible, async (visible) => {
|
|
102
125
|
if (visible) {
|
|
126
|
+
previousActiveElement = document.activeElement
|
|
127
|
+
await nextTick()
|
|
128
|
+
await new Promise(resolve => setTimeout(resolve, 50))
|
|
129
|
+
const el = getDrawerDialogElement()
|
|
130
|
+
if (el) {
|
|
131
|
+
const focusable = el.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])')
|
|
132
|
+
if (focusable.length) focusable[0].focus()
|
|
133
|
+
}
|
|
134
|
+
} else {
|
|
135
|
+
if (previousActiveElement?.focus) previousActiveElement.focus()
|
|
136
|
+
previousActiveElement = null
|
|
137
|
+
}
|
|
138
|
+
})
|
|
139
|
+
|
|
140
|
+
watch([isSettingsMode, currentTab], async () => {
|
|
141
|
+
if (isVisible.value) {
|
|
103
142
|
await nextTick()
|
|
104
143
|
await new Promise(resolve => setTimeout(resolve, 50))
|
|
105
|
-
const
|
|
106
|
-
if (
|
|
107
|
-
|
|
144
|
+
const el = getDrawerDialogElement()
|
|
145
|
+
if (el) {
|
|
146
|
+
const focusable = el.querySelectorAll('button, [href], input, select, textarea, [tabindex]:not([tabindex="-1"])')
|
|
147
|
+
if (focusable.length) focusable[0].focus()
|
|
108
148
|
}
|
|
109
149
|
}
|
|
110
150
|
})
|
|
@@ -112,58 +152,121 @@ watch(isVisible, async (visible) => {
|
|
|
112
152
|
// Event handlers
|
|
113
153
|
const handleAcceptAll = () => {
|
|
114
154
|
acceptAll()
|
|
155
|
+
isVisible.value = false
|
|
156
|
+
isSettingsMode.value = false
|
|
115
157
|
}
|
|
116
158
|
|
|
117
159
|
const handleRejectAll = () => {
|
|
118
160
|
rejectAll()
|
|
161
|
+
isVisible.value = false
|
|
162
|
+
isSettingsMode.value = false
|
|
119
163
|
}
|
|
120
164
|
|
|
121
165
|
const handleAcceptSelection = () => {
|
|
122
|
-
|
|
166
|
+
const mode = mergedConfig.value.mode
|
|
167
|
+
|
|
168
|
+
if (mode === 'essential') {
|
|
169
|
+
handleAcceptAll()
|
|
170
|
+
return
|
|
171
|
+
}
|
|
172
|
+
|
|
173
|
+
if (!isSettingsMode.value) {
|
|
174
|
+
const selectedIds = Object.keys(categories.value).filter(id => categories.value[id])
|
|
175
|
+
acceptSelected(selectedIds)
|
|
176
|
+
isVisible.value = false
|
|
177
|
+
return
|
|
178
|
+
}
|
|
179
|
+
|
|
180
|
+
// Settings mode: navigate tabs or save
|
|
181
|
+
const tabs = enabledSettingsTabs.value
|
|
182
|
+
if (currentTab.value === 'privacy' && tabs.length) {
|
|
183
|
+
currentTab.value = tabs[0]
|
|
184
|
+
return
|
|
185
|
+
}
|
|
186
|
+
|
|
187
|
+
const idx = tabs.indexOf(currentTab.value)
|
|
188
|
+
if (idx >= 0 && idx < tabs.length - 1) {
|
|
189
|
+
currentTab.value = tabs[idx + 1]
|
|
190
|
+
return
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
const selectedIds = Object.keys(categories.value).filter(id => categories.value[id])
|
|
194
|
+
acceptSelected(selectedIds)
|
|
195
|
+
isVisible.value = false
|
|
196
|
+
isSettingsMode.value = false
|
|
123
197
|
}
|
|
124
198
|
|
|
125
199
|
const handleOpenSettings = () => {
|
|
126
|
-
|
|
200
|
+
isSettingsMode.value = true
|
|
127
201
|
}
|
|
128
202
|
|
|
129
203
|
const handleSelectTab = (tabId) => {
|
|
130
|
-
|
|
204
|
+
currentTab.value = tabId
|
|
131
205
|
}
|
|
132
206
|
|
|
133
207
|
const handleToggleCategory = (categoryId) => {
|
|
134
|
-
const
|
|
135
|
-
if (
|
|
136
|
-
categories.value[categoryId] =
|
|
208
|
+
const cat = mergedConfig.value.categories[categoryId]
|
|
209
|
+
if (cat && !cat.disabled && !cat.required) {
|
|
210
|
+
categories.value[categoryId] = !categories.value[categoryId]
|
|
137
211
|
}
|
|
138
212
|
}
|
|
139
213
|
|
|
140
214
|
const handleClose = () => {
|
|
141
215
|
if (isSettingsMode.value) {
|
|
142
|
-
|
|
216
|
+
if (hasConsented.value) {
|
|
217
|
+
isVisible.value = false
|
|
218
|
+
isSettingsMode.value = false
|
|
219
|
+
} else {
|
|
220
|
+
isSettingsMode.value = false
|
|
221
|
+
}
|
|
222
|
+
} else {
|
|
223
|
+
if (mergedConfig.value.mode === 'essential') {
|
|
224
|
+
handleAcceptAll()
|
|
225
|
+
}
|
|
143
226
|
}
|
|
144
227
|
}
|
|
145
228
|
|
|
146
|
-
// Cleanup on unmount
|
|
147
229
|
onUnmounted(() => {
|
|
148
|
-
|
|
230
|
+
if (typeof document !== 'undefined') {
|
|
231
|
+
document.body.style.overflow = ''
|
|
232
|
+
}
|
|
233
|
+
if (typeof window !== 'undefined') {
|
|
234
|
+
delete window.CookieConsent
|
|
235
|
+
}
|
|
149
236
|
})
|
|
150
237
|
|
|
151
238
|
// Expose methods for external use
|
|
152
239
|
defineExpose({
|
|
153
240
|
acceptAll: handleAcceptAll,
|
|
154
241
|
rejectAll: handleRejectAll,
|
|
155
|
-
resetConsent
|
|
156
|
-
|
|
157
|
-
}
|
|
242
|
+
resetConsent,
|
|
243
|
+
show: () => { isVisible.value = true },
|
|
244
|
+
showSettings: () => { isSettingsMode.value = true; isVisible.value = true }
|
|
158
245
|
})
|
|
246
|
+
|
|
247
|
+
// Helpers
|
|
248
|
+
function buildCategoryState(cats) {
|
|
249
|
+
if (!cats) return {}
|
|
250
|
+
return Object.fromEntries(
|
|
251
|
+
Object.keys(cats).map(id => [id, !!cats[id].enabled])
|
|
252
|
+
)
|
|
253
|
+
}
|
|
254
|
+
|
|
255
|
+
function deepMerge(target, source) {
|
|
256
|
+
const result = { ...target }
|
|
257
|
+
for (const key of Object.keys(source)) {
|
|
258
|
+
if (source[key] && typeof source[key] === 'object' && !Array.isArray(source[key])) {
|
|
259
|
+
result[key] = deepMerge(target[key] || {}, source[key])
|
|
260
|
+
} else {
|
|
261
|
+
result[key] = source[key]
|
|
262
|
+
}
|
|
263
|
+
}
|
|
264
|
+
return result
|
|
265
|
+
}
|
|
159
266
|
</script>
|
|
160
267
|
|
|
161
268
|
<style lang="scss" scoped>
|
|
162
269
|
.cookie-consent {
|
|
163
270
|
position: relative;
|
|
164
|
-
|
|
165
|
-
&--has-modal {
|
|
166
|
-
min-height: 100vh;
|
|
167
|
-
}
|
|
168
271
|
}
|
|
169
272
|
</style>
|
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
<template>
|
|
2
|
+
<div v-if="shouldShow" class="cookie-consent-debug">
|
|
3
|
+
<button
|
|
4
|
+
@click="showPanel = !showPanel"
|
|
5
|
+
class="cookie-consent-debug__button"
|
|
6
|
+
title="Cookie Consent Debug"
|
|
7
|
+
>
|
|
8
|
+
🍪
|
|
9
|
+
</button>
|
|
10
|
+
|
|
11
|
+
<div v-if="showPanel" class="cookie-consent-debug__panel">
|
|
12
|
+
<div class="cookie-consent-debug__header">
|
|
13
|
+
<h4>Cookie Consent Debug</h4>
|
|
14
|
+
<button @click="showPanel = false" class="cookie-consent-debug__close">×</button>
|
|
15
|
+
</div>
|
|
16
|
+
<div class="cookie-consent-debug__info">
|
|
17
|
+
<p><strong>Status:</strong> {{ consentStatus }}</p>
|
|
18
|
+
<p v-for="(status, cat) in categoryStatuses" :key="cat">
|
|
19
|
+
<strong>{{ cat }}:</strong> {{ status }}
|
|
20
|
+
</p>
|
|
21
|
+
</div>
|
|
22
|
+
|
|
23
|
+
<div class="cookie-consent-debug__actions">
|
|
24
|
+
<button @click="doAcceptAll" class="cookie-consent-debug__btn cookie-consent-debug__btn--accept">Accept All</button>
|
|
25
|
+
<button @click="doRejectAll" class="cookie-consent-debug__btn cookie-consent-debug__btn--reject">Reject All</button>
|
|
26
|
+
<button @click="doClear" class="cookie-consent-debug__btn cookie-consent-debug__btn--clear">Clear</button>
|
|
27
|
+
<button @click="doShow" class="cookie-consent-debug__btn cookie-consent-debug__btn--show">Show Banner</button>
|
|
28
|
+
</div>
|
|
29
|
+
</div>
|
|
30
|
+
</div>
|
|
31
|
+
</template>
|
|
32
|
+
|
|
33
|
+
<script setup>
|
|
34
|
+
import { ref, computed } from 'vue'
|
|
35
|
+
|
|
36
|
+
const props = defineProps({
|
|
37
|
+
debug: { type: Boolean, default: false },
|
|
38
|
+
categories: { type: Array, default: () => ['necessary', 'analytics', 'marketing', 'preferences'] }
|
|
39
|
+
})
|
|
40
|
+
|
|
41
|
+
const showPanel = ref(false)
|
|
42
|
+
|
|
43
|
+
const shouldShow = computed(() => {
|
|
44
|
+
if (props.debug) return true
|
|
45
|
+
if (typeof window === 'undefined') return false
|
|
46
|
+
const host = window.location?.hostname || ''
|
|
47
|
+
return host === 'localhost' || host.includes('test') || host.includes('.local')
|
|
48
|
+
})
|
|
49
|
+
|
|
50
|
+
const consentStatus = computed(() => {
|
|
51
|
+
return window.CookieConsent?.hasConsent() ? 'Given' : 'Not given'
|
|
52
|
+
})
|
|
53
|
+
|
|
54
|
+
const categoryStatuses = computed(() => {
|
|
55
|
+
const result = {}
|
|
56
|
+
props.categories.forEach(cat => {
|
|
57
|
+
result[cat] = window.CookieConsent?.hasCategoryConsent(cat) ? 'Enabled' : 'Disabled'
|
|
58
|
+
})
|
|
59
|
+
return result
|
|
60
|
+
})
|
|
61
|
+
|
|
62
|
+
const doAcceptAll = () => window.CookieConsent?.acceptAll()
|
|
63
|
+
const doRejectAll = () => window.CookieConsent?.rejectAll()
|
|
64
|
+
const doClear = () => window.CookieConsent?.clearConsent()
|
|
65
|
+
const doShow = () => window.CookieConsent?.show()
|
|
66
|
+
</script>
|
|
67
|
+
|
|
68
|
+
<style scoped>
|
|
69
|
+
.cookie-consent-debug {
|
|
70
|
+
position: fixed;
|
|
71
|
+
bottom: 20px;
|
|
72
|
+
right: 20px;
|
|
73
|
+
z-index: 10000;
|
|
74
|
+
font-family: -apple-system, BlinkMacSystemFont, 'Segoe UI', Roboto, sans-serif;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
.cookie-consent-debug__button {
|
|
78
|
+
width: 40px;
|
|
79
|
+
height: 40px;
|
|
80
|
+
border-radius: 50%;
|
|
81
|
+
background: #0026aa;
|
|
82
|
+
color: white;
|
|
83
|
+
border: none;
|
|
84
|
+
cursor: pointer;
|
|
85
|
+
font-size: 16px;
|
|
86
|
+
display: flex;
|
|
87
|
+
align-items: center;
|
|
88
|
+
justify-content: center;
|
|
89
|
+
box-shadow: 0 2px 8px rgba(0, 0, 0, 0.2);
|
|
90
|
+
transition: all 0.2s;
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
.cookie-consent-debug__button:hover {
|
|
94
|
+
background: #001d88;
|
|
95
|
+
transform: scale(1.1);
|
|
96
|
+
}
|
|
97
|
+
|
|
98
|
+
.cookie-consent-debug__panel {
|
|
99
|
+
position: absolute;
|
|
100
|
+
bottom: 50px;
|
|
101
|
+
right: 0;
|
|
102
|
+
background: white;
|
|
103
|
+
border: 1px solid #e0e0e0;
|
|
104
|
+
border-radius: 8px;
|
|
105
|
+
padding: 16px;
|
|
106
|
+
min-width: 250px;
|
|
107
|
+
box-shadow: 0 4px 12px rgba(0, 0, 0, 0.15);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
.cookie-consent-debug__header {
|
|
111
|
+
display: flex;
|
|
112
|
+
justify-content: space-between;
|
|
113
|
+
align-items: center;
|
|
114
|
+
margin-bottom: 12px;
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
.cookie-consent-debug__header h4 {
|
|
118
|
+
margin: 0;
|
|
119
|
+
font-size: 14px;
|
|
120
|
+
font-weight: 600;
|
|
121
|
+
color: #333;
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
.cookie-consent-debug__close {
|
|
125
|
+
background: none;
|
|
126
|
+
border: none;
|
|
127
|
+
font-size: 18px;
|
|
128
|
+
cursor: pointer;
|
|
129
|
+
color: #666;
|
|
130
|
+
padding: 0 4px;
|
|
131
|
+
}
|
|
132
|
+
|
|
133
|
+
.cookie-consent-debug__info p {
|
|
134
|
+
margin: 4px 0;
|
|
135
|
+
font-size: 12px;
|
|
136
|
+
color: #666;
|
|
137
|
+
}
|
|
138
|
+
|
|
139
|
+
.cookie-consent-debug__actions {
|
|
140
|
+
display: flex;
|
|
141
|
+
flex-direction: column;
|
|
142
|
+
gap: 6px;
|
|
143
|
+
margin-top: 12px;
|
|
144
|
+
}
|
|
145
|
+
|
|
146
|
+
.cookie-consent-debug__btn {
|
|
147
|
+
padding: 6px 12px;
|
|
148
|
+
border: none;
|
|
149
|
+
border-radius: 4px;
|
|
150
|
+
font-size: 11px;
|
|
151
|
+
cursor: pointer;
|
|
152
|
+
transition: all 0.2s;
|
|
153
|
+
color: white;
|
|
154
|
+
}
|
|
155
|
+
|
|
156
|
+
.cookie-consent-debug__btn--accept { background: #28a745; }
|
|
157
|
+
.cookie-consent-debug__btn--accept:hover { background: #218838; }
|
|
158
|
+
.cookie-consent-debug__btn--reject { background: #dc3545; }
|
|
159
|
+
.cookie-consent-debug__btn--reject:hover { background: #c82333; }
|
|
160
|
+
.cookie-consent-debug__btn--clear { background: #ffc107; color: #212529; }
|
|
161
|
+
.cookie-consent-debug__btn--clear:hover { background: #e0a800; }
|
|
162
|
+
.cookie-consent-debug__btn--show { background: #17a2b8; }
|
|
163
|
+
.cookie-consent-debug__btn--show:hover { background: #138496; }
|
|
164
|
+
|
|
165
|
+
@media (max-width: 767px) {
|
|
166
|
+
.cookie-consent-debug { display: none; }
|
|
167
|
+
}
|
|
168
|
+
</style>
|