cookiecraft 1.0.0

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/LICENSE ADDED
@@ -0,0 +1,21 @@
1
+ MIT License
2
+
3
+ Copyright (c) 2026 Webflow Cookie Consent Contributors
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in all
13
+ copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
21
+ SOFTWARE.
package/README.md ADDED
@@ -0,0 +1,418 @@
1
+ # CookieCraft
2
+
3
+ 🍪 **Lightweight GDPR-compliant cookie consent library for any website**
4
+
5
+ Open-source cookie consent library with modern UI, full RGPD compliance, and Google Consent Mode v2 support. Perfect for Webflow, WordPress, Shopify, or any website.
6
+
7
+ ## Features
8
+
9
+ - ✅ **GDPR Compliant** - Full opt-in, cookie blocking before consent
10
+ - 🎨 **Modern UI** - Beautiful, professional design
11
+ - ⚡ **Lightweight** - < 15KB total (JS + CSS minified)
12
+ - 🚀 **Zero Dependencies** - Pure Vanilla JavaScript
13
+ - 🎯 **Universal** - Works on any website (Webflow, WordPress, Shopify, custom sites)
14
+ - 📱 **Responsive** - Mobile-first design
15
+ - ♿ **Accessible** - WCAG 2.2 AA compliant
16
+ - 🔌 **GTM Ready** - Google Consent Mode v2 built-in
17
+ - 🎨 **Customizable** - CSS variables for easy theming
18
+ - 🌐 **i18n Ready** - Multi-language support
19
+
20
+ ## Quick Start
21
+
22
+ ### For Webflow
23
+
24
+ Add this code in **Site Settings > Custom Code > Footer**:
25
+
26
+ ```html
27
+ <!-- CSS -->
28
+ <link rel="stylesheet" href="https://cdn.jsdelivr.net/npm/cookiecraft@1/dist/cookiecraft.min.css">
29
+
30
+ <!-- JavaScript -->
31
+ <script src="https://cdn.jsdelivr.net/npm/cookiecraft@1/dist/cookiecraft.min.js"></script>
32
+
33
+ <!-- Initialize -->
34
+ <script>
35
+ const cookieConsent = new CookieCraft.CookieConsent({
36
+ mode: 'opt-in',
37
+ autoShow: true,
38
+ revision: 1,
39
+
40
+ categories: {
41
+ necessary: {
42
+ enabled: true,
43
+ readOnly: true,
44
+ label: 'Essentiels',
45
+ description: 'Nécessaires au bon fonctionnement du site'
46
+ },
47
+ analytics: {
48
+ enabled: false,
49
+ readOnly: false,
50
+ label: 'Statistiques',
51
+ description: 'Pour comprendre comment vous utilisez notre site'
52
+ },
53
+ marketing: {
54
+ enabled: false,
55
+ readOnly: false,
56
+ label: 'Marketing',
57
+ description: 'Pour vous proposer du contenu personnalisé'
58
+ }
59
+ },
60
+
61
+ theme: 'auto',
62
+ primaryColor: '#0066cc'
63
+ });
64
+
65
+ cookieConsent.init();
66
+ </script>
67
+ ```
68
+
69
+ ### Block Third-Party Scripts
70
+
71
+ Change your tracking scripts to use `type="text/plain"` and `data-cookieconsent` attribute:
72
+
73
+ ```html
74
+ <!-- Google Analytics - Blocked until analytics consent -->
75
+ <script type="text/plain" data-cookieconsent="analytics">
76
+ (function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;...})(window,document,'script','//www.google-analytics.com/analytics.js','ga');
77
+ ga('create', 'UA-XXXXX-Y', 'auto');
78
+ ga('send', 'pageview');
79
+ </script>
80
+
81
+ <!-- Facebook Pixel - Blocked until marketing consent -->
82
+ <script type="text/plain" data-cookieconsent="marketing">
83
+ !function(f,b,e,v,n,t,s){...}
84
+ fbq('init', 'YOUR_PIXEL_ID');
85
+ fbq('track', 'PageView');
86
+ </script>
87
+ ```
88
+
89
+ ## Layout Options
90
+
91
+ Choose from multiple banner styles and positions to match your site's design:
92
+
93
+ ### Banner Layouts
94
+
95
+ - **`bar`** (default) - Full-width banner at top or bottom
96
+ - **`box`** - Compact modal in corner or center
97
+ - **`floating`** - Small notification-style banner
98
+
99
+ ### Banner Positions
100
+
101
+ - **`bottom`** (default) - Bottom of the screen
102
+ - **`top`** - Top of the screen
103
+ - **`center`** - Centered modal with overlay
104
+ - **`bottom-left`** - Bottom left corner
105
+ - **`bottom-right`** - Bottom right corner
106
+
107
+ ### Floating Widget
108
+
109
+ A permanent button that stays visible after consent, allowing users to modify preferences anytime:
110
+
111
+ - **`showWidget`** (default: `true`) - Show/hide the floating widget
112
+ - **`widgetPosition`** (default: `'bottom-right'`) - Widget position: `'bottom-left'`, `'bottom-right'`, `'top-left'`, `'top-right'`
113
+
114
+ ### Examples
115
+
116
+ ```javascript
117
+ // Full-width bar at bottom (classic)
118
+ const consent = new CookieCraft.CookieConsent({
119
+ layout: 'bar',
120
+ position: 'bottom',
121
+ showWidget: true,
122
+ widgetPosition: 'bottom-right'
123
+ });
124
+
125
+ // Small modal in bottom-right corner (modern)
126
+ const consent = new CookieCraft.CookieConsent({
127
+ layout: 'box',
128
+ position: 'bottom-right',
129
+ showWidget: true,
130
+ widgetPosition: 'bottom-left' // Widget on opposite side
131
+ });
132
+
133
+ // Compact floating notification (minimal)
134
+ const consent = new CookieCraft.CookieConsent({
135
+ layout: 'floating',
136
+ position: 'bottom-right',
137
+ showWidget: false // No widget needed, already compact
138
+ });
139
+
140
+ // Centered modal with overlay (maximum attention)
141
+ const consent = new CookieCraft.CookieConsent({
142
+ layout: 'box',
143
+ position: 'center',
144
+ disablePageInteraction: true, // Block page until choice
145
+ showWidget: true
146
+ });
147
+ ```
148
+
149
+ ## Configuration
150
+
151
+ ### Basic Options
152
+
153
+ ```typescript
154
+ interface ConsentConfig {
155
+ // Core settings
156
+ mode: 'opt-in' | 'opt-out'; // GDPR requires 'opt-in'
157
+ autoShow: boolean; // Show banner on first visit
158
+ revision: number; // Policy version (increment to re-ask)
159
+
160
+ // Categories
161
+ categories: {
162
+ necessary: CategoryConfig;
163
+ analytics: CategoryConfig;
164
+ marketing: CategoryConfig;
165
+ preferences?: CategoryConfig;
166
+ };
167
+
168
+ // UI customization
169
+ theme?: 'light' | 'dark' | 'auto';
170
+ layout?: 'bar' | 'box' | 'floating'; // Banner layout style
171
+ position?: 'bottom' | 'top' | 'center' | 'bottom-left' | 'bottom-right';
172
+ primaryColor?: string; // Hex color
173
+ backdropBlur?: boolean; // Backdrop blur effect
174
+ animationStyle?: 'smooth' | 'minimal';
175
+
176
+ // Floating widget
177
+ showWidget?: boolean; // Show permanent settings button
178
+ widgetPosition?: 'bottom-left' | 'bottom-right' | 'top-left' | 'top-right';
179
+
180
+ // Content
181
+ translations?: Translation;
182
+
183
+ // Integration
184
+ gtmConsentMode?: boolean; // Enable GTM Consent Mode v2
185
+
186
+ // Accessibility
187
+ disablePageInteraction?: boolean; // Block page until consent
188
+
189
+ // Callbacks
190
+ onAccept?: (categories) => void;
191
+ onChange?: (categories) => void;
192
+ onReject?: () => void;
193
+ }
194
+ ```
195
+
196
+ ### Category Configuration
197
+
198
+ ```typescript
199
+ interface CategoryConfig {
200
+ enabled: boolean; // Default state
201
+ readOnly: boolean; // If true, user cannot toggle
202
+ label: string; // Display name
203
+ description: string; // Explanation
204
+ }
205
+ ```
206
+
207
+ ### Customization Examples
208
+
209
+ #### Custom Colors
210
+
211
+ ```javascript
212
+ const cookieConsent = new CookieCraft.CookieConsent({
213
+ primaryColor: '#ff6b6b', // Red theme
214
+ theme: 'dark',
215
+ // ...
216
+ });
217
+ ```
218
+
219
+ #### Custom Text
220
+
221
+ ```javascript
222
+ const cookieConsent = new CookieCraft.CookieConsent({
223
+ translations: {
224
+ title: 'Nous respectons votre vie privée',
225
+ description: 'Choisissez les cookies que vous acceptez',
226
+ acceptAll: 'Tout accepter',
227
+ rejectAll: 'Tout refuser',
228
+ customize: 'Personnaliser',
229
+ savePreferences: 'Enregistrer',
230
+ cookieSettings: 'Paramètres cookies', // Floating widget tooltip
231
+ cookies: 'Cookies', // Floating widget text
232
+ },
233
+ // ...
234
+ });
235
+ ```
236
+
237
+ #### With GTM Consent Mode v2
238
+
239
+ ```html
240
+ <!-- In HEAD, BEFORE GTM -->
241
+ <script>
242
+ window.dataLayer = window.dataLayer || [];
243
+ function gtag(){dataLayer.push(arguments);}
244
+
245
+ gtag('consent', 'default', {
246
+ 'ad_storage': 'denied',
247
+ 'analytics_storage': 'denied',
248
+ 'ad_user_data': 'denied',
249
+ 'ad_personalization': 'denied'
250
+ });
251
+ </script>
252
+
253
+ <!-- Google Tag Manager -->
254
+ <script>(function(w,d,s,l,i){...})(window,document,'script','dataLayer','GTM-XXXX');</script>
255
+
256
+ <!-- In FOOTER -->
257
+ <script>
258
+ const cookieConsent = new CookieCraft.CookieConsent({
259
+ gtmConsentMode: true, // Enable GTM integration
260
+ // ...
261
+ });
262
+ cookieConsent.init();
263
+ </script>
264
+ ```
265
+
266
+ ## API
267
+
268
+ ### Methods
269
+
270
+ ```javascript
271
+ // Initialize
272
+ cookieConsent.init();
273
+
274
+ // Show banner
275
+ cookieConsent.show();
276
+
277
+ // Hide banner
278
+ cookieConsent.hide();
279
+
280
+ // Show preferences modal
281
+ cookieConsent.showPreferences();
282
+
283
+ // Get current consent
284
+ const consent = cookieConsent.getConsent();
285
+ // Returns: { version, timestamp, categories, expiresAt }
286
+
287
+ // Reset (clear and re-show)
288
+ cookieConsent.reset();
289
+
290
+ // Event listeners
291
+ cookieConsent.on('consent:accept', (categories) => {
292
+ console.log('Accepted:', categories);
293
+ });
294
+
295
+ cookieConsent.on('consent:update', (categories) => {
296
+ console.log('Updated:', categories);
297
+ });
298
+ ```
299
+
300
+ ### Events
301
+
302
+ - `consent:init` - Library initialized
303
+ - `consent:show` - Banner shown
304
+ - `consent:accept` - All accepted
305
+ - `consent:reject` - All rejected
306
+ - `consent:update` - Consent changed
307
+ - `consent:load` - Stored consent loaded
308
+ - `preferences:show` - Preferences modal shown
309
+ - `script:activated` - Script unblocked
310
+
311
+ ### Cookie Settings Access
312
+
313
+ #### Option 1: Floating Widget (Automatic)
314
+
315
+ The library automatically shows a permanent floating button after consent (enabled by default):
316
+
317
+ ```javascript
318
+ const cookieConsent = new CookieCraft.CookieConsent({
319
+ showWidget: true, // Show floating widget (default)
320
+ widgetPosition: 'bottom-right', // Position
321
+ // ...
322
+ });
323
+ ```
324
+
325
+ #### Option 2: Custom Button (Manual)
326
+
327
+ In Webflow Designer, add your own button and connect it:
328
+
329
+ ```html
330
+ <script>
331
+ document.getElementById('cookie-settings')?.addEventListener('click', () => {
332
+ window.cookieConsent?.showPreferences();
333
+ });
334
+ </script>
335
+ ```
336
+
337
+ ## Styling
338
+
339
+ ### CSS Variables
340
+
341
+ Customize appearance using CSS variables:
342
+
343
+ ```css
344
+ :root {
345
+ /* Colors */
346
+ --cc-primary: #0066cc;
347
+ --cc-bg: #ffffff;
348
+ --cc-text: #1a1a1a;
349
+ --cc-text-secondary: #666666;
350
+
351
+ /* Spacing */
352
+ --cc-padding: 1.5rem;
353
+ --cc-gap: 0.75rem;
354
+
355
+ /* Borders */
356
+ --cc-border-radius: 12px;
357
+ --cc-shadow: 0 -4px 24px rgba(0, 0, 0, 0.12);
358
+
359
+ /* Animations */
360
+ --cc-transition: 0.3s cubic-bezier(0.4, 0, 0.2, 1);
361
+ }
362
+ ```
363
+
364
+ ## Browser Support
365
+
366
+ - Chrome/Edge (last 2 versions)
367
+ - Firefox (last 2 versions)
368
+ - Safari (last 2 versions)
369
+ - iOS Safari (last 2 versions)
370
+ - Chrome Android (last 2 versions)
371
+
372
+ ## Development
373
+
374
+ ```bash
375
+ # Install dependencies
376
+ npm install
377
+
378
+ # Dev mode with watch
379
+ npm run dev
380
+
381
+ # Build
382
+ npm run build
383
+
384
+ # Run tests
385
+ npm test
386
+
387
+ # Type check
388
+ npm run type-check
389
+
390
+ # Check bundle size
391
+ npm run size
392
+ ```
393
+
394
+ ## Why This Library?
395
+
396
+ ### vs Premium Solutions
397
+
398
+ - **Cost**: Free vs €39+/month
399
+ - **Performance**: 10-15KB vs 50-100KB+
400
+ - **Privacy**: Self-hosted option vs SaaS only
401
+ - **Flexibility**: Full code access vs black box
402
+
403
+ ## Contributing
404
+
405
+ Contributions are welcome! Please feel free to submit a Pull Request.
406
+
407
+ ## License
408
+
409
+ MIT © 2026
410
+
411
+ ## Credits
412
+
413
+ Inspired by:
414
+ - [vanilla-cookieconsent](https://github.com/orestbida/cookieconsent) - Technical patterns
415
+
416
+ ---
417
+
418
+ Made with ❤️ for the open web
@@ -0,0 +1 @@
1
+ .cc-banner,.cc-modal,.cc-widget{--cc-primary:#06c;--cc-primary-hover:#0052a3;--cc-primary-glow:color-mix(in srgb,var(--cc-primary) 30%,transparent);--cc-bg:#fff;--cc-text:#1a1a1a;--cc-text-secondary:#666;--cc-border:#e5e7eb;--cc-overlay:rgba(0,0,0,.4);--cc-padding:1.5rem;--cc-gap:0.75rem;--cc-border-radius:12px;--cc-shadow:0 -4px 24px rgba(0,0,0,.12);--cc-transition:0.3s cubic-bezier(0.4,0,0.2,1);--cc-backdrop-blur:10px;--cc-font-family:-apple-system,BlinkMacSystemFont,"Segoe UI",Roboto,"Helvetica Neue",Arial,sans-serif}.cc-banner *,.cc-modal *,.cc-widget *{box-sizing:border-box;margin:0;padding:0}.cc-banner{-webkit-font-smoothing:antialiased;-moz-osx-font-smoothing:grayscale;background:var(--cc-bg);bottom:0;box-shadow:var(--cc-shadow);font-family:var(--cc-font-family);left:0;padding:var(--cc-padding);position:fixed;right:0;transform:translateY(100%);transition:transform var(--cc-transition);z-index:9999}.cc-banner.cc-backdrop-blur{backdrop-filter:blur(var(--cc-backdrop-blur));background:hsla(0,0%,100%,.95)}.cc-banner.is-visible{transform:translateY(0)}.cc-banner__container{margin:0 auto;max-width:1200px}.cc-banner__content{margin-bottom:1rem}.cc-banner__title{color:var(--cc-text);font-size:1.125rem;font-weight:600;line-height:1.4;margin:0 0 .5rem}.cc-banner__description{color:var(--cc-text-secondary);font-size:.875rem;line-height:1.6;margin:0}.cc-banner__description a{color:var(--cc-primary);font-weight:500;text-decoration:none;transition:all .2s ease}.cc-banner__description a:hover{color:var(--cc-primary-hover);text-decoration:underline}.cc-banner__actions{display:flex;flex-wrap:wrap;gap:var(--cc-gap)}.cc-btn{align-items:center;border:none;border-radius:var(--cc-border-radius);cursor:pointer;display:inline-flex;font-family:inherit;font-size:.875rem;font-weight:500;justify-content:center;min-height:44px;min-width:44px;padding:.75rem 1.5rem;text-decoration:none;transition:all .2s ease}.cc-btn:focus-visible{outline:3px solid var(--cc-primary);outline-offset:2px}.cc-btn:disabled{cursor:not-allowed;opacity:.6}.cc-btn--accept{background:var(--cc-primary);color:#fff}.cc-btn--accept:hover:not(:disabled){background:var(--cc-primary-hover);box-shadow:0 4px 12px var(--cc-primary-glow);transform:translateY(-1px)}.cc-btn--accept:active:not(:disabled){transform:translateY(0)}.cc-btn--reject{background:var(--cc-text);color:#fff}.cc-btn--reject:hover:not(:disabled){box-shadow:0 4px 12px rgba(0,0,0,.2);opacity:.85;transform:translateY(-1px)}.cc-btn--reject:active:not(:disabled){transform:translateY(0)}.cc-btn--primary{background:var(--cc-primary);color:#fff}.cc-btn--primary:hover:not(:disabled){background:var(--cc-primary-hover);box-shadow:0 4px 12px var(--cc-primary-glow);transform:translateY(-1px)}.cc-btn--primary:active:not(:disabled){transform:translateY(0)}.cc-btn--secondary{background:#f3f4f6;color:var(--cc-text)}.cc-btn--secondary:hover:not(:disabled){background:#e5e7eb;transform:translateY(-1px)}.cc-btn--secondary:active:not(:disabled){transform:translateY(0)}.cc-btn--tertiary{background:transparent;border:1px solid var(--cc-border);color:var(--cc-text-secondary)}.cc-btn--tertiary:hover:not(:disabled){background:rgba(0,0,0,.03);color:var(--cc-text);transform:translateY(-1px)}.cc-btn--tertiary:active:not(:disabled){transform:translateY(0)}@media (min-width:768px){.cc-banner--bar .cc-banner__container{align-items:center;display:flex;gap:2rem;justify-content:space-between}.cc-banner--bar .cc-banner__content{flex:1;margin-bottom:0}.cc-banner--bar .cc-banner__actions{flex-shrink:0}}.cc-banner--top{bottom:auto;box-shadow:0 4px 24px rgba(0,0,0,.12);top:0;transform:translateY(-100%)}.cc-banner--top.is-visible{transform:translateY(0)}.cc-banner--center{border-radius:var(--cc-border-radius);bottom:auto;left:50%;max-width:600px;opacity:0;right:auto;top:50%;transform:translate(-50%,-50%) scale(.9)}.cc-banner--center.is-visible{opacity:1;transform:translate(-50%,-50%) scale(1)}.cc-banner--bottom-right{border-radius:var(--cc-border-radius);bottom:1.5rem;left:auto;max-width:420px;right:1.5rem;transform:translateX(calc(100% + 2rem))}.cc-banner--bottom-right.is-visible{transform:translateX(0)}.cc-banner--bottom-left{border-radius:var(--cc-border-radius);bottom:1.5rem;left:1.5rem;max-width:420px;right:auto;transform:translateX(calc(-100% - 2rem))}.cc-banner--bottom-left.is-visible{transform:translateX(0)}.cc-banner--box{border-radius:var(--cc-border-radius);max-width:420px}.cc-banner--box .cc-banner__container{display:block}.cc-banner--box .cc-banner__actions{flex-direction:column;margin-top:1rem}.cc-banner--box .cc-banner__actions .cc-btn{width:100%}.cc-banner--floating{border-radius:var(--cc-border-radius);box-shadow:0 8px 32px rgba(0,0,0,.2);max-width:380px}.cc-banner--floating .cc-banner__container{display:block}.cc-banner--floating .cc-banner__title{font-size:1rem}.cc-banner--floating .cc-banner__description{font-size:.8125rem}.cc-banner--floating .cc-banner__actions{flex-direction:column;margin-top:1rem}.cc-banner--floating .cc-banner__actions .cc-btn{font-size:.8125rem;padding:.625rem 1rem;width:100%}@media (prefers-color-scheme:dark){.cc-banner[data-theme=auto]{--cc-bg:#1a1a1a;--cc-text:#fff;--cc-text-secondary:#a0a0a0;--cc-border:#333}.cc-banner[data-theme=auto].cc-backdrop-blur{background:rgba(26,26,26,.95)}.cc-banner[data-theme=auto] .cc-btn--secondary{background:#2a2a2a;color:#fff}.cc-banner[data-theme=auto] .cc-btn--secondary:hover:not(:disabled){background:#333}.cc-banner[data-theme=auto] .cc-btn--reject{background:#fff;color:#1a1a1a}.cc-banner[data-theme=auto] .cc-btn--tertiary{border-color:#555;color:#a0a0a0}}.cc-banner[data-theme=dark]{--cc-bg:#1a1a1a;--cc-text:#fff;--cc-text-secondary:#a0a0a0;--cc-border:#333}.cc-banner[data-theme=dark].cc-backdrop-blur{background:rgba(26,26,26,.95)}.cc-banner[data-theme=dark] .cc-btn--secondary{background:#2a2a2a;color:#fff}.cc-banner[data-theme=dark] .cc-btn--secondary:hover:not(:disabled){background:#333}.cc-banner[data-theme=dark] .cc-btn--reject{background:#fff;color:#1a1a1a}.cc-banner[data-theme=dark] .cc-btn--tertiary{border-color:#555;color:#a0a0a0}@keyframes cc-fadeIn{0%{opacity:0}to{opacity:1}}@keyframes cc-slideUp{0%{opacity:0;transform:translateY(100%)}to{opacity:1;transform:translateY(0)}}@keyframes cc-slideDown{0%{opacity:0;transform:translateY(-100%)}to{opacity:1;transform:translateY(0)}}@keyframes cc-scaleIn{0%{opacity:0;transform:scale(.9)}to{opacity:1;transform:scale(1)}}@keyframes cc-backdropFadeIn{0%{opacity:0}to{opacity:1}}.cc-animate-fadeIn{animation:cc-fadeIn .3s ease-out}.cc-animate-slideUp{animation:cc-slideUp .4s cubic-bezier(.4,0,.2,1)}.cc-animate-slideDown{animation:cc-slideDown .4s cubic-bezier(.4,0,.2,1)}.cc-animate-scaleIn{animation:cc-scaleIn .3s cubic-bezier(.4,0,.2,1)}@media (prefers-reduced-motion:reduce){.cc-banner{transition:none}.cc-animate-fadeIn,.cc-animate-scaleIn,.cc-animate-slideDown,.cc-animate-slideUp{animation:none}.cc-btn{transition:none}}.cc-modal{align-items:center;bottom:0;display:flex;justify-content:center;left:0;opacity:0;padding:1rem;pointer-events:none;position:fixed;right:0;top:0;transition:opacity var(--cc-transition);z-index:10000}.cc-modal.is-visible{opacity:1;pointer-events:all}.cc-modal__overlay{background:var(--cc-overlay);bottom:0;cursor:pointer;left:0;position:absolute;right:0;top:0}.cc-modal__content{background:var(--cc-bg);border-radius:var(--cc-border-radius);box-shadow:0 20px 60px rgba(0,0,0,.3);display:flex;flex-direction:column;max-height:90vh;max-width:600px;overflow:hidden;position:relative;transform:scale(.9);transition:transform var(--cc-transition);width:100%}.cc-modal.is-visible .cc-modal__content{transform:scale(1)}.cc-modal__header{align-items:center;background:linear-gradient(135deg,var(--cc-primary) 0,var(--cc-primary-hover) 100%);box-shadow:0 2px 8px rgba(0,0,0,.1);display:flex;flex-shrink:0;justify-content:space-between;padding:2rem 1.5rem}.cc-modal__header h2{color:#fff;font-size:1.375rem;font-weight:700;margin:0;text-shadow:0 1px 2px rgba(0,0,0,.1)}.cc-modal__close{align-items:center;background:hsla(0,0%,100%,.2);border:none;border-radius:6px;color:#fff;cursor:pointer;display:flex;justify-content:center;padding:.5rem;transition:all .2s ease}.cc-modal__close:hover{background:hsla(0,0%,100%,.3);transform:scale(1.05)}.cc-modal__close:focus-visible{outline:2px solid hsla(0,0%,100%,.5);outline-offset:2px}.cc-modal__body{flex:1;overflow-y:auto;padding:1.5rem}.cc-category{border-bottom:1px solid var(--cc-border);padding:1rem 0}.cc-category:last-child{border-bottom:none}.cc-category__header{align-items:flex-start;display:flex;gap:1rem}.cc-category__info{flex:1}.cc-category__info h3{color:var(--cc-text);font-size:1rem;font-weight:600;margin:0 0 .25rem}.cc-category__info p{color:var(--cc-text-secondary);font-size:.875rem;line-height:1.5;margin:0}.cc-toggle{align-items:center;cursor:pointer;display:inline-flex;flex-shrink:0;position:relative;-webkit-user-select:none;-moz-user-select:none;user-select:none}.cc-toggle input{height:0;opacity:0;position:absolute;width:0}.cc-toggle__slider{background:#d1d5db;border-radius:12px;height:24px;position:relative;transition:background-color .2s ease;width:48px}.cc-toggle__slider:before{background:#fff;border-radius:50%;box-shadow:0 2px 4px rgba(0,0,0,.2);content:"";height:20px;left:2px;position:absolute;top:2px;transition:transform .2s ease;width:20px}.cc-toggle input:checked+.cc-toggle__slider{background:var(--cc-primary)}.cc-toggle input:checked+.cc-toggle__slider:before{transform:translateX(24px)}.cc-toggle input:focus-visible+.cc-toggle__slider{outline:2px solid var(--cc-primary);outline-offset:2px}.cc-toggle--disabled{cursor:not-allowed;opacity:.6}.cc-toggle--disabled input:checked+.cc-toggle__slider{background:#9ca3af}.cc-modal__footer{align-items:center;border-top:1px solid var(--cc-border);display:flex;flex-shrink:0;gap:var(--cc-gap);justify-content:space-between;padding:1.5rem}.cc-modal__footer-links{align-items:center;display:flex;gap:.5rem}.cc-modal__footer-actions{display:flex;gap:var(--cc-gap)}.cc-privacy-link{align-items:center;color:var(--cc-text-secondary);display:inline-flex;font-size:.875rem;gap:.25rem;text-decoration:none;transition:color .2s ease}.cc-privacy-link:hover{color:var(--cc-primary);text-decoration:underline}@media (prefers-color-scheme:dark){.cc-modal[data-theme=auto] .cc-modal__content{background:#1a1a1a}.cc-modal[data-theme=auto] .cc-modal__close:hover{background:hsla(0,0%,100%,.1)}}.cc-modal[data-theme=dark] .cc-modal__content{background:#1a1a1a}.cc-modal[data-theme=dark] .cc-modal__close:hover{background:hsla(0,0%,100%,.1)}.cc-modal--bottom-right{align-items:flex-end;justify-content:flex-end;padding:1.5rem}.cc-modal--bottom-right .cc-modal__content{max-width:450px;transform:translateX(100%)}.cc-modal--bottom-right.is-visible .cc-modal__content{transform:translateX(0)}.cc-modal--bottom-left{align-items:flex-end;justify-content:flex-start;padding:1.5rem}.cc-modal--bottom-left .cc-modal__content{max-width:450px;transform:translateX(-100%)}.cc-modal--bottom-left.is-visible .cc-modal__content{transform:translateX(0)}.cc-modal--top-right{align-items:flex-start;justify-content:flex-end;padding:1.5rem}.cc-modal--top-right .cc-modal__content{max-width:450px;transform:translateX(100%)}.cc-modal--top-right.is-visible .cc-modal__content{transform:translateX(0)}.cc-modal--top-left{align-items:flex-start;justify-content:flex-start;padding:1.5rem}.cc-modal--top-left .cc-modal__content{max-width:450px;transform:translateX(-100%)}.cc-modal--top-left.is-visible .cc-modal__content{transform:translateX(0)}@media (max-width:640px){.cc-modal{align-items:flex-end;padding:0}.cc-modal__content{border-radius:16px 16px 0 0;max-height:85vh;max-width:100%;transform:translateY(100%);width:100%}.cc-modal.is-visible .cc-modal__content{transform:translateY(0)}.cc-modal__body{padding:1rem 1.25rem}.cc-modal__footer{align-items:stretch;flex-direction:column;padding:1rem 1.25rem 1.5rem}.cc-modal__footer-links{justify-content:center;margin-top:.75rem;order:2}.cc-modal__footer-actions{flex-direction:column;order:1;width:100%}.cc-modal__footer .cc-btn{width:100%}}.cc-widget{align-items:center;background:var(--cc-primary);border-radius:50px;box-shadow:0 4px 12px rgba(0,0,0,.15);color:#fff;cursor:pointer;display:flex;font-family:var(--cc-font-family);gap:.5rem;min-height:44px;opacity:0;padding:.75rem 1rem;position:fixed;transform:scale(0);transition:all .3s cubic-bezier(.4,0,.2,1);-webkit-user-select:none;-moz-user-select:none;user-select:none;z-index:9998}.cc-widget.is-visible{opacity:1;transform:scale(1)}.cc-widget:hover{box-shadow:0 6px 20px var(--cc-primary-glow);transform:scale(1.05)}.cc-widget:active{transform:scale(.98)}.cc-widget:focus-visible{outline:3px solid #fff;outline-offset:2px}.cc-widget__icon{flex-shrink:0;height:24px;width:24px}.cc-widget__text{font-size:.875rem;font-weight:500;white-space:nowrap}.cc-widget--bottom-right{bottom:1.5rem;right:1.5rem}.cc-widget--bottom-left{bottom:1.5rem;left:1.5rem}.cc-widget--top-right{right:1.5rem;top:1.5rem}.cc-widget--top-left{left:1.5rem;top:1.5rem}.cc-widget--compact{border-radius:50%;height:48px;justify-content:center;padding:.75rem;width:48px}.cc-widget--compact .cc-widget__text{display:none}@media (max-width:640px){.cc-widget--full .cc-widget__text{display:none}.cc-widget--full{border-radius:50%;height:56px;justify-content:center;padding:.75rem;width:56px}}@keyframes cc-widget-bounce{0%{transform:scale(0)}50%{transform:scale(1.1)}to{transform:scale(1)}}.cc-widget.is-visible{animation:cc-widget-bounce .4s cubic-bezier(.4,0,.2,1)}@media (prefers-reduced-motion:reduce){.cc-widget{transition:none}.cc-widget.is-visible{animation:none}.cc-widget:hover{transform:none}}