domma-cms 0.106.0 → 0.107.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.
Files changed (36) hide show
  1. package/admin/dist/domma/domma-tools.css +3 -3
  2. package/admin/dist/domma/domma-tools.min.js +3 -3
  3. package/admin/js/lib/effect-defs.js +1 -1
  4. package/admin/js/lib/effects-builder.js +2 -2
  5. package/admin/js/lib/shortcode-context-menu.js +2 -2
  6. package/admin/js/templates/docs/usage-tools.html +7 -0
  7. package/package.json +2 -2
  8. package/plugins/_lib/call/invite.js +172 -0
  9. package/plugins/_lib/call/rooms.js +19 -5
  10. package/plugins/_lib/call/routes.js +78 -3
  11. package/plugins/_lib/mail/render.js +14 -1
  12. package/plugins/_lib/public/call/call.css +1 -1
  13. package/plugins/_lib/public/call/call.js +287 -23
  14. package/plugins/booking/CLAUDE.md +18 -1
  15. package/plugins/booking/docs/guide.md +1 -0
  16. package/plugins/booking/lib/call.js +18 -2
  17. package/plugins/booking/lib/store.js +2 -0
  18. package/plugins/booking/plugin.js +59 -6
  19. package/plugins/booking/plugin.json +1 -1
  20. package/plugins/booking/plugin.public.js +4 -4
  21. package/plugins/booking/tests/api.test.js +114 -0
  22. package/plugins/free-tier.lock.json +10 -10
  23. package/public/js/celebrations/index.js +1 -1
  24. package/public/js/celebrations/themes/butterfly-drawing.js +1 -0
  25. package/public/js/celebrations/themes/halloween-drawing.js +1 -0
  26. package/public/js/celebrations/themes/halloween.js +1 -1
  27. package/public/js/celebrations/themes/valentines.js +1 -1
  28. package/public/js/celebrations/trait-data.mjs +1 -0
  29. package/public/js/effects.js +1 -1
  30. package/server/routes/api/calls.js +25 -3
  31. package/server/routes/call-public.js +4 -3
  32. package/server/services/callInvites.js +238 -0
  33. package/server/services/hooks.js +55 -0
  34. package/server/services/instantCalls.js +30 -3
  35. package/server/services/plugins.js +6 -1
  36. package/server/templates/page.html +1 -1
@@ -32,13 +32,13 @@ import {getConfig} from '../../server/config.js';
32
32
  import {getPluginStates} from '../../server/services/plugins.js';
33
33
  import {getPermissionsFor} from '../../server/services/roles.js';
34
34
  import {getEffectiveRoles} from '../../server/services/userRoles.js';
35
- import {listUsers} from '../../server/services/users.js';
35
+ import {getUserById, listUsers} from '../../server/services/users.js';
36
36
  import {isToolEnabled} from '../../server/services/tools.js';
37
37
  import {cleanBookable, cleanCustomer, cleanService, whenLabel} from './public/lib/clean.js';
38
38
  import {bookingRange, HOLDING, slotsFor} from './public/lib/slots.js';
39
39
  import {addDays, atWallClock, isDate, wallClock} from './public/lib/time.js';
40
40
  import {bookingRegistry} from './lib/extensions.js';
41
- import {hostPath, joinPath, sessionOf, whoIsCalling} from './lib/call.js';
41
+ import {callWindow, hostPath, invitePath, joinPath, sessionOf, whoIsCalling} from './lib/call.js';
42
42
  import {compose, icsFor, mailReady, send, siteAddress, siteTitle} from './lib/mail.js';
43
43
  import {
44
44
  BOOKABLES, BOOKINGS, NAME, SERVICES, SlotTaken, allBookables, allBookings, allServices, calendarBusy, changeBooking,
@@ -87,7 +87,7 @@ const publicFields = (list) => list.map(f => ({
87
87
  */
88
88
  const adminBooking = (b) => {
89
89
  if (!b) return b;
90
- const {token, hostKey, ...rest} = b;
90
+ const {token, hostKey, callExtraKey, ...rest} = b;
91
91
  return b.online === true && hostKey ? {...rest, hostUrl: hostPath(settingsNow(), b)} : rest;
92
92
  };
93
93
 
@@ -123,7 +123,7 @@ function registerShortcodes(hooks) {
123
123
  });
124
124
  }
125
125
 
126
- export const ASSET_V = '1.5.0';
126
+ export const ASSET_V = '1.6.0';
127
127
 
128
128
  const HERE = path.dirname(fileURLToPath(import.meta.url));
129
129
 
@@ -826,7 +826,57 @@ export default async function booking(fastify, options) {
826
826
  chatStore = createChatStore(path.join(dataDir, 'call-chat.json'));
827
827
  whoOf = (await import('../_lib/call/rooms.js')).whoOf || null;
828
828
  } catch { chatStore = null; }
829
- calls = mountCallRoutes(fastify, {prefix: '/call', ns: 'booking', ...(chatStore ? {chat: chatStore} : {}), resolve: async (request, room, body) => {
829
+ // Add people (1.6; a core with services/callInvites.js): the host searches their contacts and the team, and
830
+ // each person gets their own `&g=` pass under the session's callExtraKey (never the customer's token or the
831
+ // host key). They knock like the customer. Without that core the button stays hidden.
832
+ let invites = null;
833
+ try { invites = await import('../../server/services/callInvites.js'); } catch { invites = null; }
834
+ /** The host's booking of this session (their identity is `<booking>:host`), or null. */
835
+ const hostBooking = async (room, identity) => {
836
+ const id = String(identity || '').endsWith(':host') ? String(identity).slice(0, -5) : '';
837
+ const b = id ? await getBooking(id).catch(() => null) : null;
838
+ return b && b.online === true && HOLDING.has(b.status) && sessionOf(b) === room ? b : null;
839
+ };
840
+ /** The session's extra key: the one a booking of it already has, else a new one - on every booking of it. */
841
+ const extraKeyOf = async (b, room) => {
842
+ const session = (await allBookings()).filter(x => x.online === true && HOLDING.has(x.status) && sessionOf(x) === room);
843
+ const key = session.map(x => x.callExtraKey).find(Boolean) || (await import('../_lib/call/invite.js')).newInviteKey();
844
+ for (const x of session) if (!x.callExtraKey) await changeBooking(x.id, {callExtraKey: key}, 'call');
845
+ // Two sends at once: the first key written stays (changeBooking never replaces one) - use what is stored.
846
+ return (await getBooking(b.id).catch(() => null))?.callExtraKey || key;
847
+ };
848
+ /**
849
+ * The host's user on the site: the person's linked, active account - else '' (the host link then may be held
850
+ * by someone from outside - it is emailed to the person's address and sits in the .ics - so they get no team
851
+ * directory, no contacts and nothing sent in the site's name: Copy link only).
852
+ */
853
+ const hostUserOf = async (b) => {
854
+ const bk = b?.bookableId ? await getBookable(b.bookableId).catch(() => null) : null;
855
+ const u = bk?.userId ? await getUserById(bk.userId).catch(() => null) : null;
856
+ return u && u.disabled !== true && u.active !== false && u.isActive !== false ? u.id : '';
857
+ };
858
+ const invite = invites ? {
859
+ search: async (room, q, {identity}) => {
860
+ const b = await hostBooking(room, identity);
861
+ const owner = b ? await hostUserOf(b) : '';
862
+ return owner ? invites.searchInvitees({hostUserId: owner, q}) : [];
863
+ },
864
+ send: async (room, people, {identity, name, via, request}) => {
865
+ const b = await hostBooking(room, identity);
866
+ if (!b) return {error: 'This booking has no call now.'};
867
+ const owner = await hostUserOf(b);
868
+ if (!owner && people.length) return {error: 'Copy the link and pass it on yourself.'};
869
+ const key = await extraKeyOf(b, room);
870
+ const s = await liveSettings();
871
+ const base = invites.inviteOrigin ? invites.inviteOrigin(request) : siteBase(request);
872
+ const newLink = () => `${base}${invitePath(s, b, key)}`;
873
+ const targets = await invites.resolveInvitees(people, {hostUserId: owner});
874
+ const results = await invites.deliverInvites(targets, {linkFor: newLink, hostName: name || b.bookableName || 'Your host',
875
+ hostUserId: owner, title: b.serviceName, via, key: `booking:${room}`, expiresAt: callWindow(b).closesAt, log});
876
+ return {link: newLink(), results};
877
+ }
878
+ } : null;
879
+ calls = mountCallRoutes(fastify, {prefix: '/call', ns: 'booking', ...(chatStore ? {chat: chatStore} : {}), ...(invite ? {invite} : {}), resolve: async (request, room, body) => {
830
880
  const b = await getBooking(String(body?.b || '')).catch(() => null);
831
881
  // A class opened early ("Call now") is open for everyone in the session - someone who booked in
832
882
  // after the click too: take the session's latest opening when this booking has none of its own.
@@ -836,7 +886,10 @@ export default async function booking(fastify, options) {
836
886
  .map(x => x.callOpenedAt).sort().at(-1);
837
887
  if (opened) b.callOpenedAt = opened;
838
888
  }
839
- return whoIsCalling(b, room, {t: body?.t, h: body?.h});
889
+ const who = whoIsCalling(b, room, {t: body?.t, h: body?.h, g: body?.g});
890
+ // Add people for a host with no account here: Copy link only (see hostUserOf).
891
+ if (who?.host && invite && !(await hostUserOf(b))) return {...who, inviteOptions: {search: false, typed: false, copyLink: true}};
892
+ return who;
840
893
  }});
841
894
  } catch (err) {
842
895
  log.warn?.(`[booking] online calls unavailable on this CMS: ${err?.message || err}`);
@@ -1,7 +1,7 @@
1
1
  {
2
2
  "name": "booking",
3
3
  "displayName": "Booking",
4
- "version": "1.5.0",
4
+ "version": "1.6.0",
5
5
  "tier": "free",
6
6
  "description": "Let people book appointments with you or your team, and rooms or equipment, from a booking page on your site. Opening hours, days off and buffers; confirmation and reminder emails with a link to change or cancel; a diary in the admin. Book straight from a contact or an email.",
7
7
  "author": "Domma CMS",
@@ -20,7 +20,7 @@ import {callWindow, whoIsCalling} from './lib/call.js';
20
20
  import {dayLabel, lengthLabel} from './public/lib/page-format.js';
21
21
 
22
22
  const TEMPLATE = path.join(fileURLToPath(new URL('.', import.meta.url)), 'templates', 'booking.html');
23
- export const ASSET_V = '1.3.1';
23
+ export const ASSET_V = '1.6.0';
24
24
 
25
25
  /** Which page a path is, under the booking page's base. */
26
26
  export function matchBookingPath(pathname, base) {
@@ -128,7 +128,7 @@ export default async function bookingPublic(fastify) {
128
128
  */
129
129
  pages.call = async function call(request, reply, s, {room}) {
130
130
  const q = request.query || {};
131
- const cred = {b: String(q.b || '').slice(0, 64), ...(q.h ? {h: String(q.h).slice(0, 128)} : {t: String(q.t || '').slice(0, 128)})};
131
+ const cred = {b: String(q.b || '').slice(0, 64), ...(q.h ? {h: String(q.h).slice(0, 128)} : q.g ? {g: String(q.g).slice(0, 128)} : {t: String(q.t || '').slice(0, 128)})};
132
132
  const b = await getBooking(cred.b).catch(() => null);
133
133
  const who = whoIsCalling(b, room, cred);
134
134
  const head = `<header class="bk-hero"><p class="bk-crumbs"><a href="${esc(s.bookingPath)}">${esc(s.pageTitle || 'Book')}</a></p>`;
@@ -136,11 +136,11 @@ export default async function bookingPublic(fastify) {
136
136
  return send(reply, request, s, {page: 'call', title: `Call - ${s.pageTitle || 'Book'}`, urlPath: `${s.bookingPath}/call/${room}`, noindex: true,
137
137
  body: `${head}<h1>This call link is not valid</h1></header><p class="bk-note">The booking may have been cancelled or moved. Check the latest email we sent you.</p>`
138
138
  // A reload of the bare address (the call screen took the link out of the address bar): back to it.
139
- + (cred.t || cred.h ? '' : RECOVER_CALL)});
139
+ + (cred.t || cred.h || cred.g ? '' : RECOVER_CALL)});
140
140
  }
141
141
  const {closesAt} = callWindow(b);
142
142
  const cfg = {api: '/api/plugins/booking/call', room, cred, title: `${b.serviceName}${who.host ? `: ${b.name}` : b.bookableName ? ` with ${b.bookableName}` : ''}`,
143
- askName: false, recordingAllowed: who.recordingAllowed, closesAt};
143
+ askName: who.invited === true, recordingAllowed: who.recordingAllowed, closesAt};
144
144
  return send(reply, request, s, {page: 'call', title: `${b.serviceName} - online`, urlPath: `${s.bookingPath}/call/${room}`, noindex: true,
145
145
  body: `<link rel="stylesheet" href="/plugins/_lib/public/call/call.css?v=${ASSET_V}">`
146
146
  + `<div data-dm-call='${esc(JSON.stringify(cfg))}'><noscript><p class="bk-note">The call needs JavaScript switched on.</p></noscript></div>`
@@ -506,3 +506,117 @@ test('an early-opened call lasts two hours from the click, not all the way to a
506
506
  const soon = {...b, callOpenedAt: '2031-01-10T09:00:00.000Z'};
507
507
  assert.deepEqual(callWindow(soon, at('2031-01-10T09:05:00.000Z')), {opensAt: '2031-01-10T09:00:00.000Z', closesAt: '2031-01-10T12:00:00.000Z'}, 'joined up when they meet');
508
508
  });
509
+
510
+ test('1.6 Add people: the host adds a team member and an address; each gets their own &g= pass (never the customer token or host key); they knock', async () => {
511
+ const reg = bookingRegistry();
512
+ reg.register('test-online', {rules: (s) => (s?.slug === 'team-call' ? {online: true} : {})});
513
+ const {createUser, deleteUser} = await import('../../../server/services/users.js');
514
+ await (await import('../../../server/services/roles.js')).load();
515
+ const {setInviteMailer, resetInviteLimits} = await import('../../../server/services/callInvites.js');
516
+ const {whoIsCalling} = await import('../lib/call.js');
517
+ const tag = Math.random().toString(36).slice(2, 7);
518
+ const tess = await createUser({name: `Tess Host ${tag}`, email: `tess-${tag}@example.test`, password: 'password123', role: 'user'});
519
+ const mate = await createUser({name: `Milo Mate ${tag}`, email: `milo-${tag}@example.test`, password: 'password123', role: 'user'});
520
+ const mails = [];
521
+ setInviteMailer(async (m) => { mails.push(m); });
522
+ resetInviteLimits();
523
+ try {
524
+ const all = Object.fromEntries(['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'].map(d => [d, [{from: '00:00', to: '24:00'}]]));
525
+ let r = await call('POST', '/bookables', {name: 'Tess', kind: 'person', hours: all, userId: tess.id});
526
+ const bk = r.body;
527
+ assert.equal(bk.userId, tess.id, 'the person is a user of the site');
528
+ const svc = (await call('POST', '/services', {name: 'Team call', duration: 30, minNoticeMinutes: 0, bookables: [bk.id]})).body;
529
+ const start = new Date(Math.ceil((Date.now() + 5 * 60000) / 60000) * 60000).toISOString();
530
+ r = await call('POST', '/bookings', {serviceId: svc.id, startAt: start, customer: {name: 'Gus Guest', email: 'gus@example.com'}, force: true, notify: false});
531
+ assert.equal(r.status, 201, JSON.stringify(r.body));
532
+ const b = r.body;
533
+ const raw = await reg.services.getBooking(b.id);
534
+ const room = b.hostUrl.split('/call/')[1].split('?')[0];
535
+ const as = (p) => ({'x-call-id': p.id, 'x-call-secret': p.secret});
536
+ const post = (u, payload, headers = {}) => app.inject({method: 'POST', url: u, payload, headers});
537
+ const host = (await post(`/call/${room}/join`, {b: b.id, h: raw.hostKey})).json();
538
+ const guest = (await post(`/call/${room}/join`, {b: b.id, t: raw.token})).json();
539
+ assert.equal(host.canInvite, true);
540
+ assert.equal(guest.canInvite, false);
541
+
542
+ let s = await app.inject({method: 'GET', url: `/call/${room}/people?q=Milo`, headers: as(host)});
543
+ assert.equal(s.statusCode, 200);
544
+ assert.ok(s.json().people.some(p => p.kind === 'user' && p.id === mate.id));
545
+ assert.ok(!(await app.inject({method: 'GET', url: `/call/${room}/people?q=Tess`, headers: as(host)})).json().people.some(p => p.id === tess.id), 'not the host');
546
+ assert.equal((await app.inject({method: 'GET', url: `/call/${room}/people?q=Milo`, headers: as(guest)})).statusCode, 403, 'the customer cannot');
547
+
548
+ r = await post(`/call/${room}/invite`, {people: [{kind: 'user', id: mate.id}, {name: 'Ext Person', email: 'ext@example.com'}], via: {email: true}}, {...as(host), host: 'site.test'});
549
+ assert.equal(r.statusCode, 200, r.body);
550
+ assert.deepEqual(r.json().results.map(x => [x.name, x.emailed]), [[`Milo Mate ${tag}`, true], ['Ext Person', true]]);
551
+ const linkTo = (to) => new URL(mails.find(m => m.to === to).text.match(/https?:\/\/\S+/)[0]);
552
+ const milo = linkTo(mate.email);
553
+ const ext = linkTo('ext@example.com');
554
+ assert.equal(milo.pathname, `/booking/call/${room}`);
555
+ assert.equal(milo.searchParams.get('b'), b.id);
556
+ assert.ok(milo.searchParams.get('g') && !milo.searchParams.get('t') && !milo.searchParams.get('h'));
557
+ assert.notEqual(milo.searchParams.get('g'), ext.searchParams.get('g'));
558
+ for (const u of [milo, ext]) assert.ok(!u.href.includes(raw.token) && !u.href.includes(raw.hostKey), 'no token or host key reused');
559
+
560
+ const kept = await reg.services.getBooking(b.id);
561
+ assert.ok(kept.callExtraKey && kept.callExtraKey !== raw.token && kept.callExtraKey !== raw.hostKey);
562
+ assert.equal((await call('GET', `/bookings/${b.id}`)).body.callExtraKey, undefined, 'never shown in the admin');
563
+
564
+ // They knock with their own name; the customer keeps their place.
565
+ const m1 = (await post(`/call/${room}/join`, {b: b.id, g: milo.searchParams.get('g'), name: 'Milo'})).json();
566
+ const e1 = (await post(`/call/${room}/join`, {b: b.id, g: ext.searchParams.get('g'), name: 'Ext'})).json();
567
+ assert.equal(m1.state, 'waiting');
568
+ assert.equal(e1.state, 'waiting');
569
+ assert.equal(m1.canInvite, false);
570
+ assert.deepEqual((await app.inject({method: 'GET', url: `/call/${room}/poll?since=0`, headers: as(host)})).json().events
571
+ .filter(e => e.type === 'waiting').at(-1).waiting.map(w => w.name), ['Gus Guest', 'Milo', 'Ext']);
572
+ assert.equal((await post(`/call/${room}/join`, {b: b.id, g: 'abcdefgh.AAAAAAAAAAAAAAAAAAAAAAAA', name: 'X'})).statusCode, 404, 'forged');
573
+ const who = whoIsCalling(kept, room, {g: milo.searchParams.get('g')});
574
+ assert.equal(who.invited, true);
575
+ assert.equal(who.host, false);
576
+
577
+ // Copy link: a new pass each time.
578
+ const copy = (await post(`/call/${room}/invite`, {people: []}, {...as(host), host: 'site.test'})).json();
579
+ assert.match(copy.link, new RegExp(`/booking/call/${room}\\?b=${b.id}&g=`));
580
+ for (const p of [host, guest, m1, e1]) await post(`/call/${room}/leave`, {}, as(p));
581
+ await call('DELETE', `/bookings/${b.id}`);
582
+ } finally {
583
+ setInviteMailer(null);
584
+ reg.unregister('test-online');
585
+ for (const u of [tess, mate]) await deleteUser(u.id).catch(() => {});
586
+ }
587
+ });
588
+
589
+ test('1.6 Add people: a person with no account on the site (the host link may be anyone\'s) gets Copy link only - no directory, nothing sent', async () => {
590
+ const reg = bookingRegistry();
591
+ reg.register('test-online', {rules: (s) => (s?.slug === 'outside-call' ? {online: true} : {})});
592
+ const {setInviteMailer} = await import('../../../server/services/callInvites.js');
593
+ const mails = [];
594
+ setInviteMailer(async (m) => { mails.push(m); });
595
+ try {
596
+ const all = Object.fromEntries(['mon', 'tue', 'wed', 'thu', 'fri', 'sat', 'sun'].map(d => [d, [{from: '00:00', to: '24:00'}]]));
597
+ const olga = (await call('POST', '/bookables', {name: 'Olga Outside', kind: 'person', hours: all, email: 'olga@elsewhere.example'})).body;
598
+ const svc = (await call('POST', '/services', {name: 'Outside call', duration: 30, minNoticeMinutes: 0, bookables: [olga.id]})).body;
599
+ const start = new Date(Math.ceil((Date.now() + 5 * 60000) / 60000) * 60000).toISOString();
600
+ const b = (await call('POST', '/bookings', {serviceId: svc.id, startAt: start, customer: {name: 'Gus', email: 'gus2@example.com'}, force: true, notify: false})).body;
601
+ const raw = await reg.services.getBooking(b.id);
602
+ const room = b.hostUrl.split('/call/')[1].split('?')[0];
603
+ const post = (u, payload, headers = {}) => app.inject({method: 'POST', url: u, payload, headers});
604
+ const host = (await post(`/call/${room}/join`, {b: b.id, h: raw.hostKey})).json();
605
+ assert.equal(host.canInvite, true);
606
+ assert.deepEqual(host.inviteOptions, {search: false, typed: false, copyLink: true, external: true});
607
+ const as = {'x-call-id': host.id, 'x-call-secret': host.secret};
608
+ assert.deepEqual((await app.inject({method: 'GET', url: `/call/${room}/people?q=a`, headers: as})).json(), {people: []}, 'no team directory, no contacts');
609
+ for (const person of [{name: 'Spam', email: 'victim@example.com'}, {kind: 'user', id: 'someone'}, {kind: 'contact', id: 'c1'}]) {
610
+ assert.equal((await post(`/call/${room}/invite`, {people: [person], via: {email: true}}, as)).statusCode, 400);
611
+ }
612
+ assert.equal(mails.length, 0, 'nothing sent in the site\'s name');
613
+ const copy = (await post(`/call/${room}/invite`, {people: []}, as)).json();
614
+ assert.match(copy.link, new RegExp(`/booking/call/${room}\\?b=${b.id}&g=`), 'Copy link: their own pass');
615
+ const knock = (await post(`/call/${room}/join`, {b: b.id, g: new URL(copy.link).searchParams.get('g'), name: 'Friend'})).json();
616
+ assert.equal(knock.state, 'waiting');
617
+ await call('DELETE', `/bookings/${b.id}`);
618
+ } finally {
619
+ setInviteMailer(null);
620
+ reg.unregister('test-online');
621
+ }
622
+ });
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "note": "Written by `make sync-free-tier` in dcms-marketplace. Do not edit these plugins here - edit them there and sync.",
3
- "source": "dcms-marketplace@ba58fcb",
3
+ "source": "dcms-marketplace@f582d78",
4
4
  "plugins": {
5
5
  "blog": {
6
6
  "version": "1.9.4",
@@ -73,9 +73,9 @@
73
73
  }
74
74
  },
75
75
  "booking": {
76
- "version": "1.5.0",
76
+ "version": "1.6.0",
77
77
  "files": {
78
- "CLAUDE.md": "258f342c1acdd1d7562a3409bc7c8622828308c243549255d2ac75832be6d84e",
78
+ "CLAUDE.md": "9bcab41375a2aaecbdd332766ceca5b18bf9dafa99d4c8315549e38033c1f5c5",
79
79
  "admin/css/index.css": "4f7731921c0c5038cb1566a6ab2d43ffc35bf9b161e58f87a0cff423e81b47fa",
80
80
  "admin/lib/details.js": "8b37536bfa9ccfcc5265cc957ba8266d3f5f777133e7c0946ae49540f1c80e3c",
81
81
  "admin/lib/diary-layout.js": "d818c79001fb9a9ecc15c96239ba961b0a1d98b9e8fce903eefb57d85adf2a73",
@@ -93,15 +93,15 @@
93
93
  "collections/booking-bookings.json": "fa0b15cc81b4a2114734a0bc8adc4b4d25049d16cf62549b83634cdbeb543b52",
94
94
  "collections/booking-services.json": "cecb500a9215a0eba894e6b4c9ae78f5fbeb209830d07d47050c56c75143f66a",
95
95
  "config.js": "5565fd627481cf809322257cc300a7cbb7ccbf6cae076b4cf8a7453fcf554bd3",
96
- "docs/guide.md": "dd6a8250ac25914dc8ac6922167c92e815f69f464d61b424dfab42770af59626",
96
+ "docs/guide.md": "186e2419f44ff5b13dfbcea709dc8616f3817c260717839a9fa51834ab73f718",
97
97
  "inject/body.html": "ab55ce945eec2302eb0c836a4626cffa5e091d050c6a59c6f2af332b05570777",
98
- "lib/call.js": "7dfa0ae60be87d8f7f26b4360d5c1144322abe66eb61b233ed812e35bb8e59bd",
98
+ "lib/call.js": "87c11b8531e1a6d247d52d4e03ea8e5aa06a1af49cbd7cbd0af97e2586237f7b",
99
99
  "lib/extensions.js": "8cec28ff8685daad8083b23f034f1c3bf82fd2322943dd19d46570ccdc311105",
100
100
  "lib/mail.js": "cad8f409df62d6e42f0f4024ec364bbe018a62b842fa3f7c38786fd4e7f92408",
101
- "lib/store.js": "270161f907c68de39d32758931e86c290582524af4650e722c2c3be209acc875",
102
- "plugin.js": "afe2c3dd15c69515828f513478bb40f2ea3788a8b67ace571c01198acd79dc3b",
103
- "plugin.json": "be0576b42a5fa60c04da8131a5d20113c020eb8088db228892f0715e9b1808f8",
104
- "plugin.public.js": "5f2dc4f01202b16ae804364a33c547509d1fa4026eadebac440730234aa266b5",
101
+ "lib/store.js": "7aa7967e4694e5a3b85378f60302e5a47394b1d90c7dd86e09578156f1c13e03",
102
+ "plugin.js": "90424e681de6c086dcc316c734ddb907c7572e452949ced4cc756205a4798f6e",
103
+ "plugin.json": "8f7a5b1ed1e0bbfb1e933906b2c63bff4fbfc62868244b2077b1090d87fc3cc0",
104
+ "plugin.public.js": "4d5b4e8ad6943182ee3b6c6e6f6762a2d3f676d8d7ee363fcb07bd47d3d6532e",
105
105
  "public/booking.css": "8b88989fdd04e50efc1688a2f32fe2e422299d7d13b81bb8fce0cc162fa99df6",
106
106
  "public/booking.js": "7dfc3d205696967013886567f67a867443c2e370bac29019d69911d695fdbade",
107
107
  "public/lib/clean.js": "d591be9ea8462705b7bc8806670027fdd4d6d4764777da08e91a010d27ec1651",
@@ -110,7 +110,7 @@
110
110
  "public/lib/time.js": "48e13a23e3d7a4916e2097e8f87c03a87d37b71c4ef037a5a16143c6a4185c41",
111
111
  "templates/booking.html": "a4995de46dfdab7e692b56f21347f29c76320cf3b0477889dc3c5d14e0b6dcd7",
112
112
  "tests/admin-diary.test.js": "4e34b447d62b41272ceec49bad9d491ae00dd04f4760d8a7a3046be1912f4abe",
113
- "tests/api.test.js": "937a79e5af9af6b3f12b7ae15dabc470a91c7fe8ca03e3f7f261e4e641dfe3dc",
113
+ "tests/api.test.js": "43ae40477a43a03d21096038f317d745bc08cd51767c97548bf0601e99ebdd97",
114
114
  "tests/public.test.js": "0b7b626d60df5a3d73d7c0077b73f59b62a9fded98ce84a8d93cd32cd0333159",
115
115
  "tests/pure.test.js": "8ba0c8f76b4c9efcc422f39ff29509612544d81f760025a4b9a5594af14e7bcf"
116
116
  }
@@ -1 +1 @@
1
- var w=Object.defineProperty;var v=(f,e,i)=>e in f?w(f,e,{enumerable:!0,configurable:!0,writable:!0,value:i}):f[e]=i;var p=(f,e,i)=>v(f,typeof e!="symbol"?e+"":e,i);import{CanvasManager as M}from"./core/canvas.js";import{PhysicsEngine as P,updateMovingParticle as b,updateParticlePhysics as C}from"./core/physics.js";import{createParticle as z}from"./core/particles.js";const h=class h{static isDateInRange(e,i,s,o){const r=new Date,n=r.getMonth()+1,c=r.getDate();return s<e?n===e&&c>=i||n>e||n<e&&n<=s||n===s&&c<=o:(n>e||n===e&&c>=i)&&(n<s||n===s&&c<=o)}static getCurrentTheme(){for(const[e,i]of Object.entries(h.themes)){const[[s,o],[r,n]]=i.dates;if(h.isDateInRange(s,o,r,n))return e}return null}static isCelebrationSeason(){return h.getCurrentTheme()!==null}static getThemes(){return h.themes}constructor(e={}){this.options={theme:e.theme||"auto",intensity:e.intensity||"medium",enabled:e.enabled!==void 0?e.enabled:!0,zIndex:e.zIndex||999,overrides:e.overrides||{}},this.canvasManager=null,this.physicsEngine=null,this.themeModule=null,this.currentTheme=null,this.particles=[],this.specialParticles=[],this.animationFrame=null,this.lastTime=0,this.running=!1,this.initialized=!1}async init(){if(this.initialized){console.warn("[Celebrations] Already initialized");return}console.log("[Celebrations] Initializing...");let e=this.options.theme;if(e==="auto"&&(e=h.getCurrentTheme(),!e)){console.log("[Celebrations] No active celebration detected");return}if(!h.themes[e]){console.error(`[Celebrations] Unknown theme: ${e}`);return}this.currentTheme=e,console.log(`[Celebrations] Loading theme: ${e}`);try{const s=await import(h.themes[e].module);this.themeModule=s.default,console.log(`[Celebrations] Theme loaded: ${this.themeModule.displayName}`)}catch(i){console.error(`[Celebrations] Failed to load theme module: ${i.message}`);return}this.canvasManager=new M({canvasId:"celebrations-canvas",zIndex:this.options.zIndex}),this.canvasManager.create(),this.physicsEngine=new P({gustInterval:[5e3,15e3],gustStrength:[-2,2]}),window.addEventListener("resize",()=>{this.canvasManager.resize(),this.resetParticles()}),this.resetParticles(),this.initialized=!0,console.log("[Celebrations] Initialized successfully"),this.options.enabled&&this.start()}resetParticles(){const e=Object.assign({},this.themeModule.intensityConfig[this.options.intensity]),i=a=>String(a).toLowerCase().replace(/[^a-z0-9]/g,""),s=[...Object.keys(e),...this.themeModule.decorationKeys||[]],o=a=>s.find(l=>i(l)===i(a))||a,r=a=>["true","1","yes","on"].includes(String(a).toLowerCase()),n=this.themeModule.optInDecorations||{},c=Object.entries(this.options.overrides).some(([a,l])=>["snowonly","fallingonly"].includes(i(a))&&r(l));for(const a of Object.values(n))e[a.chance]=0;for(const[a,l]of Object.entries(this.options.overrides)){if((i(a)==="snowonly"||i(a)==="fallingonly")&&r(l)){for(const g of this.themeModule.decorationKeys||[])e[g]=0;continue}const y=Object.keys(n).find(g=>i(g)===i(a));if(y&&!c&&r(l)){const g=n[y];e[g.chance]=g.value}l==="off"&&(e[o(a)]=0)}this.config=e;const t=this.canvasManager.canvas.width,d=this.canvasManager.canvas.height;this.particles=[],this.specialParticles=[];const m=e.initialParticleRatio!==void 0?e.initialParticleRatio:1,u=Math.floor(e.count*m);if(this.themeModule.createFallingParticle)for(let a=0;a<u;a++){const l=this.themeModule.createFallingParticle(t,d,e);l.y=Math.random()*d,this.particles.push(l)}else for(let a=0;a<u;a++){const l=z(e,t,d);l.y=Math.random()*d,this.particles.push(l)}if(this.themeModule.createInitialDecorations){const a=this.themeModule.createInitialDecorations(t,d,e);this.specialParticles.push(...a),console.log(`[Celebrations] Created ${a.length} decorations`)}console.log(`[Celebrations] Created ${u}/${e.count} particles (${this.options.intensity}, ${Math.round(m*100)}% initial)`)}start(){if(!this.initialized){console.warn("[Celebrations] Cannot start - not initialized");return}if(this.running){console.warn("[Celebrations] Already running");return}console.log("[Celebrations] Starting animation"),this.running=!0,this.lastTime=performance.now(),this.animate()}pause(){this.running&&(console.log("[Celebrations] Pausing animation"),this.running=!1,this.animationFrame&&(cancelAnimationFrame(this.animationFrame),this.animationFrame=null))}resume(){this.running||this.start()}destroy(){console.log("[Celebrations] Destroying effect"),this.pause(),this.canvasManager&&this.canvasManager.canvas&&this.canvasManager.canvas.remove(),this.particles=[],this.specialParticles=[],this.canvasManager=null,this.physicsEngine=null,this.themeModule=null,this.initialized=!1}setIntensity(e){if(!["light","medium","heavy"].includes(e)){console.error(`[Celebrations] Invalid intensity: ${e}`);return}console.log(`[Celebrations] Changing intensity to: ${e}`),this.options.intensity=e,this.resetParticles()}async setTheme(e){console.log(`[Celebrations] Changing theme to: ${e}`);const i=this.running;this.pause(),await new Promise(s=>setTimeout(s,20)),this.initialized=!1,this.canvasManager&&this.canvasManager.clear(),this.particles=[],this.specialParticles=[],this.canvasManager&&this.canvasManager.clear(),this.options.theme=e,await this.init(),i&&this.start()}animate(){if(!this.running)return;const e=performance.now(),i=e-this.lastTime;this.lastTime=e,this.canvasManager.clear();const s=this.canvasManager.ctx,o=this.canvasManager.canvas.width,r=this.canvasManager.canvas.height,n=this.config;this.physicsEngine.updateWind(e);const c=this.physicsEngine.getWindForce();if(this.particles.length<n.count&&Math.random()<.05&&this.themeModule.createFallingParticle){const t=this.themeModule.createFallingParticle(o,r,n);(t.y===void 0||t.y<0)&&(t.y=-20),this.particles.push(t)}if(this.particles.forEach((t,d)=>{if(C(t,i,c),t.y>r+50&&(t.y=-20,t.x=Math.random()*o),t.x<-50&&(t.x=o+50),t.x>o+50&&(t.x=-50),this.themeModule.drawSnowflake&&t.type==="snowflake")this.themeModule.drawSnowflake(s,t);else if(this.themeModule["draw"+this.capitalizeFirst(t.type)]){const m="draw"+this.capitalizeFirst(t.type);this.themeModule[m](s,t,e)}else s.save(),s.globalAlpha=t.opacity,s.fillStyle=this.themeModule.colors?.primary||"#ffffff",s.beginPath(),s.arc(t.x,t.y,t.size,0,Math.PI*2),s.fill(),s.restore()}),this.themeModule.updateSpecialParticles&&this.themeModule.updateSpecialParticles(this.specialParticles,i,o,r),this.specialParticles=this.specialParticles.filter(t=>{if(!t.active)return!1;if(!t.static){b(t,i,e);const u=t.type==="train"?600:200;if(t.x<-u||t.x>o+u)return t.active=!1,!1}if(!isFinite(t.x)||!isFinite(t.y)||t.size&&!isFinite(t.size))return console.warn("[Celebrations] Invalid particle values, removing:",t.type),t.active=!1,!1;const m="draw"+this.capitalizeFirst(t.type);return this.themeModule[m]&&this.themeModule[m](s,t,e),!0}),this.themeModule.spawnSpecialParticle){const t=this.themeModule.spawnSpecialParticle(this.specialParticles,o,r,n);t&&this.specialParticles.push(t)}this.themeModule.drawGlobalEffects&&this.themeModule.drawGlobalEffects(s,e,o,r),this.animationFrame=requestAnimationFrame(()=>this.animate())}capitalizeFirst(e){if(!e)return"";const i=e.replace(/-([a-z])/g,(s,o)=>o.toUpperCase());return i.charAt(0).toUpperCase()+i.slice(1)}};p(h,"themes",{christmas:{module:"./themes/christmas.js?v=20260930-snowman",dates:[[12,1],[1,1]],displayName:"Christmas",emoji:"\u{1F384}"},halloween:{module:"./themes/halloween.js?v=20260930-snow",dates:[[10,26],[10,31]],displayName:"Halloween",emoji:"\u{1F383}"},valentines:{module:"./themes/valentines.js?v=20260930-snow",dates:[[2,9],[2,14]],displayName:"Valentine's Day",emoji:"\u{1F495}"},"guy-fawkes":{module:"./themes/guy-fawkes.js?v=20260930-snow",dates:[[11,1],[11,5]],displayName:"Guy Fawkes Night",emoji:"\u{1F386}"},"st-patricks":{module:"./themes/st-patricks.js?v=20260930-snow",dates:[[3,12],[3,17]],displayName:"St Patrick's Day",emoji:"\u2618\uFE0F"},"st-andrews":{module:"./themes/st-andrews.js?v=20260930-snow",dates:[[11,25],[11,30]],displayName:"St Andrew's Day",emoji:"\u{1F3F4}\u{E0067}\u{E0062}\u{E0073}\u{E0063}\u{E0074}\u{E007F}"},"st-davids":{module:"./themes/st-davids.js?v=20260930-snow",dates:[[2,24],[3,1]],displayName:"St David's Day",emoji:"\u{1F3F4}\u{E0067}\u{E0062}\u{E0077}\u{E006C}\u{E0073}\u{E007F}"},"st-georges":{module:"./themes/st-georges.js?v=20260930-snow",dates:[[4,18],[4,23]],displayName:"St George's Day",emoji:"\u{1F3F4}\u{E0067}\u{E0062}\u{E0065}\u{E006E}\u{E0067}\u{E007F}"}});export let CelebrationsEffect=h;export default CelebrationsEffect;
1
+ var w=Object.defineProperty;var b=(u,e,i)=>e in u?w(u,e,{enumerable:!0,configurable:!0,writable:!0,value:i}):u[e]=i;var v=(u,e,i)=>b(u,typeof e!="symbol"?e+"":e,i);import{resolveCelebrationTraits as M}from"./trait-data.mjs";import{CanvasManager as P}from"./core/canvas.js";import{PhysicsEngine as C,updateMovingParticle as z,updateParticlePhysics as j}from"./core/physics.js";import{createParticle as F}from"./core/particles.js";const c=class c{static isDateInRange(e,i,s,o){const r=new Date,n=r.getMonth()+1,d=r.getDate();return s<e?n===e&&d>=i||n>e||n<e&&n<=s||n===s&&d<=o:(n>e||n===e&&d>=i)&&(n<s||n===s&&d<=o)}static getCurrentTheme(){for(const[e,i]of Object.entries(c.themes)){const[[s,o],[r,n]]=i.dates;if(c.isDateInRange(s,o,r,n))return e}return null}static isCelebrationSeason(){return c.getCurrentTheme()!==null}static getThemes(){return c.themes}constructor(e={}){this.options={theme:e.theme||"auto",intensity:e.intensity||"medium",enabled:e.enabled!==void 0?e.enabled:!0,zIndex:e.zIndex||999,overrides:e.overrides||{}},this.canvasManager=null,this.physicsEngine=null,this.themeModule=null,this.currentTheme=null,this.particles=[],this.specialParticles=[],this.animationFrame=null,this.lastTime=0,this.running=!1,this.initialized=!1}async init(){if(this.initialized){console.warn("[Celebrations] Already initialized");return}console.log("[Celebrations] Initializing...");let e=this.options.theme;if(e==="auto"&&(e=c.getCurrentTheme(),!e)){console.log("[Celebrations] No active celebration detected");return}if(!c.themes[e]){console.error(`[Celebrations] Unknown theme: ${e}`);return}this.currentTheme=e,console.log(`[Celebrations] Loading theme: ${e}`);try{const s=await import(c.themes[e].module);this.themeModule=s.default,console.log(`[Celebrations] Theme loaded: ${this.themeModule.displayName}`)}catch(i){console.error(`[Celebrations] Failed to load theme module: ${i.message}`);return}this.canvasManager=new P({canvasId:"celebrations-canvas",zIndex:this.options.zIndex}),this.canvasManager.create(),this.physicsEngine=new C({gustInterval:[5e3,15e3],gustStrength:[-2,2]}),window.addEventListener("resize",()=>{this.canvasManager.resize(),this.resetParticles()}),this.resetParticles(),this.initialized=!0,console.log("[Celebrations] Initialized successfully"),this.options.enabled&&this.start()}isParticleEnabled(e){return!this.disabledTypes?.has(e.type)}resetParticles(){const e=Object.assign({},this.themeModule.intensityConfig[this.options.intensity]),i=a=>String(a).toLowerCase().replace(/[^a-z0-9]/g,""),s=[...Object.keys(e),...this.themeModule.decorationKeys||[]],o=a=>s.find(l=>i(l)===i(a))||a,r=a=>["true","1","yes","on"].includes(String(a).toLowerCase()),n=this.themeModule.optInDecorations||{},d=Object.entries(this.options.overrides).some(([a,l])=>["snowonly","fallingonly"].includes(i(a))&&r(l));for(const a of Object.values(n))e[a.chance]=0;for(const[a,l]of Object.entries(this.options.overrides)){if((i(a)==="snowonly"||i(a)==="fallingonly")&&r(l)){for(const m of this.themeModule.decorationKeys||[])e[m]=0;continue}const p=Object.keys(n).find(m=>i(m)===i(a));if(p&&!d&&r(l)){const m=n[p];e[m.chance]=m.value}l==="off"&&(e[o(a)]=0)}const t=M(this.themeModule.name,this.options.overrides);Object.assign(e,t.config),this.disabledTypes=t.disabledTypes,this.disabledGlobals=t.disabledGlobals,this.config=e;const f=this.canvasManager.canvas.width,h=this.canvasManager.canvas.height;this.particles=[],this.specialParticles=[];const g=e.initialParticleRatio!==void 0?e.initialParticleRatio:1,y=Math.floor(e.count*g);if(this.themeModule.createFallingParticle)for(let a=0;a<y;a++){const l=this.themeModule.createFallingParticle(f,h,e);l.y=Math.random()*h,this.isParticleEnabled(l)&&this.particles.push(l)}else for(let a=0;a<y;a++){const l=F(e,f,h);l.y=Math.random()*h,this.isParticleEnabled(l)&&this.particles.push(l)}if(this.themeModule.createInitialDecorations){const a=this.themeModule.createInitialDecorations(f,h,e);this.specialParticles.push(...a.filter(l=>this.isParticleEnabled(l))),console.log(`[Celebrations] Created ${a.length} decorations`)}console.log(`[Celebrations] Created ${y}/${e.count} particles (${this.options.intensity}, ${Math.round(g*100)}% initial)`)}start(){if(!this.initialized){console.warn("[Celebrations] Cannot start - not initialized");return}if(this.running){console.warn("[Celebrations] Already running");return}console.log("[Celebrations] Starting animation"),this.running=!0,this.lastTime=performance.now(),this.animate()}pause(){this.running&&(console.log("[Celebrations] Pausing animation"),this.running=!1,this.animationFrame&&(cancelAnimationFrame(this.animationFrame),this.animationFrame=null))}resume(){this.running||this.start()}destroy(){console.log("[Celebrations] Destroying effect"),this.pause(),this.canvasManager&&this.canvasManager.canvas&&this.canvasManager.canvas.remove(),this.particles=[],this.specialParticles=[],this.canvasManager=null,this.physicsEngine=null,this.themeModule=null,this.initialized=!1}setIntensity(e){if(!["light","medium","heavy"].includes(e)){console.error(`[Celebrations] Invalid intensity: ${e}`);return}console.log(`[Celebrations] Changing intensity to: ${e}`),this.options.intensity=e,this.resetParticles()}async setTheme(e){console.log(`[Celebrations] Changing theme to: ${e}`);const i=this.running;this.pause(),await new Promise(s=>setTimeout(s,20)),this.initialized=!1,this.canvasManager&&this.canvasManager.clear(),this.particles=[],this.specialParticles=[],this.canvasManager&&this.canvasManager.clear(),this.options.theme=e,await this.init(),i&&this.start()}animate(){if(!this.running)return;const e=performance.now(),i=e-this.lastTime;this.lastTime=e,this.canvasManager.clear();const s=this.canvasManager.ctx,o=this.canvasManager.canvas.width,r=this.canvasManager.canvas.height,n=this.config;this.physicsEngine.updateWind(e);const d=this.physicsEngine.getWindForce();if(this.particles.length<n.count&&Math.random()<.05&&this.themeModule.createFallingParticle){const t=this.themeModule.createFallingParticle(o,r,n);(t.y===void 0||t.y<0)&&(t.y=-20),this.isParticleEnabled(t)&&this.particles.push(t)}if(this.particles.forEach((t,f)=>{if(j(t,i,d),t.y>r+50&&(t.y=-20,t.x=Math.random()*o),t.x<-50&&(t.x=o+50),t.x>o+50&&(t.x=-50),this.themeModule.drawSnowflake&&t.type==="snowflake")this.themeModule.drawSnowflake(s,t);else if(this.themeModule["draw"+this.capitalizeFirst(t.type)]){const h="draw"+this.capitalizeFirst(t.type);this.themeModule[h](s,t,e)}else s.save(),s.globalAlpha=t.opacity,s.fillStyle=this.themeModule.colors?.primary||"#ffffff",s.beginPath(),s.arc(t.x,t.y,t.size,0,Math.PI*2),s.fill(),s.restore()}),this.themeModule.updateSpecialParticles&&this.themeModule.updateSpecialParticles(this.specialParticles,i,o,r),this.specialParticles=this.specialParticles.filter(t=>{if(!t.active||!this.isParticleEnabled(t))return!1;if(!t.static){z(t,i,e);const g=t.type==="train"?600:200;if(t.x<-g||t.x>o+g)return t.active=!1,!1}if(!isFinite(t.x)||!isFinite(t.y)||t.size&&!isFinite(t.size))return console.warn("[Celebrations] Invalid particle values, removing:",t.type),t.active=!1,!1;const h="draw"+this.capitalizeFirst(t.type);return this.themeModule[h]&&this.themeModule[h](s,t,e),!0}),this.themeModule.spawnSpecialParticle){const t=this.themeModule.spawnSpecialParticle(this.specialParticles,o,r,n);t&&this.isParticleEnabled(t)&&this.specialParticles.push(t)}this.themeModule.drawGlobalEffects&&!this.disabledGlobals?.has("lightning")&&this.themeModule.drawGlobalEffects(s,e,o,r),this.animationFrame=requestAnimationFrame(()=>this.animate())}capitalizeFirst(e){if(!e)return"";const i=e.replace(/-([a-z])/g,(s,o)=>o.toUpperCase());return i.charAt(0).toUpperCase()+i.slice(1)}};v(c,"themes",{christmas:{module:"./themes/christmas.js?v=20261001-options",dates:[[12,1],[1,1]],displayName:"Christmas",emoji:"\u{1F384}"},halloween:{module:"./themes/halloween.js?v=20260930-snow",dates:[[10,26],[10,31]],displayName:"Halloween",emoji:"\u{1F383}"},valentines:{module:"./themes/valentines.js?v=20260930-snow",dates:[[2,9],[2,14]],displayName:"Valentine's Day",emoji:"\u{1F495}"},"guy-fawkes":{module:"./themes/guy-fawkes.js?v=20260930-snow",dates:[[11,1],[11,5]],displayName:"Guy Fawkes Night",emoji:"\u{1F386}"},"st-patricks":{module:"./themes/st-patricks.js?v=20260930-snow",dates:[[3,12],[3,17]],displayName:"St Patrick's Day",emoji:"\u2618\uFE0F"},"st-andrews":{module:"./themes/st-andrews.js?v=20260930-snow",dates:[[11,25],[11,30]],displayName:"St Andrew's Day",emoji:"\u{1F3F4}\u{E0067}\u{E0062}\u{E0073}\u{E0063}\u{E0074}\u{E007F}"},"st-davids":{module:"./themes/st-davids.js?v=20260930-snow",dates:[[2,24],[3,1]],displayName:"St David's Day",emoji:"\u{1F3F4}\u{E0067}\u{E0062}\u{E0077}\u{E006C}\u{E0073}\u{E007F}"},"st-georges":{module:"./themes/st-georges.js?v=20260930-snow",dates:[[4,18],[4,23]],displayName:"St George's Day",emoji:"\u{1F3F4}\u{E0067}\u{E0062}\u{E0065}\u{E006E}\u{E0067}\u{E007F}"}});export let CelebrationsEffect=c;export default CelebrationsEffect;
@@ -0,0 +1 @@
1
+ export function drawIllustratedButterfly(o,e){if(![e.x,e.y,e.size].every(Number.isFinite)||e.size<=0)return;const u=.12+Math.abs(Math.sin(e.flapPhase||0))*.88;o.save(),o.translate(e.x,e.y),o.rotate((e.heading??-Math.PI/2)+Math.PI/2),o.scale(e.size/2.2,e.size/2.2),o.globalAlpha*=e.alpha??1,o.lineCap="round",o.lineJoin="round";const t=(r,a,i,d=.025)=>{o.beginPath();for(const[s,...n]of r)o[s](...n);a&&(o.fillStyle=a,o.fill()),i&&(o.strokeStyle=i,o.lineWidth=d,o.stroke())},l=(r,a,i,d,s)=>{o.beginPath(),o.ellipse(r,a,i,d,0,0,Math.PI*2),o.fillStyle=s,o.fill()};for(const r of[-1,1]){o.save(),o.scale(r*u,1);const a=o.createLinearGradient(0,0,1.1,-.8);a.addColorStop(0,"#504354"),a.addColorStop(.26,e.colourUpper||"#d891b4"),a.addColorStop(1,"#f5dabc");const i=o.createLinearGradient(0,0,.9,.7);i.addColorStop(0,"#63505d"),i.addColorStop(.35,e.colourLower||e.colourUpper||"#bd8fb3"),i.addColorStop(1,"#e8cbb6"),t([["moveTo",.045,-.04],["bezierCurveTo",.3,-.52,.73,-1.02,1.04,-.83],["bezierCurveTo",1.18,-.67,.91,-.13,.63,.03],["quadraticCurveTo",.32,.17,.045,-.04],["closePath"]],a,"#5b4a59",.035),t([["moveTo",.05,0],["bezierCurveTo",.52,-.05,.98,.2,.84,.54],["quadraticCurveTo",.74,.73,.52,.68],["quadraticCurveTo",.31,.87,.15,.54],["quadraticCurveTo",.03,.28,.05,0],["closePath"]],i,"#5b4a59",.035);for(const[d,s]of[[1.03,-.73],[.93,-.38],[.73,-.08],[.8,.37],[.61,.64],[.28,.67]])t([["moveTo",.08,0],["quadraticCurveTo",d*.53,s*.8,d,s]],null,"rgba(78,54,67,0.45)",.018);for(const[d,s,n]of[[.89,-.66,.04],[.87,-.47,.034],[.77,-.29,.03],[.69,.38,.035],[.55,.54,.03],[.35,.55,.028]])l(d,s,n,n*1.2,"#fff0d5");l(.48,-.33,.11,.14,"#69506c"),l(.48,-.33,.058,.075,"#e8c68f"),l(.45,.34,.085,.105,"rgba(90,65,86,0.65)"),o.restore()}l(0,.16,.052,.37,"#493f49"),l(0,-.12,.073,.15,"#5c4d56"),l(0,-.31,.072,.075,"#443a45");for(const r of[-1,1])t([["moveTo",r*.025,-.35],["quadraticCurveTo",r*.1,-.61,r*.2,-.56]],null,"#665362",.018),l(r*.2,-.56,.02,.025,"#d6b391");o.restore()}
@@ -0,0 +1 @@
1
+ import{path as l,ellipse as n,gradient as u,rect as s}from"./christmas-drawing.js";function f(o,r,i=!1){return![r.x,r.y,r.size].every(Number.isFinite)||r.size<=0?!1:(o.save(),o.translate(r.x,r.y),o.scale(r.size*(i&&r.vx<0?-1:1),r.size),o.rotate(r.rotation||0),o.globalAlpha*=r.opacity??1,o.lineCap="round",o.lineJoin="round",!0)}function d(o,r,i,e,a=e){n(o,r,i,e,a,"#20252c"),n(o,r-e*.25,i-a*.25,e*.25,a*.2,"#58616a")}export function drawGhost(o,r,i=r.time||0){if(!f(o,r))return;const e=Math.sin(i*.004)*.08;o.shadowColor="#a7c1c5",o.shadowBlur=5,l(o,[["moveTo",-.57,-.37],["bezierCurveTo",-.57,-1.22,.6,-1.2,.58,-.35],["quadraticCurveTo",.56,.08,.86,.4+e],["quadraticCurveTo",.56,.37,.48,.57],["quadraticCurveTo",.27,.4,.11,.65-e],["quadraticCurveTo",-.13,.41,-.32,.6],["quadraticCurveTo",-.44,.36,-.79,.44-e],["quadraticCurveTo",-.55,.05,-.57,-.37],["closePath"]],u(o,-1,.7,["#fff7df","#dfe9e8","#91aab8"])),o.shadowBlur=0;for(const a of[-1,1])l(o,[["moveTo",a*.37,-.05],["quadraticCurveTo",a*.29,.29,a*.39,.42]],null,"#a7bac2",.02);d(o,-.18,-.44,.065,.1),d(o,.2,-.47,.065,.1),n(o,.045,-.13,.07,.085,"#566779"),n(o,-.32,-.23,.09,.04,"rgba(218,155,151,0.28)"),o.restore()}export function drawBat(o,r,i=r.time||0){if(!f(o,r,!0))return;const e=Math.sin(i*.01)*.35;for(const a of[-1,1]){o.save(),o.scale(a,1),o.rotate(e),l(o,[["moveTo",.13,-.14],["quadraticCurveTo",.7,-.88,1.55,-.38],["quadraticCurveTo",1.06,-.26,1.27,.2],["quadraticCurveTo",.91,-.06,.75,.39],["quadraticCurveTo",.5,.1,.22,.52],["lineTo",.1,.18],["closePath"]],u(o,-.7,.6,["#7b657e","#3d364e","#202939"]),"#967f91",.018);for(const[T,v]of[[1.4,-.37],[1.22,.13],[.75,.31]])l(o,[["moveTo",.14,-.1],["quadraticCurveTo",T*.56,-.1,T,v]],null,"#8b7188",.015);o.restore()}n(o,0,.09,.22,.36,"#443a4b");for(const a of[-1,1])l(o,[["moveTo",a*.18,-.18],["lineTo",a*.21,-.62],["quadraticCurveTo",a*.02,-.46,a*.04,-.18]],"#514454");n(o,0,-.22,.23,.21,"#514454");for(const a of[-.085,.085])n(o,a,-.24,.035,.04,"#e8ba7a"),n(o,a,-.24,.013,.03,"#26303c");l(o,[["moveTo",-.06,-.06],["quadraticCurveTo",0,-.015,.06,-.06]],null,"#c4a5a1",.018),o.restore()}function b(o,r,i,e){if(f(o,r)){n(o,0,.8,1.1,.13,"rgba(10,15,27,0.25)");for(const a of[-1,1])for(const[T,v,m]of[[.56,.44,.68],[.29,.42,.76]])n(o,a*T,.06,v,m,u(o,-.75,.8,["#eab474","#c87942","#7d4136"])),l(o,[["moveTo",a*T,-.53],["quadraticCurveTo",a*(T+.12),-.25,a*(T+.07),.48]],null,"#f1be7d",.025);if(n(o,0,.05,.37,.76,u(o,-.75,.8,["#efb876","#d58a48","#98543a"])),l(o,[["moveTo",-.1,-.65],["quadraticCurveTo",-.17,-.94,.11,-1.02],["lineTo",.2,-.89],["quadraticCurveTo",.04,-.9,.06,-.65],["closePath"]],"#526953","#a6a077",.02),l(o,[["moveTo",.05,-.72],["bezierCurveTo",.41,-.99,.62,-.73,.42,-.66],["bezierCurveTo",.3,-.64,.3,-.83,.44,-.79]],null,"#718665",.025),e){o.shadowColor="#f1b960",o.shadowBlur=5+Math.sin(i*.006+(r.glowPhase||0))*2;const a=u(o,-.3,.55,["#fff0bc","#e7aa59"]);for(const T of[-1,1])l(o,[["moveTo",T*.12,-.19],["lineTo",T*.53,-.28],["quadraticCurveTo",T*.44,.04,T*.18,.02],["closePath"]],a,"#7c4935",.025);l(o,[["moveTo",-.52,.22],["lineTo",-.28,.29],["lineTo",-.24,.2],["lineTo",-.09,.32],["lineTo",.1,.25],["lineTo",.16,.35],["lineTo",.32,.26],["lineTo",.51,.19],["quadraticCurveTo",.21,.7,-.13,.5],["quadraticCurveTo",-.36,.48,-.52,.22],["closePath"]],a,"#7c4935",.025),o.shadowBlur=0}o.restore()}}export function drawPumpkin(o,r,i=r.time||0){b(o,r,i,!1)}export function drawJackOLantern(o,r,i=r.time||0){b(o,r,i,!0)}export function drawScarecrow(o,r,i=0){if(f(o,r)){o.rotate(Math.sin(i*.002+(r.swayPhase||0))*.025),s(o,-.055,.1,.11,1.85,.025,u(o,0,2,["#9b805c","#635042"]));for(const e of[-1,1]){o.save(),o.scale(e,1),l(o,[["moveTo",.31,-.01],["lineTo",1.06,.1],["lineTo",1,.38],["lineTo",.62,.3],["lineTo",.33,.5],["closePath"]],u(o,0,.5,["#8a9b88","#566e68"]),"#b1ab87",.018);for(let a=0;a<5;a++)l(o,[["moveTo",.99,.19+a*.025],["lineTo",1.2+a%2*.08,.13+a*.05]],null,"#d5b679",.02);l(o,[["moveTo",.62,.11],["lineTo",.6,.25]],null,"#c2b991",.012),o.restore()}l(o,[["moveTo",-.37,-.01],["quadraticCurveTo",-.51,.45,-.55,1.02],["lineTo",-.36,.92],["lineTo",-.2,1.12],["lineTo",.02,1.02],["lineTo",.24,1.13],["lineTo",.5,.97],["quadraticCurveTo",.47,.45,.35,-.01],["closePath"]],u(o,0,1.15,["#899782","#536960","#394e4c"]),"#a7a98b",.018);for(const e of[.27,.52,.78])d(o,.025,e,.028),l(o,[["moveTo",-.25,e],["lineTo",-.13,e+.1]],null,"#b2ad89",.012);s(o,.16,.48,.2,.22,.01,"#b28966","#d7b78c",.015);for(let e=0;e<7;e++)l(o,[["moveTo",-.37+e*.12,1],["lineTo",-.4+e*.12,1.2+e%2*.08]],null,"#d3b77d",.016);n(o,0,-.38,.35,.35,u(o,-.75,0,["#ddc8a0","#b99a71"]));for(const e of[-.12,.12])l(o,[["moveTo",e-.04,-.45],["lineTo",e+.04,-.37],["moveTo",e+.04,-.45],["lineTo",e-.04,-.37]],null,"#554b47",.026);l(o,[["moveTo",-.17,-.24],["quadraticCurveTo",0,-.13,.17,-.25]],null,"#655448",.02);for(let e=0;e<5;e++)l(o,[["moveTo",-.13+e*.06,-.25],["lineTo",-.14+e*.06,-.18]],null,"#655448",.018);l(o,[["moveTo",-.39,-.65],["lineTo",-.25,-1.16],["quadraticCurveTo",-.05,-1.24,.28,-1.12],["lineTo",.38,-.62],["closePath"]],u(o,-1.2,-.5,["#655655","#322f39"])),s(o,-.31,-.76,.66,.12,.02,"#ab725d"),n(o,0,-.61,.58,.09,"#3e3942"),l(o,[["moveTo",-.2,-.03],["lineTo",-.07,.12],["lineTo",.12,.05],["lineTo",.34,.41],["lineTo",.51,.32],["lineTo",.19,-.06],["closePath"]],"#b96858"),o.restore()}}export function drawHauntedHouse(o,r,i=0){if(f(o,r)){n(o,0,1.53,1.16,.13,"rgba(5,12,23,0.35)");for(const e of[-1,1]){o.save(),o.scale(e,1),o.rotate(e*.015),l(o,[["moveTo",.61,-.56],["lineTo",1.04,-.61],["lineTo",1.08,1.41],["lineTo",.56,1.41],["closePath"]],u(o,-.6,1.4,["#586172","#303d51","#19283c"]),"#71808a",.018),l(o,[["moveTo",.49,-.55],["lineTo",.77,-1.25],["lineTo",1.16,-.58],["closePath"]],"#303347","#879095",.02),l(o,[["moveTo",.77,-1.25],["lineTo",.77,-1.49]],null,"#97a1a5",.015);for(const a of[-.22,.27,.75])s(o,.72,a,.18,.25,.035,"#e8bb7d","#a58066",.02),l(o,[["moveTo",.81,a],["lineTo",.81,a+.25],["moveTo",.72,a+.13],["lineTo",.9,a+.13]],null,"#5f5c5c",.022);o.restore()}s(o,-.52,.11,1.09,1.32,.025,u(o,0,1.5,["#4a556a","#28384d"]),"#65737d",.018),s(o,.28,-.94,.12,.55,.01,"#475266"),l(o,[["moveTo",-.64,.16],["lineTo",-.12,-.73],["lineTo",.63,.14],["closePath"]],"#353b51","#849092",.02);for(let e=0;e<4;e++)l(o,[["moveTo",-.48+e*.11,.03-e*.17],["lineTo",.49-e*.15,.03-e*.17]],null,"#5e6271",.012);n(o,-.08,-.11,.12,.16,"#dcac73");for(const e of[-.32,.26])s(o,e,.35,.21,.31,.07,"#e8b980","#7f7972",.025),l(o,[["moveTo",e+.1,.35],["lineTo",e+.1,.66],["moveTo",e,.5],["lineTo",e+.21,.5]],null,"#575c65",.02),s(o,e-.035,.7,.28,.035,.01,"#8b9699");s(o,-.15,.86,.34,.54,.15,"#182739","#6f7e89",.025),n(o,.11,1.15,.018,.018,"#d6b582");for(const e of[1.43,1.49])s(o,-.27,e,.59,.055,.01,"#64707c");for(let e=0;e<12;e++){const a=(e<6?-1.15:.72)+e%6*.085;l(o,[["moveTo",a,1.5],["lineTo",a,1.15],["lineTo",a-.025,1.2],["moveTo",a,1.15],["lineTo",a+.025,1.2]],null,"#78868c",.012)}l(o,[["moveTo",-1.16,1.32],["lineTo",-.68,1.32],["moveTo",.73,1.32],["lineTo",1.17,1.32]],null,"#78868c",.018),o.save(),o.globalAlpha*=.3,l(o,[["moveTo",.34,-.94],["bezierCurveTo",.06,-1.23,.65,-1.42,.32+Math.sin(i*.001)*.07,-1.7]],null,"#a1b2bb",.045),o.restore(),o.restore()}}
@@ -1 +1 @@
1
- export default{name:"halloween",displayName:"Halloween",emoji:"\u{1F383}",intensityConfig:{light:{count:30,speedRange:[.3,1],sizeRange:[2,5],gravestones:3,jackOLanterns:2,spiders:2,scarecrows:1,twinklingStars:8},medium:{count:60,speedRange:[.5,1.5],sizeRange:[2,6],gravestones:5,jackOLanterns:4,spiders:4,scarecrows:2,twinklingStars:15},heavy:{count:100,speedRange:[.7,2],sizeRange:[3,8],gravestones:8,jackOLanterns:6,spiders:6,scarecrows:3,twinklingStars:25}},particles:["bat","ghost","pumpkin"],decorations:["gravestone","jack-o-lantern","haunted-house","cauldron","spider","scarecrow","twinkling-star"],colors:{primary:"#ff6600",secondary:"#1a1a2e",accent:"#8b0000",ghost:"#f0f0f0",purple:"#6a0dad"},lightningChance:5e-4,lightningDuration:200,lightningTimer:0,lightningActive:!1,lightningColor:"#fefefe",createBat(e,n,i){const s=Math.random();let o,a,l;s<.33?(o=i.sizeRange[0]+Math.random()*(i.sizeRange[1]-i.sizeRange[0])*1.5,a=i.speedRange[0]+Math.random()*(i.speedRange[1]-i.speedRange[0])*1.3,l=.9+Math.random()*.1):s<.66?(o=i.sizeRange[0]+Math.random()*(i.sizeRange[1]-i.sizeRange[0]),a=i.speedRange[0]+Math.random()*(i.speedRange[1]-i.sizeRange[0]),l=.6+Math.random()*.2):(o=i.sizeRange[0]+Math.random()*(i.sizeRange[1]-i.sizeRange[0])*.7,a=i.speedRange[0]+Math.random()*(i.sizeRange[1]-i.sizeRange[0])*.7,l=.4+Math.random()*.2);const t=Math.random()<.5;return{type:"bat",x:t?-20:e+20,y:Math.random()*n*.7,baseY:Math.random()*n*.7,vx:t?a*2:-a*2,vy:0,size:o,speed:a,opacity:l,waveAmplitude:10+Math.random()*20,waveFrequency:.002+Math.random()*.003,waveOffset:Math.random()*Math.PI*2,time:0,rotation:0,active:!0,depth:s}},createGhost(e,n,i){const s=Math.random();let o,a,l;return s<.33?(o=i.sizeRange[0]+Math.random()*(i.sizeRange[1]-i.sizeRange[0])*1.8,a=i.speedRange[0]+Math.random()*(i.speedRange[1]-i.sizeRange[0])*.5,l=.7+Math.random()*.2):s<.66?(o=i.sizeRange[0]+Math.random()*(i.sizeRange[1]-i.sizeRange[0])*1.2,a=i.speedRange[0]+Math.random()*(i.sizeRange[1]-i.sizeRange[0])*.5,l=.5+Math.random()*.15):(o=i.sizeRange[0]+Math.random()*(i.sizeRange[1]-i.sizeRange[0]),a=i.speedRange[0]+Math.random()*(i.sizeRange[1]-i.sizeRange[0])*.5,l=.3+Math.random()*.15),{type:"ghost",x:Math.random()*e,y:-50,size:o,speed:a,opacity:l,windOffset:Math.random()*Math.PI*2,windSpeed:.01+Math.random()*.02,rotation:0,rotationSpeed:(Math.random()-.5)*.005,time:Math.random()*1e3,active:!0,depth:s}},createPumpkin(e,n,i){return{type:"pumpkin",x:Math.random()*e,y:Math.random()*n,size:i.sizeRange[0]+Math.random()*(i.sizeRange[1]-i.sizeRange[0]),vx:(Math.random()-.5)*.2,speed:(i.speedRange[0]+Math.random()*(i.speedRange[1]-i.sizeRange[0]))*.2,opacity:.8+Math.random()*.2,rotation:Math.random()*Math.PI*2,rotationSpeed:(Math.random()-.5)*.008,windOffset:Math.random()*Math.PI*2,windSpeed:.008+Math.random()*.015,glowPhase:Math.random()*Math.PI*2,floatPhase:Math.random()*Math.PI*2,floatSpeed:.001+Math.random()*.002,active:!0}},createGravestone(e,n,i={}){const s=["rounded","cross","obelisk"];return{type:"gravestone",x:i.x!==void 0?i.x:Math.random()*e,y:i.y!==void 0?i.y:n-30-Math.random()*20,size:15+Math.random()*10,opacity:.7+Math.random()*.2,shape:s[Math.floor(Math.random()*s.length)],rotation:(Math.random()-.5)*.1,active:!0,static:!0}},createJackOLantern(e,n,i={}){const s=["happy","scary","wicked"];return{type:"jack-o-lantern",x:i.x!==void 0?i.x:Math.random()*e,y:i.y!==void 0?i.y:n-25-Math.random()*15,size:12+Math.random()*8,opacity:.9+Math.random()*.1,face:s[Math.floor(Math.random()*s.length)],glowPhase:Math.random()*Math.PI*2,active:!0,static:!0}},createSpider(e,n,i={}){return{type:"spider",x:i.x!==void 0?i.x:Math.random()*e,y:i.y!==void 0?i.y:Math.random()*n*.3,baseY:i.y!==void 0?i.y:Math.random()*n*.3,size:5+Math.random()*5,opacity:.8+Math.random()*.2,threadLength:30+Math.random()*50,swingPhase:Math.random()*Math.PI*2,swingAmplitude:20+Math.random()*30,swingSpeed:.001+Math.random()*.002,time:0,active:!0,static:!1}},createFallingParticle(e,n,i){return Math.random()<.7?this.createPumpkin(e,n,i):{type:"spider-small",x:Math.random()*e,y:-20,size:1+Math.random()*2,speed:i.speedRange[0]+Math.random()*(i.speedRange[1]-i.sizeRange[0])*.5,opacity:.6+Math.random()*.3,rotation:Math.random()*Math.PI*2,rotationSpeed:(Math.random()-.5)*.02,windOffset:Math.random()*Math.PI*2,windSpeed:.01+Math.random()*.02,active:!0}},createInitialDecorations(e,n,i){const s=[];s.push({type:"haunted-house",x:80,y:n-120,size:80,opacity:.85,active:!0,static:!0}),s.push({type:"moon",x:e-120,y:100,size:60,opacity:.9,glowPhase:0,active:!0,static:!0});const o=i.gravestones??5;for(let r=0;r<o;r++)s.push({type:"gravestone",x:e/(o+1)*(r+1)+(Math.random()-.5)*40,y:n-30-Math.random()*10,size:12+Math.random()*8,opacity:.7+Math.random()*.2,variant:Math.floor(Math.random()*3),active:!0,static:!0});const a=i.jackOLanterns??4;for(let r=0;r<a;r++)s.push({type:"jack-o-lantern",x:e/(a+1)*(r+1)+(Math.random()-.5)*30,y:n-15,size:10+Math.random()*6,opacity:.9,glowPhase:Math.random()*Math.PI*2,active:!0,static:!0});const l=i.spiders??4;for(let r=0;r<l;r++)s.push({type:"spider",x:e/(l+1)*(r+1),y:0,targetY:50+Math.random()*100,currentY:0,size:3+Math.random()*3,opacity:.8,swingPhase:Math.random()*Math.PI*2,swingAmplitude:20+Math.random()*30,swingSpeed:.02+Math.random()*.02,active:!0,static:!0});const t=i.scarecrows??2;for(let r=0;r<t;r++)s.push({type:"scarecrow",x:120+r/(t-1||1)*(e-240),y:n-60,size:30+Math.random()*10,opacity:.9,swayPhase:Math.random()*Math.PI*2,swaySpeed:.015+Math.random()*.015,glowPhase:Math.random()*Math.PI*2,glowSpeed:.02,active:!0,static:!0});const h=i.twinklingStars??15;for(let r=0;r<h;r++)s.push(this.createTwinklingStar(e,n));return s},createTwinklingStar(e,n){return{type:"twinkling-star",x:Math.random()*e,y:Math.random()*(n*.5),size:1+Math.random()*2.5,opacity:.5+Math.random()*.3,twinklePhase:Math.random()*Math.PI*2,twinkleSpeed:.004+Math.random()*.003,colorVariant:Math.floor(Math.random()*3),active:!0,static:!0}},drawLightning(e,n,i,s,o,a,l,t,h,r,M){if(a<1)return;const d=(n+s)/2,f=(i+o)/2,g=Math.atan2(o-i,s-n),m=g+Math.PI/2,T=(Math.random()-.5)*l,y=d+Math.cos(m)*T,P=f+Math.sin(m)*T,u=a-1,w=l*t,b=r*.8;if(e.strokeStyle=M,e.lineWidth=r,e.lineCap="round",e.lineJoin="round",e.beginPath(),e.moveTo(n,i),e.lineTo(y,P),e.lineTo(s,o),e.stroke(),u>0&&(this.drawLightning(e,n,i,y,P,u,w,t,h,b,M),this.drawLightning(e,y,P,s,o,u,w,t,h,b,M),Math.random()<h&&u>1)){const S=Math.random()*l*.5,v=(Math.random()-.5)*Math.PI/3,k=y+Math.cos(g+v)*S,p=P+Math.sin(g+v)*S;this.drawLightning(e,y,P,k,p,u-1,w*.5,t,h*.5,b*.6,M)}},spawnSpecialParticle(e,n,i){const s=Math.random(),o=this.intensityConfig.medium;if(s<.15)return this.createBat(n,i,o);if(s<.16){if(e.some(l=>l.type==="witch"))return null;const a=Math.random()<.5;return{type:"witch",x:a?-100:n+100,y:Math.random()*(i*.4)+50,baseY:Math.random()*(i*.4)+50,vx:a?3+Math.random()*2:-(3+Math.random()*2),size:15+Math.random()*8,opacity:.85,waveAmplitude:15+Math.random()*20,waveFrequency:.002+Math.random()*.002,waveOffset:Math.random()*Math.PI*2,time:0,active:!0,static:!1}}return s<.18?{type:"floating-pumpkin",x:Math.random()*n,y:-50,size:8+Math.random()*6,vx:(Math.random()-.5)*.5,vy:.3+Math.random()*.5,opacity:.9,rotation:Math.random()*Math.PI*2,rotationSpeed:(Math.random()-.5)*.01,glowPhase:Math.random()*Math.PI*2,windOffset:Math.random()*Math.PI*2,windSpeed:.015,active:!0}:s<.2102?e.some(a=>a.type==="haunted-house")?null:{type:"haunted-house",x:Math.random()*n*.6+n*.2,y:i-100,size:40+Math.random()*20,opacity:.8,windowFlicker:0,active:!0,static:!0}:s<.2105?e.some(a=>a.type==="cauldron")?null:{type:"cauldron",x:Math.random()*n*.6+n*.2,y:i-40,size:25+Math.random()*10,opacity:1,bubbles:[],smoke:[],time:0,active:!0,static:!0}:null},updateSpecialParticles(e,n,i,s){this.lightningActive?(this.lightningTimer+=n,this.lightningTimer>=this.lightningDuration&&(this.lightningActive=!1,this.lightningTimer=0)):Math.random()<this.lightningChance&&(this.lightningActive=!0,this.lightningTimer=0),e.forEach(o=>{switch(o.time!==void 0&&(o.time+=n),o.type){case"moon":break;case"witch":(o.vx>0&&o.x>i+100||o.vx<0&&o.x<-100)&&(o.active=!1);break;case"floating-pumpkin":o.y>s+50&&(o.active=!1);break;case"cauldron":o.time%100<20&&o.bubbles.push({x:(Math.random()-.5)*o.size*.5,y:o.size*.3+Math.random()*o.size*.2,size:o.size*(.05+Math.random()*.1),vy:-.5-Math.random()*.5,opacity:1,life:1,fadeRate:.01+Math.random()*.005}),o.bubbles=o.bubbles.filter(a=>(a.y+=a.vy,a.vy*=.98,a.opacity-=a.fadeRate,a.opacity>0&&a.y>-o.size)),o.time%200<30&&o.smoke.push({x:(Math.random()-.5)*o.size*.3,y:-o.size*.5+Math.random()*o.size*.1,size:o.size*(.2+Math.random()*.3),vx:(Math.random()-.5)*.1,vy:-.2-Math.random()*.2,opacity:.5,life:1,fadeRate:.005+Math.random()*.003}),o.smoke=o.smoke.filter(a=>(a.x+=a.vx,a.y+=a.vy,a.size*=1.02,a.opacity-=a.fadeRate,a.opacity>0));break}})},drawBat(e,n){const i=n.x,s=n.y,o=n.size,a=n.vx>0?1:-1,l=n.time;e.save(),e.globalAlpha=n.opacity,e.translate(i,s),a===-1&&e.scale(-1,1);const t=l*.01,h=Math.sin(t)*(Math.PI/4);e.fillStyle="#1a1a1a",e.beginPath(),e.ellipse(0,0,o*.4,o*.6,0,0,Math.PI*2),e.fill(),e.beginPath(),e.arc(o*.3,-o*.4,o*.35,0,Math.PI*2),e.fill(),e.beginPath(),e.moveTo(o*.1,-o*.7),e.lineTo(o*.25,-o*1.1),e.lineTo(o*.4,-o*.7),e.fill(),e.beginPath(),e.moveTo(o*.2,-o*.7),e.lineTo(o*.35,-o*1),e.lineTo(o*.5,-o*.7),e.fill(),e.fillStyle="#ff0000",e.beginPath(),e.arc(o*.2,-o*.45,o*.08,0,Math.PI*2),e.fill(),e.beginPath(),e.arc(o*.4,-o*.45,o*.08,0,Math.PI*2),e.fill(),e.fillStyle="rgba(255, 0, 0, 0.3)",e.beginPath(),e.arc(o*.2,-o*.45,o*.15,0,Math.PI*2),e.fill(),e.beginPath(),e.arc(o*.4,-o*.45,o*.15,0,Math.PI*2),e.fill(),e.fillStyle="#2a2a2a",e.strokeStyle="#1a1a1a",e.lineWidth=1,e.save(),e.translate(-o*.3,0),e.rotate(-h),e.beginPath(),e.moveTo(0,0),e.quadraticCurveTo(-o*.8,-o*.5,-o*1.2,0),e.quadraticCurveTo(-o*.9,o*.3,-o*.6,o*.4),e.quadraticCurveTo(-o*.3,o*.2,0,0),e.closePath(),e.fill(),e.stroke(),e.restore(),e.save(),e.translate(o*.3,0),e.rotate(h),e.beginPath(),e.moveTo(0,0),e.quadraticCurveTo(o*.8,-o*.5,o*1.2,0),e.quadraticCurveTo(o*.9,o*.3,o*.6,o*.4),e.quadraticCurveTo(o*.3,o*.2,0,0),e.closePath(),e.fill(),e.stroke(),e.restore(),e.restore()},drawGhost(e,n){const i=n.x,s=n.y,o=n.size,a=n.time;e.save(),e.globalAlpha=n.opacity,e.translate(i,s),e.fillStyle="#f0f0f0",e.shadowColor="rgba(240, 240, 240, 0.5)",e.shadowBlur=15,e.beginPath(),e.arc(0,-o*.5,o*.6,Math.PI,0);const l=5;for(let t=0;t<=l;t++){const h=-o*.6+t/l*o*1.2,r=Math.sin(a*.005+t)*o*.15;t===0?e.lineTo(h,r):e.quadraticCurveTo(h-o*.12,r-o*.1,h,r)}e.closePath(),e.fill(),e.shadowBlur=0,e.fillStyle="#000000",e.beginPath(),e.ellipse(-o*.2,-o*.6,o*.12,o*.18,-.2,0,Math.PI*2),e.fill(),e.beginPath(),e.ellipse(o*.2,-o*.6,o*.12,o*.18,.2,0,Math.PI*2),e.fill(),e.beginPath(),e.ellipse(0,-o*.3,o*.15,o*.2,0,0,Math.PI*2),e.fill(),e.restore()},drawPumpkin(e,n,i){const s=n.x,o=n.y,a=n.size;e.save(),e.globalAlpha=n.opacity,e.translate(s,o),e.rotate(n.rotation),e.fillStyle="#ff6600",e.strokeStyle="#cc5200",e.lineWidth=a*.08;const l=5;e.beginPath();for(let r=0;r<l;r++){const M=r/l*Math.PI*2-Math.PI/2,d=(r+1)/l*Math.PI*2-Math.PI/2,f=a*.9,g=a;r===0&&e.moveTo(Math.cos(M)*f,Math.sin(M)*g),e.quadraticCurveTo(Math.cos((M+d)/2)*f*.8,Math.sin((M+d)/2)*g*.8,Math.cos(d)*f,Math.sin(d)*g)}e.closePath(),e.fill(),e.stroke(),e.fillStyle="#8B4513",e.fillRect(-a*.2,-a*1.1,a*.4,a*.2);const t=(Math.sin(i*.002+n.glowPhase)+1)*.5;e.globalAlpha=n.opacity*t*.7;const h=e.createRadialGradient(0,0,0,0,0,a);h.addColorStop(0,"#ffaa00"),h.addColorStop(1,"rgba(255, 170, 0, 0)"),e.fillStyle=h,e.fillRect(-a,-a,a*2,a*2),e.restore()},drawGravestone(e,n){const i=n.x,s=n.y,o=n.size;e.save(),e.globalAlpha=n.opacity,e.translate(i,s),e.rotate(n.rotation),e.fillStyle="#555555",e.strokeStyle="#333333",e.lineWidth=1,n.shape==="rounded"?(e.beginPath(),e.moveTo(-o*.5,o*.8),e.lineTo(-o*.5,-o*.3),e.arc(0,-o*.3,o*.5,Math.PI,0),e.lineTo(o*.5,o*.8),e.closePath(),e.fill(),e.stroke()):n.shape==="cross"?(e.fillRect(-o*.15,-o,o*.3,o*1.8),e.fillRect(-o*.5,-o*.3,o,o*.3)):(e.beginPath(),e.moveTo(-o*.4,o*.8),e.lineTo(-o*.5,-o*.5),e.lineTo(0,-o),e.lineTo(o*.5,-o*.5),e.lineTo(o*.4,o*.8),e.closePath(),e.fill(),e.stroke()),e.fillStyle="rgba(0, 100, 0, 0.3)",e.fillRect(-o*.4,o*.5,o*.8,o*.3),e.fillStyle="#222222",e.font=`${o*.4}px serif`,e.textAlign="center",e.textBaseline="middle",e.fillText("RIP",0,0),e.restore()},drawJackOLantern(e,n,i){const s=n.x,o=n.y,a=n.size;e.save(),e.globalAlpha=n.opacity,e.translate(s,o),e.fillStyle="#ff8800",e.beginPath(),e.ellipse(0,0,a*1.2,a,0,0,Math.PI*2),e.fill(),e.strokeStyle="#cc6600",e.lineWidth=a*.08;for(let r=-2;r<=2;r++)e.beginPath(),e.moveTo(r*a*.35,-a*.8),e.quadraticCurveTo(r*a*.3,0,r*a*.35,a*.8),e.stroke();e.fillStyle="#8B4513",e.fillRect(-a*.2,-a*1.1,a*.4,a*.2);const l=.8+Math.sin(i*.02+n.glowPhase)*.2,t=.5+l*.5,h=e.createRadialGradient(0,0,0,0,0,a*1.5);h.addColorStop(0,`rgba(255, 255, 100, ${t})`),h.addColorStop(.5,`rgba(255, 200, 0, ${t*.7})`),h.addColorStop(1,"rgba(255, 150, 0, 0)"),e.fillStyle=h,e.fillRect(-a*1.5,-a*1.5,a*3,a*3),e.fillStyle="#000000",e.shadowColor=`rgba(255, 180, 0, ${l})`,e.shadowBlur=a*.5,e.beginPath(),e.moveTo(-a*.6,-a*.4),e.lineTo(-a*.2,-a*.3),e.lineTo(-a*.5,-a*.1),e.closePath(),e.fill(),e.beginPath(),e.moveTo(a*.6,-a*.4),e.lineTo(a*.2,-a*.3),e.lineTo(a*.5,-a*.1),e.closePath(),e.fill(),e.beginPath(),e.moveTo(-a*.7,a*.2),e.lineTo(-a*.5,a*.4),e.lineTo(-a*.3,a*.25),e.lineTo(0,a*.45),e.lineTo(a*.3,a*.25),e.lineTo(a*.5,a*.4),e.lineTo(a*.7,a*.2),e.lineTo(a*.5,a*.1),e.lineTo(0,a*.25),e.lineTo(-a*.5,a*.1),e.closePath(),e.fill(),e.shadowBlur=0,e.restore()},drawHauntedHouse(e,n,i){const s=n.x,o=n.y,a=n.size;e.save(),e.globalAlpha=n.opacity,e.translate(s,o);const l="#1a1a2e",t="#0a0a0a",h=.5+Math.random()*.5;e.fillStyle=l,e.strokeStyle=t,e.lineWidth=2,e.fillRect(-a*1.2,-a*1.4,a*.5,a*2);for(let r=0;r<3;r++)e.fillRect(-a*1.2+r*a*.2,-a*1.5,a*.12,a*.1);e.beginPath(),e.moveTo(-a*1.3,-a*1.5),e.lineTo(-a*.95,-a*2),e.lineTo(-a*.6,-a*1.5),e.closePath(),e.fill(),e.stroke(),e.fillRect(a*.7,-a*1.3,a*.5,a*1.9);for(let r=0;r<3;r++)e.fillRect(a*.7+r*a*.2,-a*1.4,a*.12,a*.1);e.beginPath(),e.moveTo(a*.6,-a*1.4),e.lineTo(a*.95,-a*1.9),e.lineTo(a*1.3,-a*1.4),e.closePath(),e.fill(),e.stroke(),e.fillRect(-a*.7,-a*.9,a*1.4,a*1.5),e.strokeRect(-a*.7,-a*.9,a*1.4,a*1.5);for(let r=0;r<7;r++)r%2===0&&e.fillRect(-a*.7+r*a*.2,-a*1,a*.2,a*.1);e.fillRect(-a*.8,-a*1.1,a*.3,a*.5),e.beginPath(),e.moveTo(-a*.85,-a*1.1),e.lineTo(-a*.65,-a*1.35),e.lineTo(-a*.45,-a*1.1),e.closePath(),e.fill(),e.fillRect(a*.5,-a*1.1,a*.3,a*.5),e.beginPath(),e.moveTo(a*.45,-a*1.1),e.lineTo(a*.65,-a*1.35),e.lineTo(a*.85,-a*1.1),e.closePath(),e.fill(),e.fillStyle=`rgba(255, 200, 100, ${h})`,e.shadowColor="#ffcc66",e.shadowBlur=10,e.fillRect(-a*1.05,-a*1.2,a*.2,a*.25),e.fillRect(-a*1.05,-a*.8,a*.2,a*.25),e.fillRect(-a*1.05,-a*.4,a*.2,a*.25),e.fillRect(a*.85,-a*1.1,a*.2,a*.25),e.fillRect(a*.85,-a*.7,a*.2,a*.25),e.fillRect(a*.85,-a*.3,a*.2,a*.25),e.fillRect(-a*.5,-a*.6,a*.25,a*.3),e.fillRect(-a*.1,-a*.6,a*.25,a*.3),e.fillRect(a*.3,-a*.6,a*.25,a*.3),e.fillRect(-a*.3,-a*.15,a*.25,a*.3),e.fillRect(a*.1,-a*.15,a*.25,a*.3),e.shadowBlur=0,e.fillStyle=t,e.beginPath(),e.arc(0,a*.3,a*.25,Math.PI,0,!0),e.lineTo(a*.25,a*.6),e.lineTo(-a*.25,a*.6),e.closePath(),e.fill(),e.strokeStyle="#333333",e.lineWidth=1.5;for(let r=-2;r<=2;r++)e.beginPath(),e.moveTo(r*a*.1,a*.3),e.lineTo(r*a*.1,a*.6),e.stroke();e.restore()},drawCauldron(e,n,i){const s=n.x,o=n.y,a=n.size;e.save(),e.translate(s,o),e.fillStyle="#222222",e.strokeStyle="#111111",e.lineWidth=2,e.beginPath(),e.arc(0,0,a*.8,0,Math.PI),e.lineTo(-a*.8,a*.5),e.quadraticCurveTo(0,a*.8,a*.8,a*.5),e.closePath(),e.fill(),e.stroke(),e.strokeStyle="#1a1a1a",e.lineWidth=3,[-a*.5,0,a*.5].forEach(l=>{e.beginPath(),e.moveTo(l,a*.5),e.lineTo(l,a*.9),e.stroke()}),e.fillStyle="#00ff00",e.shadowColor="#00ff00",e.shadowBlur=15,e.beginPath(),e.ellipse(0,0,a*.7,a*.3,0,0,Math.PI*2),e.fill(),e.shadowBlur=0,n.bubbles.forEach(l=>{e.globalAlpha=l.opacity,e.fillStyle="#88ff88",e.beginPath(),e.arc(l.x,l.y,l.size,0,Math.PI*2),e.fill()}),e.globalAlpha=1,n.smoke.forEach(l=>{e.globalAlpha=l.opacity,e.fillStyle="#aaaaaa",e.beginPath(),e.arc(l.x,l.y,l.size,0,Math.PI*2),e.fill()}),e.restore()},drawSpider(e,n,i){const s=n.x,o=n.y,a=n.size,l=Math.sin(i*n.swingSpeed+n.swingPhase)*n.swingAmplitude;e.save(),e.globalAlpha=n.opacity,e.strokeStyle="#cccccc",e.lineWidth=1,e.setLineDash([2,2]),e.beginPath(),e.moveTo(s,o),e.lineTo(s+l,o+n.threadLength),e.stroke(),e.setLineDash([]),e.translate(s+l,o+n.threadLength),e.fillStyle="#000000",e.beginPath(),e.ellipse(0,0,a*.5,a*.7,0,0,Math.PI*2),e.fill(),e.beginPath(),e.arc(0,-a*.5,a*.3,0,Math.PI*2),e.fill(),e.strokeStyle="#1a1a1a",e.lineWidth=a*.1;const t=Math.sin(i*.005)*.2;for(let h=0;h<4;h++){const r=h/4*Math.PI-Math.PI/2,M=a*1.5;e.beginPath(),e.moveTo(-a*.3,-a*.2+h*a*.2),e.quadraticCurveTo(-a-Math.sin(r+t)*a*.3,-a*.2+h*a*.2-a*.5,-a*1.2,-a*.2+h*a*.2),e.stroke(),e.beginPath(),e.moveTo(a*.3,-a*.2+h*a*.2),e.quadraticCurveTo(a+Math.sin(r+t)*a*.3,-a*.2+h*a*.2-a*.5,a*1.2,-a*.2+h*a*.2),e.stroke()}e.fillStyle="#ff0000";for(let h=0;h<4;h++)e.beginPath(),e.arc(-a*.15+h%2*a*.3,-a*.55+Math.floor(h/2)*a*.1,a*.05,0,Math.PI*2),e.fill();e.restore()},drawSpiderSmall(e,n){e.save(),e.globalAlpha=n.opacity,e.translate(n.x,n.y),e.rotate(n.rotation),e.fillStyle="#1a1a1a",e.beginPath(),e.arc(0,0,n.size,0,Math.PI*2),e.fill(),e.strokeStyle="#1a1a1a",e.lineWidth=n.size*.3;for(let i=0;i<4;i++){const s=i/4*Math.PI-Math.PI/2;e.beginPath(),e.moveTo(0,0),e.lineTo(Math.cos(s)*n.size*2,Math.sin(s)*n.size*2),e.stroke(),e.beginPath(),e.moveTo(0,0),e.lineTo(-Math.cos(s)*n.size*2,Math.sin(s)*n.size*2),e.stroke()}e.restore()},drawWitch(e,n,i){const s=n.x,o=n.y+Math.sin(i*n.waveFrequency+n.waveOffset)*n.waveAmplitude,a=n.size,l=n.vx>0?1:-1;e.save(),e.globalAlpha=n.opacity,e.translate(s,o),l===-1&&e.scale(-1,1),e.strokeStyle="#8B4513",e.lineWidth=a*.12,e.lineCap="round",e.beginPath(),e.moveTo(a*.2,a*.6),e.lineTo(-a*1.5,a*.6),e.stroke(),e.strokeStyle="#D2691E",e.lineWidth=a*.06;for(let t=0;t<8;t++){const h=Math.PI/3*(t/7-.5);e.beginPath(),e.moveTo(-a*1.5,a*.6),e.lineTo(-a*1.5+Math.cos(h)*a*.4,a*.6+Math.sin(h)*a*.4),e.stroke()}e.fillStyle="#1a1a1a",e.strokeStyle="#0a0a0a",e.lineWidth=1,e.beginPath(),e.moveTo(a*.2,a*.6),e.quadraticCurveTo(-a*.5,a*.4,-a*.8,a*.1),e.lineTo(-a*.7,-a*.3),e.quadraticCurveTo(-a*.3,-a*.5,0,-a*.6),e.lineTo(a*.3,-a*.4),e.lineTo(a*.3,a*.5),e.closePath(),e.fill(),e.stroke(),e.fillStyle="#8B9355",e.beginPath(),e.ellipse(a*.25,-a*.6,a*.3,a*.35,0,0,Math.PI*2),e.fill(),e.fillStyle="#7a8245",e.beginPath(),e.moveTo(a*.45,-a*.6),e.quadraticCurveTo(a*.7,-a*.65,a*.72,-a*.5),e.lineTo(a*.6,-a*.52),e.quadraticCurveTo(a*.58,-a*.58,a*.4,-a*.55),e.closePath(),e.fill(),e.fillStyle="#2C1810",e.beginPath(),e.moveTo(a*.05,-a*.85),e.lineTo(a*.4,-a*1.6),e.lineTo(a*.5,-a*.8),e.closePath(),e.fill(),e.beginPath(),e.ellipse(a*.25,-a*.85,a*.5,a*.12,0,0,Math.PI*2),e.fill(),e.fillStyle="#ff6600",e.shadowColor="#ff6600",e.shadowBlur=a*.3,e.beginPath(),e.arc(a*.35,-a*.65,a*.08,0,Math.PI*2),e.fill(),e.shadowBlur=0,e.fillStyle="#8B9355",e.beginPath(),e.arc(a*.1,a*.5,a*.12,0,Math.PI*2),e.fill(),e.beginPath(),e.arc(-a*.3,a*.55,a*.1,0,Math.PI*2),e.fill(),e.strokeStyle="#1a1a1a",e.lineWidth=a*.1,e.beginPath(),e.moveTo(a*.15,a*.6),e.lineTo(a*.1,a*1),e.stroke(),e.beginPath(),e.moveTo(a*.05,a*.6),e.lineTo(a*.2,a*.95),e.stroke(),e.restore()},drawFloatingPumpkin(e,n){const i=n.x,s=n.y,o=n.size;e.save(),e.globalAlpha=n.opacity,e.translate(i,s),e.rotate(n.rotation),e.fillStyle="#ff6600",e.strokeStyle="#cc5200",e.lineWidth=o*.1,e.beginPath(),e.ellipse(0,0,o,o*.9,0,0,Math.PI*2),e.fill(),e.stroke();for(let l=-2;l<=2;l++)e.beginPath(),e.moveTo(l*o*.25,-o*.8),e.quadraticCurveTo(l*o*.3,0,l*o*.25,o*.8),e.stroke();e.fillStyle="#228B22",e.beginPath(),e.moveTo(-o*.15,-o*.8),e.lineTo(-o*.1,-o*1.1),e.lineTo(o*.1,-o*1.1),e.lineTo(o*.15,-o*.8),e.closePath(),e.fill();const a=.5+Math.sin(n.glowPhase+Date.now()*.005)*.5;e.fillStyle=`rgba(255, 200, 0, ${a})`,e.beginPath(),e.moveTo(-o*.4,-o*.3),e.lineTo(-o*.2,-o*.1),e.lineTo(-o*.6,-o*.1),e.closePath(),e.fill(),e.beginPath(),e.moveTo(o*.4,-o*.3),e.lineTo(o*.6,-o*.1),e.lineTo(o*.2,-o*.1),e.closePath(),e.fill(),e.beginPath(),e.moveTo(-o*.5,o*.2);for(let l=-4;l<=4;l++)e.lineTo(l*o*.12,o*.2+(l%2===0?o*.15:0));e.lineTo(o*.5,o*.2),e.closePath(),e.fill(),e.restore()},drawMoon(e,n,i){const s=n.x,o=n.y,a=n.size;e.save(),e.globalAlpha=n.opacity,e.translate(s,o);const l=.8+Math.sin(i*.001+n.glowPhase)*.2,t=e.createRadialGradient(0,0,0,0,0,a*1.5);t.addColorStop(0,`rgba(255, 255, 220, ${l})`),t.addColorStop(.4,`rgba(255, 255, 200, ${l*.5})`),t.addColorStop(.7,`rgba(255, 220, 150, ${l*.2})`),t.addColorStop(1,"rgba(255, 200, 100, 0)"),e.fillStyle=t,e.fillRect(-a*1.5,-a*1.5,a*3,a*3),e.fillStyle="#ffffdc",e.shadowColor="#ffffaa",e.shadowBlur=a*.3,e.beginPath(),e.arc(0,0,a,0,Math.PI*2),e.fill(),e.shadowBlur=0,e.fillStyle="rgba(200, 200, 170, 0.4)",e.beginPath(),e.arc(-a*.3,-a*.2,a*.2,0,Math.PI*2),e.fill(),e.beginPath(),e.arc(a*.4,a*.1,a*.15,0,Math.PI*2),e.fill(),e.beginPath(),e.arc(a*.1,a*.4,a*.18,0,Math.PI*2),e.fill(),e.beginPath(),e.arc(-a*.2,a*.5,a*.12,0,Math.PI*2),e.fill(),e.restore()},drawScarecrow(e,n,i){const s=n.x,o=n.y,a=n.size,l=Math.sin(i*n.swaySpeed+n.swayPhase)*a*.12,t=Math.abs(Math.sin(i*n.swaySpeed*2+n.swayPhase))*a*.03;e.save(),e.globalAlpha=n.opacity,e.translate(s+l,o-t);const h=e.createLinearGradient(-a*.05,0,a*.05,0);if(h.addColorStop(0,"#5a4632"),h.addColorStop(.5,"#6b5644"),h.addColorStop(1,"#4a3622"),e.fillStyle=h,e.fillRect(-a*.05,0,a*.1,a*1.8),e.strokeStyle="#3a2612",e.lineWidth=1,e.strokeRect(-a*.05,0,a*.1,a*1.8),e.fillRect(-a*.8,a*.5,a*1.6,a*.08),e.strokeRect(-a*.8,a*.5,a*1.6,a*.08),e.fillStyle="#8B7355",e.strokeStyle="#654321",e.lineWidth=1.5,e.beginPath(),e.moveTo(-a*.4,a*.5),e.lineTo(-a*.45,a*1.2),e.lineTo(-a*.25,a*1.4),e.lineTo(a*.25,a*1.4),e.lineTo(a*.45,a*1.2),e.lineTo(a*.4,a*.5),e.closePath(),e.fill(),e.stroke(),e.fillStyle="#6b5c4d",e.fillRect(-a*.3,a*.7,a*.2,a*.15),e.fillRect(a*.1,a*1,a*.18,a*.2),e.fillStyle="#8B7355",e.strokeStyle="#654321",e.beginPath(),e.moveTo(-a*.8,a*.5),e.lineTo(-a*.75,a*.7),e.lineTo(-a*.85,a*.75),e.lineTo(-a*.9,a*.6),e.closePath(),e.fill(),e.stroke(),e.beginPath(),e.moveTo(a*.8,a*.5),e.lineTo(a*.75,a*.7),e.lineTo(a*.85,a*.75),e.lineTo(a*.9,a*.6),e.closePath(),e.fill(),e.stroke(),Math.random()<.6){const d=e.createRadialGradient(-a*.1,-a*.05,0,0,0,a*.35);d.addColorStop(0,"#ff8c00"),d.addColorStop(.7,"#ff6600"),d.addColorStop(1,"#cc5500"),e.fillStyle=d,e.beginPath(),e.ellipse(0,a*.2,a*.35,a*.3,0,0,Math.PI*2),e.fill(),e.strokeStyle="#8B4513",e.lineWidth=2,e.stroke(),e.strokeStyle="#cc5500",e.lineWidth=1;for(let f=-2;f<=2;f++)e.beginPath(),e.moveTo(f*a*.12,a*.05),e.quadraticCurveTo(f*a*.1,a*.2,f*a*.12,a*.45),e.stroke();e.fillStyle="#228B22",e.strokeStyle="#1a6b1a",e.fillRect(-a*.04,a*.02,a*.08,a*.1),e.strokeRect(-a*.04,a*.02,a*.08,a*.1)}else{e.fillStyle="#d2b48c",e.strokeStyle="#8b7355",e.lineWidth=2,e.beginPath(),e.ellipse(0,a*.2,a*.32,a*.28,0,0,Math.PI*2),e.fill(),e.stroke(),e.strokeStyle="#a0826d",e.lineWidth=.5;for(let d=-3;d<=3;d++)e.beginPath(),e.moveTo(d*a*.1,a*.05),e.lineTo(d*a*.1,a*.42),e.stroke()}const M=.6+Math.sin(i*n.glowSpeed+n.glowPhase)*.4;e.shadowColor="#ff0000",e.shadowBlur=a*.2,e.fillStyle=`rgba(255, 0, 0, ${M})`,e.beginPath(),e.moveTo(-a*.18,a*.15),e.lineTo(-a*.12,a*.2),e.lineTo(-a*.18,a*.25),e.closePath(),e.fill(),e.beginPath(),e.moveTo(a*.18,a*.15),e.lineTo(a*.12,a*.2),e.lineTo(a*.18,a*.25),e.closePath(),e.fill(),e.strokeStyle="#000000",e.lineWidth=a*.03,e.beginPath(),e.moveTo(-a*.2,a*.35);for(let d=-3;d<=3;d++){const f=d%2===0?0:a*.04;e.lineTo(d*a*.07,a*.35+f)}e.stroke(),e.shadowBlur=0,e.fillStyle="#2a2a2a",e.strokeStyle="#1a1a1a",e.lineWidth=2,e.beginPath(),e.ellipse(0,a*.05,a*.45,a*.08,0,0,Math.PI*2),e.fill(),e.stroke(),e.beginPath(),e.moveTo(-a*.2,a*.05),e.lineTo(-a*.15,-a*.2),e.lineTo(a*.15,-a*.2),e.lineTo(a*.2,a*.05),e.closePath(),e.fill(),e.stroke(),e.fillStyle="#8B4513",e.fillRect(-a*.2,a*.03,a*.4,a*.05),e.strokeStyle="#1a1a1a",e.lineWidth=1;for(let d=0;d<5;d++){const f=d/5*Math.PI*2,g=Math.cos(f)*a*.45,m=a*.05+Math.sin(f)*a*.08;e.beginPath(),e.moveTo(g,m),e.lineTo(g+Math.cos(f)*a*.08,m+Math.sin(f)*a*.08),e.stroke()}if(Math.random()<.3){const d=Math.random()<.5?-a*.6:a*.6;e.fillStyle="#000000",e.beginPath(),e.arc(d,a*.5,a*.08,0,Math.PI*2),e.fill(),e.fillStyle="#ffa500",e.beginPath(),e.moveTo(d+a*.08,a*.5),e.lineTo(d+a*.15,a*.5),e.lineTo(d+a*.1,a*.52),e.closePath(),e.fill()}e.restore()},drawTwinklingStar(e,n,i){const s=n.x,o=n.y,a=n.size,l=.4+Math.sin(i*n.twinkleSpeed+n.twinklePhase)*.6;let t,h;switch(n.colorVariant){case 0:t=`rgba(255, 255, 255, ${l})`,h=`rgba(255, 255, 255, ${l*.3})`;break;case 1:t=`rgba(200, 150, 255, ${l})`,h=`rgba(150, 100, 255, ${l*.3})`;break;case 2:t=`rgba(255, 180, 100, ${l})`,h=`rgba(255, 150, 50, ${l*.3})`;break}e.save(),e.translate(s,o),e.shadowColor=h,e.shadowBlur=a*4*l,e.fillStyle=t,e.beginPath();for(let r=0;r<4;r++){const M=r/4*Math.PI-Math.PI/2,d=Math.cos(M)*a,f=Math.sin(M)*a,g=M+Math.PI/4,m=Math.cos(g)*(a*.3),T=Math.sin(g)*(a*.3);r===0?e.moveTo(d,f):e.lineTo(d,f),e.lineTo(m,T)}e.closePath(),e.fill(),e.shadowBlur=a*2*l,e.beginPath(),e.arc(0,0,a*.3,0,Math.PI*2),e.fill(),e.restore()},drawGlobalEffects(e,n,i,s){if(this.lightningActive){e.save(),e.globalAlpha=.8+Math.sin(this.lightningTimer*.01)*.2,e.shadowColor=this.lightningColor,e.shadowBlur=20;const o=i*(.4+Math.random()*.2),a=0,l=i*Math.random(),t=s;this.drawLightning(e,o,a,l,t,5+Math.floor(Math.random()*3),50+Math.random()*50,.7,.3,3+Math.random()*2,this.lightningColor),e.restore()}}};
1
+ import{drawBat as b,drawGhost as R,drawPumpkin as I,drawJackOLantern as C,drawHauntedHouse as A,drawScarecrow as L}from"./halloween-drawing.js";export default{name:"halloween",displayName:"Halloween",emoji:"\u{1F383}",intensityConfig:{light:{count:30,speedRange:[.3,1],sizeRange:[2,5],gravestones:3,jackOLanterns:2,spiders:2,scarecrows:1,twinklingStars:8},medium:{count:60,speedRange:[.5,1.5],sizeRange:[2,6],gravestones:5,jackOLanterns:4,spiders:4,scarecrows:2,twinklingStars:15},heavy:{count:100,speedRange:[.7,2],sizeRange:[3,8],gravestones:8,jackOLanterns:6,spiders:6,scarecrows:3,twinklingStars:25}},particles:["bat","ghost","pumpkin"],decorations:["gravestone","jack-o-lantern","haunted-house","cauldron","spider","scarecrow","twinkling-star"],colors:{primary:"#ff6600",secondary:"#1a1a2e",accent:"#8b0000",ghost:"#f0f0f0",purple:"#6a0dad"},lightningChance:5e-4,lightningDuration:200,lightningTimer:0,lightningActive:!1,lightningColor:"#fefefe",createBat(e,t,o){const r=Math.random();let n,a,i;r<.33?(n=o.sizeRange[0]+Math.random()*(o.sizeRange[1]-o.sizeRange[0])*1.5,a=o.speedRange[0]+Math.random()*(o.speedRange[1]-o.speedRange[0])*1.3,i=.9+Math.random()*.1):r<.66?(n=o.sizeRange[0]+Math.random()*(o.sizeRange[1]-o.sizeRange[0]),a=o.speedRange[0]+Math.random()*(o.speedRange[1]-o.sizeRange[0]),i=.6+Math.random()*.2):(n=o.sizeRange[0]+Math.random()*(o.sizeRange[1]-o.sizeRange[0])*.7,a=o.speedRange[0]+Math.random()*(o.sizeRange[1]-o.sizeRange[0])*.7,i=.4+Math.random()*.2);const s=Math.random()<.5;return{type:"bat",x:s?-20:e+20,y:Math.random()*t*.7,baseY:Math.random()*t*.7,vx:s?a*2:-a*2,vy:0,size:n,speed:a,opacity:i,waveAmplitude:10+Math.random()*20,waveFrequency:.002+Math.random()*.003,waveOffset:Math.random()*Math.PI*2,time:0,rotation:0,active:!0,depth:r}},createGhost(e,t,o){const r=Math.random();let n,a,i;return r<.33?(n=o.sizeRange[0]+Math.random()*(o.sizeRange[1]-o.sizeRange[0])*1.8,a=o.speedRange[0]+Math.random()*(o.speedRange[1]-o.sizeRange[0])*.5,i=.7+Math.random()*.2):r<.66?(n=o.sizeRange[0]+Math.random()*(o.sizeRange[1]-o.sizeRange[0])*1.2,a=o.speedRange[0]+Math.random()*(o.sizeRange[1]-o.sizeRange[0])*.5,i=.5+Math.random()*.15):(n=o.sizeRange[0]+Math.random()*(o.sizeRange[1]-o.sizeRange[0]),a=o.speedRange[0]+Math.random()*(o.sizeRange[1]-o.sizeRange[0])*.5,i=.3+Math.random()*.15),{type:"ghost",x:Math.random()*e,y:-50,size:n,speed:a,opacity:i,windOffset:Math.random()*Math.PI*2,windSpeed:.01+Math.random()*.02,rotation:0,rotationSpeed:(Math.random()-.5)*.005,time:Math.random()*1e3,active:!0,depth:r}},createPumpkin(e,t,o){return{type:"pumpkin",x:Math.random()*e,y:Math.random()*t,size:o.sizeRange[0]+Math.random()*(o.sizeRange[1]-o.sizeRange[0]),vx:(Math.random()-.5)*.2,speed:(o.speedRange[0]+Math.random()*(o.speedRange[1]-o.sizeRange[0]))*.2,opacity:.8+Math.random()*.2,rotation:Math.random()*Math.PI*2,rotationSpeed:(Math.random()-.5)*.008,windOffset:Math.random()*Math.PI*2,windSpeed:.008+Math.random()*.015,glowPhase:Math.random()*Math.PI*2,floatPhase:Math.random()*Math.PI*2,floatSpeed:.001+Math.random()*.002,active:!0}},createGravestone(e,t,o={}){const r=["rounded","cross","obelisk"];return{type:"gravestone",x:o.x!==void 0?o.x:Math.random()*e,y:o.y!==void 0?o.y:t-30-Math.random()*20,size:15+Math.random()*10,opacity:.7+Math.random()*.2,shape:r[Math.floor(Math.random()*r.length)],rotation:(Math.random()-.5)*.1,active:!0,static:!0}},createJackOLantern(e,t,o={}){const r=["happy","scary","wicked"];return{type:"jack-o-lantern",x:o.x!==void 0?o.x:Math.random()*e,y:o.y!==void 0?o.y:t-25-Math.random()*15,size:12+Math.random()*8,opacity:.9+Math.random()*.1,face:r[Math.floor(Math.random()*r.length)],glowPhase:Math.random()*Math.PI*2,active:!0,static:!0}},createSpider(e,t,o={}){return{type:"spider",x:o.x!==void 0?o.x:Math.random()*e,y:o.y!==void 0?o.y:Math.random()*t*.3,baseY:o.y!==void 0?o.y:Math.random()*t*.3,size:5+Math.random()*5,opacity:.8+Math.random()*.2,threadLength:30+Math.random()*50,swingPhase:Math.random()*Math.PI*2,swingAmplitude:20+Math.random()*30,swingSpeed:.001+Math.random()*.002,time:0,active:!0,static:!1}},createFallingParticle(e,t,o){return Math.random()<.7?this.createPumpkin(e,t,o):{type:"spider-small",x:Math.random()*e,y:-20,size:1+Math.random()*2,speed:o.speedRange[0]+Math.random()*(o.speedRange[1]-o.sizeRange[0])*.5,opacity:.6+Math.random()*.3,rotation:Math.random()*Math.PI*2,rotationSpeed:(Math.random()-.5)*.02,windOffset:Math.random()*Math.PI*2,windSpeed:.01+Math.random()*.02,active:!0}},createInitialDecorations(e,t,o){const r=[];r.push({type:"haunted-house",x:80,y:t-120,size:80,opacity:.85,active:!0,static:!0}),r.push({type:"moon",x:e-120,y:100,size:60,opacity:.9,glowPhase:0,active:!0,static:!0});const n=o.gravestones??5;for(let l=0;l<n;l++)r.push({type:"gravestone",x:e/(n+1)*(l+1)+(Math.random()-.5)*40,y:t-30-Math.random()*10,size:12+Math.random()*8,opacity:.7+Math.random()*.2,variant:Math.floor(Math.random()*3),active:!0,static:!0});const a=o.jackOLanterns??4;for(let l=0;l<a;l++)r.push({type:"jack-o-lantern",x:e/(a+1)*(l+1)+(Math.random()-.5)*30,y:t-15,size:10+Math.random()*6,opacity:.9,glowPhase:Math.random()*Math.PI*2,active:!0,static:!0});const i=o.spiders??4;for(let l=0;l<i;l++)r.push({type:"spider",x:e/(i+1)*(l+1),y:0,targetY:50+Math.random()*100,currentY:0,size:3+Math.random()*3,opacity:.8,swingPhase:Math.random()*Math.PI*2,swingAmplitude:20+Math.random()*30,swingSpeed:.02+Math.random()*.02,active:!0,static:!0});const s=o.scarecrows??2;for(let l=0;l<s;l++)r.push({type:"scarecrow",x:120+l/(s-1||1)*(e-240),y:t-60,size:30+Math.random()*10,opacity:.9,swayPhase:Math.random()*Math.PI*2,swaySpeed:.015+Math.random()*.015,glowPhase:Math.random()*Math.PI*2,glowSpeed:.02,active:!0,static:!0});const h=o.twinklingStars??15;for(let l=0;l<h;l++)r.push(this.createTwinklingStar(e,t));return r},createTwinklingStar(e,t){return{type:"twinkling-star",x:Math.random()*e,y:Math.random()*(t*.5),size:1+Math.random()*2.5,opacity:.5+Math.random()*.3,twinklePhase:Math.random()*Math.PI*2,twinkleSpeed:.004+Math.random()*.003,colorVariant:Math.floor(Math.random()*3),active:!0,static:!0}},drawLightning(e,t,o,r,n,a,i,s,h,l,d){if(a<1)return;const y=(t+r)/2,u=(o+n)/2,M=Math.atan2(n-o,r-t),P=M+Math.PI/2,w=(Math.random()-.5)*i,m=y+Math.cos(P)*w,g=u+Math.sin(P)*w,f=a-1,p=i*s,z=l*.8;if(e.strokeStyle=d,e.lineWidth=l,e.lineCap="round",e.lineJoin="round",e.beginPath(),e.moveTo(t,o),e.lineTo(m,g),e.lineTo(r,n),e.stroke(),f>0&&(this.drawLightning(e,t,o,m,g,f,p,s,h,z,d),this.drawLightning(e,m,g,r,n,f,p,s,h,z,d),Math.random()<h&&f>1)){const v=Math.random()*i*.5,T=(Math.random()-.5)*Math.PI/3,S=m+Math.cos(M+T)*v,k=g+Math.sin(M+T)*v;this.drawLightning(e,m,g,S,k,f-1,p*.5,s,h*.5,z*.6,d)}},spawnSpecialParticle(e,t,o){const r=Math.random(),n=this.intensityConfig.medium;if(r<.15)return this.createBat(t,o,n);if(r<.16){if(e.some(i=>i.type==="witch"))return null;const a=Math.random()<.5;return{type:"witch",x:a?-100:t+100,y:Math.random()*(o*.4)+50,baseY:Math.random()*(o*.4)+50,vx:a?3+Math.random()*2:-(3+Math.random()*2),size:15+Math.random()*8,opacity:.85,waveAmplitude:15+Math.random()*20,waveFrequency:.002+Math.random()*.002,waveOffset:Math.random()*Math.PI*2,time:0,active:!0,static:!1}}return r<.18?{type:"floating-pumpkin",x:Math.random()*t,y:-50,size:8+Math.random()*6,vx:(Math.random()-.5)*.5,vy:.3+Math.random()*.5,opacity:.9,rotation:Math.random()*Math.PI*2,rotationSpeed:(Math.random()-.5)*.01,glowPhase:Math.random()*Math.PI*2,windOffset:Math.random()*Math.PI*2,windSpeed:.015,active:!0}:r<.2102?e.some(a=>a.type==="haunted-house")?null:{type:"haunted-house",x:Math.random()*t*.6+t*.2,y:o-100,size:40+Math.random()*20,opacity:.8,windowFlicker:0,active:!0,static:!0}:r<.2105?e.some(a=>a.type==="cauldron")?null:{type:"cauldron",x:Math.random()*t*.6+t*.2,y:o-40,size:25+Math.random()*10,opacity:1,bubbles:[],smoke:[],time:0,active:!0,static:!0}:null},updateSpecialParticles(e,t,o,r){this.lightningActive?(this.lightningTimer+=t,this.lightningTimer>=this.lightningDuration&&(this.lightningActive=!1,this.lightningTimer=0)):Math.random()<this.lightningChance&&(this.lightningActive=!0,this.lightningTimer=0),e.forEach(n=>{switch(n.time!==void 0&&(n.time+=t),n.type){case"moon":break;case"witch":(n.vx>0&&n.x>o+100||n.vx<0&&n.x<-100)&&(n.active=!1);break;case"floating-pumpkin":n.y>r+50&&(n.active=!1);break;case"cauldron":n.time%100<20&&n.bubbles.push({x:(Math.random()-.5)*n.size*.5,y:n.size*.3+Math.random()*n.size*.2,size:n.size*(.05+Math.random()*.1),vy:-.5-Math.random()*.5,opacity:1,life:1,fadeRate:.01+Math.random()*.005}),n.bubbles=n.bubbles.filter(a=>(a.y+=a.vy,a.vy*=.98,a.opacity-=a.fadeRate,a.opacity>0&&a.y>-n.size)),n.time%200<30&&n.smoke.push({x:(Math.random()-.5)*n.size*.3,y:-n.size*.5+Math.random()*n.size*.1,size:n.size*(.2+Math.random()*.3),vx:(Math.random()-.5)*.1,vy:-.2-Math.random()*.2,opacity:.5,life:1,fadeRate:.005+Math.random()*.003}),n.smoke=n.smoke.filter(a=>(a.x+=a.vx,a.y+=a.vy,a.size*=1.02,a.opacity-=a.fadeRate,a.opacity>0));break}})},drawBat:b,drawGhost:R,drawPumpkin:I,drawGravestone(e,t){const o=t.x,r=t.y,n=t.size;e.save(),e.globalAlpha=t.opacity,e.translate(o,r),e.rotate(t.rotation),e.fillStyle="#555555",e.strokeStyle="#333333",e.lineWidth=1,t.shape==="rounded"?(e.beginPath(),e.moveTo(-n*.5,n*.8),e.lineTo(-n*.5,-n*.3),e.arc(0,-n*.3,n*.5,Math.PI,0),e.lineTo(n*.5,n*.8),e.closePath(),e.fill(),e.stroke()):t.shape==="cross"?(e.fillRect(-n*.15,-n,n*.3,n*1.8),e.fillRect(-n*.5,-n*.3,n,n*.3)):(e.beginPath(),e.moveTo(-n*.4,n*.8),e.lineTo(-n*.5,-n*.5),e.lineTo(0,-n),e.lineTo(n*.5,-n*.5),e.lineTo(n*.4,n*.8),e.closePath(),e.fill(),e.stroke()),e.fillStyle="rgba(0, 100, 0, 0.3)",e.fillRect(-n*.4,n*.5,n*.8,n*.3),e.fillStyle="#222222",e.font=`${n*.4}px serif`,e.textAlign="center",e.textBaseline="middle",e.fillText("RIP",0,0),e.restore()},drawJackOLantern:C,drawHauntedHouse:A,drawCauldron(e,t,o){const r=t.x,n=t.y,a=t.size;e.save(),e.translate(r,n),e.fillStyle="#222222",e.strokeStyle="#111111",e.lineWidth=2,e.beginPath(),e.arc(0,0,a*.8,0,Math.PI),e.lineTo(-a*.8,a*.5),e.quadraticCurveTo(0,a*.8,a*.8,a*.5),e.closePath(),e.fill(),e.stroke(),e.strokeStyle="#1a1a1a",e.lineWidth=3,[-a*.5,0,a*.5].forEach(i=>{e.beginPath(),e.moveTo(i,a*.5),e.lineTo(i,a*.9),e.stroke()}),e.fillStyle="#00ff00",e.shadowColor="#00ff00",e.shadowBlur=15,e.beginPath(),e.ellipse(0,0,a*.7,a*.3,0,0,Math.PI*2),e.fill(),e.shadowBlur=0,t.bubbles.forEach(i=>{e.globalAlpha=i.opacity,e.fillStyle="#88ff88",e.beginPath(),e.arc(i.x,i.y,i.size,0,Math.PI*2),e.fill()}),e.globalAlpha=1,t.smoke.forEach(i=>{e.globalAlpha=i.opacity,e.fillStyle="#aaaaaa",e.beginPath(),e.arc(i.x,i.y,i.size,0,Math.PI*2),e.fill()}),e.restore()},drawSpider(e,t,o){const r=t.x,n=t.y,a=t.size,i=Math.sin(o*t.swingSpeed+t.swingPhase)*t.swingAmplitude;e.save(),e.globalAlpha=t.opacity,e.strokeStyle="#cccccc",e.lineWidth=1,e.setLineDash([2,2]),e.beginPath(),e.moveTo(r,n),e.lineTo(r+i,n+t.threadLength),e.stroke(),e.setLineDash([]),e.translate(r+i,n+t.threadLength),e.fillStyle="#000000",e.beginPath(),e.ellipse(0,0,a*.5,a*.7,0,0,Math.PI*2),e.fill(),e.beginPath(),e.arc(0,-a*.5,a*.3,0,Math.PI*2),e.fill(),e.strokeStyle="#1a1a1a",e.lineWidth=a*.1;const s=Math.sin(o*.005)*.2;for(let h=0;h<4;h++){const l=h/4*Math.PI-Math.PI/2,d=a*1.5;e.beginPath(),e.moveTo(-a*.3,-a*.2+h*a*.2),e.quadraticCurveTo(-a-Math.sin(l+s)*a*.3,-a*.2+h*a*.2-a*.5,-a*1.2,-a*.2+h*a*.2),e.stroke(),e.beginPath(),e.moveTo(a*.3,-a*.2+h*a*.2),e.quadraticCurveTo(a+Math.sin(l+s)*a*.3,-a*.2+h*a*.2-a*.5,a*1.2,-a*.2+h*a*.2),e.stroke()}e.fillStyle="#ff0000";for(let h=0;h<4;h++)e.beginPath(),e.arc(-a*.15+h%2*a*.3,-a*.55+Math.floor(h/2)*a*.1,a*.05,0,Math.PI*2),e.fill();e.restore()},drawSpiderSmall(e,t){e.save(),e.globalAlpha=t.opacity,e.translate(t.x,t.y),e.rotate(t.rotation),e.fillStyle="#1a1a1a",e.beginPath(),e.arc(0,0,t.size,0,Math.PI*2),e.fill(),e.strokeStyle="#1a1a1a",e.lineWidth=t.size*.3;for(let o=0;o<4;o++){const r=o/4*Math.PI-Math.PI/2;e.beginPath(),e.moveTo(0,0),e.lineTo(Math.cos(r)*t.size*2,Math.sin(r)*t.size*2),e.stroke(),e.beginPath(),e.moveTo(0,0),e.lineTo(-Math.cos(r)*t.size*2,Math.sin(r)*t.size*2),e.stroke()}e.restore()},drawWitch(e,t,o){const r=t.x,n=t.y+Math.sin(o*t.waveFrequency+t.waveOffset)*t.waveAmplitude,a=t.size,i=t.vx>0?1:-1;e.save(),e.globalAlpha=t.opacity,e.translate(r,n),i===-1&&e.scale(-1,1),e.strokeStyle="#8B4513",e.lineWidth=a*.12,e.lineCap="round",e.beginPath(),e.moveTo(a*.2,a*.6),e.lineTo(-a*1.5,a*.6),e.stroke(),e.strokeStyle="#D2691E",e.lineWidth=a*.06;for(let s=0;s<8;s++){const h=Math.PI/3*(s/7-.5);e.beginPath(),e.moveTo(-a*1.5,a*.6),e.lineTo(-a*1.5+Math.cos(h)*a*.4,a*.6+Math.sin(h)*a*.4),e.stroke()}e.fillStyle="#1a1a1a",e.strokeStyle="#0a0a0a",e.lineWidth=1,e.beginPath(),e.moveTo(a*.2,a*.6),e.quadraticCurveTo(-a*.5,a*.4,-a*.8,a*.1),e.lineTo(-a*.7,-a*.3),e.quadraticCurveTo(-a*.3,-a*.5,0,-a*.6),e.lineTo(a*.3,-a*.4),e.lineTo(a*.3,a*.5),e.closePath(),e.fill(),e.stroke(),e.fillStyle="#8B9355",e.beginPath(),e.ellipse(a*.25,-a*.6,a*.3,a*.35,0,0,Math.PI*2),e.fill(),e.fillStyle="#7a8245",e.beginPath(),e.moveTo(a*.45,-a*.6),e.quadraticCurveTo(a*.7,-a*.65,a*.72,-a*.5),e.lineTo(a*.6,-a*.52),e.quadraticCurveTo(a*.58,-a*.58,a*.4,-a*.55),e.closePath(),e.fill(),e.fillStyle="#2C1810",e.beginPath(),e.moveTo(a*.05,-a*.85),e.lineTo(a*.4,-a*1.6),e.lineTo(a*.5,-a*.8),e.closePath(),e.fill(),e.beginPath(),e.ellipse(a*.25,-a*.85,a*.5,a*.12,0,0,Math.PI*2),e.fill(),e.fillStyle="#ff6600",e.shadowColor="#ff6600",e.shadowBlur=a*.3,e.beginPath(),e.arc(a*.35,-a*.65,a*.08,0,Math.PI*2),e.fill(),e.shadowBlur=0,e.fillStyle="#8B9355",e.beginPath(),e.arc(a*.1,a*.5,a*.12,0,Math.PI*2),e.fill(),e.beginPath(),e.arc(-a*.3,a*.55,a*.1,0,Math.PI*2),e.fill(),e.strokeStyle="#1a1a1a",e.lineWidth=a*.1,e.beginPath(),e.moveTo(a*.15,a*.6),e.lineTo(a*.1,a*1),e.stroke(),e.beginPath(),e.moveTo(a*.05,a*.6),e.lineTo(a*.2,a*.95),e.stroke(),e.restore()},drawFloatingPumpkin(e,t){const o=t.x,r=t.y,n=t.size;e.save(),e.globalAlpha=t.opacity,e.translate(o,r),e.rotate(t.rotation),e.fillStyle="#ff6600",e.strokeStyle="#cc5200",e.lineWidth=n*.1,e.beginPath(),e.ellipse(0,0,n,n*.9,0,0,Math.PI*2),e.fill(),e.stroke();for(let i=-2;i<=2;i++)e.beginPath(),e.moveTo(i*n*.25,-n*.8),e.quadraticCurveTo(i*n*.3,0,i*n*.25,n*.8),e.stroke();e.fillStyle="#228B22",e.beginPath(),e.moveTo(-n*.15,-n*.8),e.lineTo(-n*.1,-n*1.1),e.lineTo(n*.1,-n*1.1),e.lineTo(n*.15,-n*.8),e.closePath(),e.fill();const a=.5+Math.sin(t.glowPhase+Date.now()*.005)*.5;e.fillStyle=`rgba(255, 200, 0, ${a})`,e.beginPath(),e.moveTo(-n*.4,-n*.3),e.lineTo(-n*.2,-n*.1),e.lineTo(-n*.6,-n*.1),e.closePath(),e.fill(),e.beginPath(),e.moveTo(n*.4,-n*.3),e.lineTo(n*.6,-n*.1),e.lineTo(n*.2,-n*.1),e.closePath(),e.fill(),e.beginPath(),e.moveTo(-n*.5,n*.2);for(let i=-4;i<=4;i++)e.lineTo(i*n*.12,n*.2+(i%2===0?n*.15:0));e.lineTo(n*.5,n*.2),e.closePath(),e.fill(),e.restore()},drawMoon(e,t,o){const r=t.x,n=t.y,a=t.size;e.save(),e.globalAlpha=t.opacity,e.translate(r,n);const i=.8+Math.sin(o*.001+t.glowPhase)*.2,s=e.createRadialGradient(0,0,0,0,0,a*1.5);s.addColorStop(0,`rgba(255, 255, 220, ${i})`),s.addColorStop(.4,`rgba(255, 255, 200, ${i*.5})`),s.addColorStop(.7,`rgba(255, 220, 150, ${i*.2})`),s.addColorStop(1,"rgba(255, 200, 100, 0)"),e.fillStyle=s,e.fillRect(-a*1.5,-a*1.5,a*3,a*3),e.fillStyle="#ffffdc",e.shadowColor="#ffffaa",e.shadowBlur=a*.3,e.beginPath(),e.arc(0,0,a,0,Math.PI*2),e.fill(),e.shadowBlur=0,e.fillStyle="rgba(200, 200, 170, 0.4)",e.beginPath(),e.arc(-a*.3,-a*.2,a*.2,0,Math.PI*2),e.fill(),e.beginPath(),e.arc(a*.4,a*.1,a*.15,0,Math.PI*2),e.fill(),e.beginPath(),e.arc(a*.1,a*.4,a*.18,0,Math.PI*2),e.fill(),e.beginPath(),e.arc(-a*.2,a*.5,a*.12,0,Math.PI*2),e.fill(),e.restore()},drawScarecrow:L,drawTwinklingStar(e,t,o){const r=t.x,n=t.y,a=t.size,i=.4+Math.sin(o*t.twinkleSpeed+t.twinklePhase)*.6;let s,h;switch(t.colorVariant){case 0:s=`rgba(255, 255, 255, ${i})`,h=`rgba(255, 255, 255, ${i*.3})`;break;case 1:s=`rgba(200, 150, 255, ${i})`,h=`rgba(150, 100, 255, ${i*.3})`;break;case 2:s=`rgba(255, 180, 100, ${i})`,h=`rgba(255, 150, 50, ${i*.3})`;break}e.save(),e.translate(r,n),e.shadowColor=h,e.shadowBlur=a*4*i,e.fillStyle=s,e.beginPath();for(let l=0;l<4;l++){const d=l/4*Math.PI-Math.PI/2,y=Math.cos(d)*a,u=Math.sin(d)*a,M=d+Math.PI/4,P=Math.cos(M)*(a*.3),w=Math.sin(M)*(a*.3);l===0?e.moveTo(y,u):e.lineTo(y,u),e.lineTo(P,w)}e.closePath(),e.fill(),e.shadowBlur=a*2*i,e.beginPath(),e.arc(0,0,a*.3,0,Math.PI*2),e.fill(),e.restore()},drawGlobalEffects(e,t,o,r){if(this.lightningActive){e.save(),e.globalAlpha=.8+Math.sin(this.lightningTimer*.01)*.2,e.shadowColor=this.lightningColor,e.shadowBlur=20;const n=o*(.4+Math.random()*.2),a=0,i=o*Math.random(),s=r;this.drawLightning(e,n,a,i,s,5+Math.floor(Math.random()*3),50+Math.random()*50,.7,.3,3+Math.random()*2,this.lightningColor),e.restore()}}};
@@ -1 +1 @@
1
- export default{name:"valentines",displayName:"Valentine's Day",emoji:"\u{1F495}",intensityConfig:{light:{count:40,speedRange:[.3,.8],sizeRange:[2,4],garlands:1,cupidChance:3e-4},medium:{count:80,speedRange:[.4,1.2],sizeRange:[2,5],garlands:2,cupidChance:5e-4},heavy:{count:150,speedRange:[.5,1.5],sizeRange:[3,6],garlands:3,cupidChance:8e-4}},particles:["heart","rose-petal","sparkle","lips"],decorations:["cupid","love-letter","heart-garland","envelope","butterfly","heart-moon","neon-sign"],colors:{primary:"#ff1493",secondary:"#ff69b4",accent:"#dc143c",light:"#ffb6c1",gold:"#ffd700"},createHeart(e,o,n){const s=Math.random();let l,a,h;const i=["#ff1493","#ff69b4","#dc143c","#ffb6c1","#ff0080"];return s<.33?(l=n.sizeRange[0]+Math.random()*(n.sizeRange[1]-n.sizeRange[0])*1.5,a=n.speedRange[0]+Math.random()*(n.speedRange[1]-n.speedRange[0]),h=.8+Math.random()*.2):s<.66?(l=n.sizeRange[0]+Math.random()*(n.sizeRange[1]-n.sizeRange[0]),a=n.speedRange[0]+Math.random()*(n.speedRange[1]-n.speedRange[0]),h=.6+Math.random()*.2):(l=n.sizeRange[0]+Math.random()*(n.sizeRange[1]-n.sizeRange[0])*.8,a=n.speedRange[0]+Math.random()*(n.speedRange[1]-n.speedRange[0])*.8,h=.4+Math.random()*.2),{type:"heart",x:Math.random()*e,y:Math.random()*o,size:l,vx:(Math.random()-.5)*.3,vy:(Math.random()-.7)*.2,speed:a*.3,opacity:h,color:i[Math.floor(Math.random()*i.length)],windOffset:Math.random()*Math.PI*2,windSpeed:.005+Math.random()*.01,rotation:Math.random()*Math.PI*2,rotationSpeed:(Math.random()-.5)*.01,pulsePhase:Math.random()*Math.PI*2,pulseSpeed:.002+Math.random()*.003,floatPhase:Math.random()*Math.PI*2,floatSpeed:.001+Math.random()*.002,active:!0,depth:s}},createRosePetal(e,o,n){const s=["#ff1493","#ff69b4","#dc143c","#ff758f","#ffb6c1"];return{type:"rose-petal",x:Math.random()*e,y:-20,size:1.5+Math.random()*2,speed:n.speedRange[0]+Math.random()*(n.speedRange[1]-n.speedRange[0])*.6,opacity:.7+Math.random()*.3,color:s[Math.floor(Math.random()*s.length)],windOffset:Math.random()*Math.PI*2,windSpeed:.02+Math.random()*.03,rotation:Math.random()*Math.PI*2,rotationSpeed:(Math.random()-.5)*.05,flutterPhase:Math.random()*Math.PI*2,active:!0}},createSparkle(e,o,n){return{type:"sparkle",x:Math.random()*e,y:Math.random()*o,size:.5+Math.random()*1.5,opacity:1,life:20+Math.random()*30,maxLife:20+Math.random()*30,color:Math.random()<.5?"#ffd700":"#ffffff",twinklePhase:Math.random()*Math.PI*2,active:!0,static:!0}},createLips(e,o,n){const s=["#dc143c","#ff1493","#c71585","#ff0066"];return{type:"lips",x:Math.random()*e,y:Math.random()*o,size:3+Math.random()*3,vx:(Math.random()-.5)*.4,vy:(Math.random()-.7)*.2,speed:.3+Math.random()*.2,opacity:.8+Math.random()*.2,color:s[Math.floor(Math.random()*s.length)],rotation:Math.random()*Math.PI*2,rotationSpeed:(Math.random()-.5)*.01,pulsePhase:Math.random()*Math.PI*2,pulseSpeed:.003+Math.random()*.002,floatPhase:Math.random()*Math.PI*2,floatSpeed:.001+Math.random()*.002,kissPhase:Math.random()*Math.PI*2,active:!0}},createHeartGarland(e,o,n={}){const s=8+Math.floor(Math.random()*5),l=[];for(let a=0;a<s;a++)l.push({offset:a/s,size:8+Math.random()*4,color:Math.random()<.5?"#ff1493":"#ff69b4",pulsePhase:Math.random()*Math.PI*2});return{type:"heart-garland",x:0,y:n.y!==void 0?n.y:50+Math.random()*100,width:e,hearts:l,sag:40+Math.random()*30,swayPhase:Math.random()*Math.PI*2,swayAmplitude:10+Math.random()*10,opacity:.8+Math.random()*.2,active:!0,static:!0}},createLoveLetter(e,o,n={}){const s=Math.random()<.5;return{type:"love-letter",x:s?-50:e+50,y:Math.random()*o*.6,baseY:Math.random()*o*.6,vx:s?1+Math.random()*1:-(1+Math.random()*1),size:12+Math.random()*6,opacity:.9+Math.random()*.1,rotation:(Math.random()-.5)*.3,rotationSpeed:(Math.random()-.5)*.005,waveAmplitude:15+Math.random()*15,waveFrequency:.002+Math.random()*.002,waveOffset:Math.random()*Math.PI*2,time:0,active:!0,static:!1}},createFallingParticle(e,o,n){const s=Math.random();return s<.6?this.createHeart(e,o,n):s<.85?this.createRosePetal(e,o,n):this.createLips(e,o,n)},createInitialDecorations(e,o,n){const s=[],l=n.garlands??2;for(let i=0;i<l;i++)s.push(this.createHeartGarland(e,o,{y:50+i*(o/(l+1))}));const a=4;for(let i=0;i<a;i++)s.push({type:"envelope",x:e/(a+1)*(i+1)+(Math.random()-.5)*30,y:o-25-Math.random()*10,size:15+Math.random()*5,opacity:.85,rotation:(Math.random()-.5)*.3,active:!0,static:!0});const h=n.butterflies??3;for(let i=0;i<h;i++)s.push({type:"butterfly",x:Math.random()*e,y:Math.random()*o*.6+50,baseY:Math.random()*o*.6+50,size:12+Math.random()*6,vx:(Math.random()-.5)*2,vy:0,waveOffset:Math.random()*Math.PI*2,waveFrequency:.003+Math.random()*.002,waveAmplitude:20+Math.random()*15,wingPhase:Math.random()*Math.PI*2,opacity:.9,color:Math.random()<.5?"pink":"purple",active:!0,static:!1});return s.push({type:"heart-moon",x:e*.85,y:o*.15,size:60+Math.random()*20,opacity:.9,glowPhase:Math.random()*Math.PI*2,active:!0,static:!0}),s},spawnSpecialParticle(e,o,n,s){const l=Math.random();if(l<s.cupidChance){if(e.some(h=>h.type==="cupid"))return null;const a=Math.random()<.5;return{type:"cupid",x:a?-100:o+100,y:Math.random()*n*.4+50,baseY:Math.random()*n*.4+50,vx:a?2+Math.random()*1.5:-(2+Math.random()*1.5),size:15+Math.random()*8,opacity:1,waveAmplitude:20+Math.random()*20,waveFrequency:.002+Math.random()*.002,waveOffset:Math.random()*Math.PI*2,time:0,wingPhase:Math.random()*Math.PI*2,active:!0,static:!1}}return l<.001?this.createLoveLetter(o,n):l<.0015?e.some(a=>a.type==="neon-sign"&&a.active)?null:{type:"neon-sign",x:o/2,y:n*.3,size:40+Math.random()*20,opacity:0,targetOpacity:1,phase:"fade-in",phaseTime:0,fadeInDuration:1500,stableDuration:5e3,fadeOutDuration:1e3,crackleIntensity:1,flickerPhase:Math.random()*Math.PI*2,glowPhase:Math.random()*Math.PI*2,tubeFlickers:[],active:!0,static:!1}:l<.02?this.createSparkle(o,n,s):null},drawHeart(e,o,n){const s=o.x,l=o.y,a=o.size,h=1+Math.sin(n*o.pulseSpeed+o.pulsePhase)*.15;e.save(),e.globalAlpha=o.opacity,e.translate(s,l),e.rotate(o.rotation),e.scale(h,h),e.fillStyle=o.color,e.shadowColor=o.color,e.shadowBlur=a*.8,e.beginPath(),e.moveTo(0,a*.3),e.bezierCurveTo(-a,-a*.3,-a*.6,-a,0,-a*.4),e.bezierCurveTo(a*.6,-a,a,-a*.3,0,a*.3),e.closePath(),e.fill(),e.shadowBlur=0,e.fillStyle="rgba(255, 255, 255, 0.3)",e.beginPath(),e.arc(-a*.25,-a*.5,a*.15,0,Math.PI*2),e.fill(),e.restore()},drawRosePetal(e,o,n){const s=o.x,l=o.y,a=o.size,h=Math.sin(n*.01+o.flutterPhase)*.3;e.save(),e.globalAlpha=o.opacity,e.translate(s,l),e.rotate(o.rotation+h),e.fillStyle=o.color,e.strokeStyle="rgba(139, 0, 0, 0.3)",e.lineWidth=.5,e.beginPath(),e.moveTo(0,0),e.bezierCurveTo(a*2,-a,a*2,a,0,0),e.closePath(),e.fill(),e.beginPath(),e.moveTo(0,0),e.quadraticCurveTo(a,0,a*1.5,0),e.stroke(),e.restore()},drawSparkle(e,o,n){const s=o.x,l=o.y,a=o.size,h=o.life/o.maxLife,i=(Math.sin(n*.01+o.twinklePhase)+1)*.5;e.save(),e.globalAlpha=o.opacity*h*i,e.translate(s,l),e.fillStyle=o.color,e.shadowColor=o.color,e.shadowBlur=a*2,e.beginPath();for(let r=0;r<4;r++){const d=r/4*Math.PI*2-Math.PI/2,f=Math.cos(d)*a*3,t=Math.sin(d)*a*3,M=d+Math.PI/4,u=Math.cos(M)*a*.5,P=Math.sin(M)*a*.5;r===0?e.moveTo(f,t):(e.lineTo(u,P),e.lineTo(f,t))}e.closePath(),e.fill(),e.restore()},drawLips(e,o,n){const s=o.x,l=o.y,a=o.size,h=1+Math.sin(n*o.pulseSpeed+o.pulsePhase)*.1,i=Math.sin(n*.008+o.kissPhase);e.save(),e.globalAlpha=o.opacity,e.translate(s,l),e.rotate(o.rotation),e.scale(h,h);const r=e.createRadialGradient(0,0,0,0,0,a*2);r.addColorStop(0,o.color),r.addColorStop(.6,o.color),r.addColorStop(1,"#8b0000"),e.fillStyle=r,e.strokeStyle="#8b0000",e.lineWidth=a*.1,e.beginPath(),e.moveTo(-a,0),e.bezierCurveTo(-a*.8,-a*.8,-a*.3,-a*1,0,-a*.6),e.bezierCurveTo(a*.3,-a*1,a*.8,-a*.8,a,0),e.fill(),e.stroke();const d=i*a*.15;if(e.beginPath(),e.moveTo(-a,0),e.bezierCurveTo(-a*.7,a*(.7+d),-a*.3,a*(1+d),0,a*(.8+d)),e.bezierCurveTo(a*.3,a*(1+d),a*.7,a*(.7+d),a,0),e.fill(),e.stroke(),e.globalAlpha=o.opacity*.5,e.fillStyle="rgba(255, 255, 255, 0.6)",e.beginPath(),e.ellipse(-a*.4,-a*.4,a*.2,a*.15,-.3,0,Math.PI*2),e.fill(),e.beginPath(),e.ellipse(a*.4,-a*.4,a*.2,a*.15,.3,0,Math.PI*2),e.fill(),e.beginPath(),e.ellipse(0,a*.5,a*.3,a*.2,0,0,Math.PI*2),e.fill(),i>.8){const f=a*1.5,t=Math.sin(o.kissPhase)*f,M=-Math.abs(Math.cos(o.kissPhase))*f;e.globalAlpha=o.opacity*(i-.8)*5,e.fillStyle="#ffd700",e.shadowColor="#ffd700",e.shadowBlur=a*.5,e.beginPath(),e.arc(t,M,a*.15,0,Math.PI*2),e.fill();for(let u=0;u<4;u++){const P=u/4*Math.PI*2+n*.01,m=t+Math.cos(P)*a*.4,y=M+Math.sin(P)*a*.4;e.beginPath(),e.moveTo(t,M),e.lineTo(m,y),e.lineWidth=a*.05,e.strokeStyle="#ffffff",e.stroke()}}e.restore()},drawCupid(e,o,n){const s=o.x,l=o.y,a=o.size,h=o.vx>0?1:-1;e.save(),e.globalAlpha=o.opacity,e.translate(s,l),h===-1&&e.scale(-1,1);const i=Math.sin(n*.015+o.wingPhase)*(Math.PI/6);e.fillStyle="#ffffff",e.strokeStyle="#ffb6c1",e.lineWidth=1.5,e.save(),e.translate(-a*.2,-a*.3),e.rotate(-i),e.beginPath(),e.moveTo(0,0),e.bezierCurveTo(-a*1.2,-a*.8,-a*1.5,-a*.3,-a*1.2,a*.3),e.bezierCurveTo(-a*.8,a*.2,-a*.3,0,0,0),e.closePath(),e.fill(),e.stroke(),e.restore(),e.fillStyle="#ffdbac",e.beginPath(),e.ellipse(0,0,a*.4,a*.5,0,0,Math.PI*2),e.fill(),e.beginPath(),e.ellipse(a*.3,-a*.6,a*.35,a*.4,0,0,Math.PI*2),e.fill(),e.fillStyle="#ffd700",e.beginPath(),e.arc(a*.1,-a*.9,a*.22,0,Math.PI*2),e.fill(),e.beginPath(),e.arc(a*.4,-a*.95,a*.18,0,Math.PI*2),e.fill(),e.beginPath(),e.arc(a*.15,-a*.75,a*.15,0,Math.PI*2),e.fill(),e.fillStyle="#000000",e.beginPath(),e.arc(a*.42,-a*.62,a*.05,0,Math.PI*2),e.fill(),e.fillStyle="rgba(255, 105, 180, 0.3)",e.beginPath(),e.arc(a*.25,-a*.5,a*.12,0,Math.PI*2),e.fill(),e.strokeStyle="#ffdbac",e.lineWidth=a*.15,e.lineCap="round",e.beginPath(),e.moveTo(-a*.15,a*.4),e.lineTo(-a*.3,a*.8),e.stroke(),e.beginPath(),e.moveTo(-a*.05,a*.4),e.lineTo(-a*.15,a*.75),e.stroke(),e.strokeStyle="#ffdbac",e.lineWidth=a*.12,e.beginPath(),e.moveTo(a*.1,-a*.2),e.lineTo(a*.6,-a*.3),e.stroke(),e.beginPath(),e.moveTo(a*.05,0),e.lineTo(a*.9,-a*.35),e.stroke(),e.strokeStyle="#8B4513",e.lineWidth=2.5,e.beginPath(),e.moveTo(a*.9,-a*.6),e.quadraticCurveTo(a*1.1,-a*.35,a*.9,-a*.1),e.stroke(),e.strokeStyle="#daa520",e.lineWidth=1.5,e.beginPath(),e.moveTo(a*.9,-a*.6),e.lineTo(a*.6,-a*.35),e.lineTo(a*.9,-a*.1),e.stroke(),e.strokeStyle="#8B4513",e.lineWidth=2,e.beginPath(),e.moveTo(a*.6,-a*.35),e.lineTo(a*1.8,-a*.35),e.stroke(),e.fillStyle="#dc143c",e.beginPath(),e.moveTo(a*1.8,-a*.35),e.lineTo(a*2,-a*.35),e.lineTo(a*1.9,-a*.25),e.closePath(),e.fill(),e.beginPath(),e.moveTo(a*1.8,-a*.35),e.lineTo(a*2,-a*.35),e.lineTo(a*1.9,-a*.45),e.closePath(),e.fill(),e.fillStyle="#ff69b4",e.beginPath(),e.moveTo(a*.6,-a*.35),e.lineTo(a*.5,-a*.25),e.lineTo(a*.5,-a*.45),e.closePath(),e.fill(),e.fillStyle="#ffffff",e.strokeStyle="#ffb6c1",e.lineWidth=1.5,e.save(),e.translate(a*.1,-a*.3),e.rotate(i),e.beginPath(),e.moveTo(0,0),e.bezierCurveTo(-a*1,-a*.6,-a*1.3,-a*.2,-a*1,a*.2),e.bezierCurveTo(-a*.6,a*.15,-a*.2,0,0,0),e.closePath(),e.fill(),e.stroke(),e.restore(),e.restore()},drawLoveLetter(e,o,n){const s=o.x,l=o.y,a=o.size;e.save(),e.globalAlpha=o.opacity,e.translate(s,l),e.rotate(o.rotation),e.fillStyle="#fff5e6",e.strokeStyle="#daa520",e.lineWidth=1,e.fillRect(-a,-a*.7,a*2,a*1.4),e.strokeRect(-a,-a*.7,a*2,a*1.4),e.fillStyle="#ffe4b3",e.beginPath(),e.moveTo(-a,-a*.7),e.lineTo(0,0),e.lineTo(a,-a*.7),e.closePath(),e.fill(),e.stroke(),e.fillStyle="#dc143c",e.shadowColor="#dc143c",e.shadowBlur=5,e.beginPath(),e.moveTo(0,a*.1),e.bezierCurveTo(-a*.3,-a*.1,-a*.2,-a*.3,0,-a*.15),e.bezierCurveTo(a*.2,-a*.3,a*.3,-a*.1,0,a*.1),e.closePath(),e.fill(),e.shadowBlur=0,e.restore()},drawHeartGarland(e,o,n){const s=o.y,l=o.width,a=o.sag,h=o.hearts;e.save(),e.globalAlpha=o.opacity;const i=Math.sin(n*.001+o.swayPhase)*o.swayAmplitude;e.strokeStyle="#daa520",e.lineWidth=2,e.beginPath(),e.moveTo(0,s),e.quadraticCurveTo(l/2,s+a+i,l,s),e.stroke(),h.forEach(r=>{const d=r.offset*l,f=s+Math.sin(Math.PI*r.offset)*(a+i),t=1+Math.sin(n*.003+r.pulsePhase)*.1;e.save(),e.translate(d,f),e.scale(t,t),e.fillStyle=r.color,e.shadowColor=r.color,e.shadowBlur=r.size*.5,e.beginPath(),e.moveTo(0,r.size*.3),e.bezierCurveTo(-r.size,-r.size*.3,-r.size*.6,-r.size,0,-r.size*.4),e.bezierCurveTo(r.size*.6,-r.size,r.size,-r.size*.3,0,r.size*.3),e.closePath(),e.fill(),e.shadowBlur=0,e.restore()}),e.restore()},drawEnvelope(e,o,n){const s=o.x,l=o.y,a=o.size;e.save(),e.globalAlpha=o.opacity,e.translate(s,l),e.rotate(o.rotation),e.fillStyle="#fff5f5",e.strokeStyle="#ff69b4",e.lineWidth=a*.05,e.fillRect(-a*.7,-a*.5,a*1.4,a),e.strokeRect(-a*.7,-a*.5,a*1.4,a),e.fillStyle="#ffe4e1",e.beginPath(),e.moveTo(-a*.7,-a*.5),e.lineTo(0,a*.1),e.lineTo(a*.7,-a*.5),e.closePath(),e.fill(),e.stroke(),e.fillStyle="#ff1493",e.shadowColor="#ff1493",e.shadowBlur=a*.3,e.beginPath(),e.moveTo(0,a*.25),e.bezierCurveTo(-a*.3,-a*.1,-a*.2,-a*.3,0,-a*.15),e.bezierCurveTo(a*.2,-a*.3,a*.3,-a*.1,0,a*.25),e.closePath(),e.fill(),e.shadowBlur=0,e.restore()},drawButterfly(e,o,n){const s=o.x+Math.sin(n*o.waveFrequency+o.waveOffset)*o.waveAmplitude,l=o.baseY+Math.cos(n*o.waveFrequency*.7+o.waveOffset)*(o.waveAmplitude*.6),a=o.size,h=o.vx>0?1:-1;e.save(),e.globalAlpha=o.opacity,e.translate(s,l),h===-1&&e.scale(-1,1);const i=Math.sin(n*.015+o.wingPhase)*(Math.PI/6),d={pink:{main:"#ffb3d9",accent:"#ff69b4",outline:"#ff1493"},purple:{main:"#dda0dd",accent:"#ba55d3",outline:"#9932cc"}}[o.color];e.save(),e.translate(-a*.15,0),e.rotate(-i),e.fillStyle=d.main,e.strokeStyle=d.outline,e.lineWidth=a*.03,e.beginPath(),e.moveTo(0,0),e.bezierCurveTo(-a*.8,-a*.6,-a*.5,-a*1.2,0,-a*.5),e.closePath(),e.fill(),e.stroke(),e.beginPath(),e.moveTo(0,0),e.bezierCurveTo(-a*.7,a*.4,-a*.4,a*.9,0,a*.4),e.closePath(),e.fill(),e.stroke(),e.fillStyle=d.accent,e.beginPath(),e.arc(-a*.4,-a*.5,a*.12,0,Math.PI*2),e.fill(),e.beginPath(),e.arc(-a*.35,a*.35,a*.1,0,Math.PI*2),e.fill(),e.restore(),e.save(),e.translate(a*.15,0),e.rotate(i),e.fillStyle=d.main,e.strokeStyle=d.outline,e.lineWidth=a*.03,e.beginPath(),e.moveTo(0,0),e.bezierCurveTo(a*.8,-a*.6,a*.5,-a*1.2,0,-a*.5),e.closePath(),e.fill(),e.stroke(),e.beginPath(),e.moveTo(0,0),e.bezierCurveTo(a*.7,a*.4,a*.4,a*.9,0,a*.4),e.closePath(),e.fill(),e.stroke(),e.fillStyle=d.accent,e.beginPath(),e.arc(a*.4,-a*.5,a*.12,0,Math.PI*2),e.fill(),e.beginPath(),e.arc(a*.35,a*.35,a*.1,0,Math.PI*2),e.fill(),e.restore(),e.fillStyle="#4a4a4a",e.fillRect(-a*.05,-a*.5,a*.1,a*.9),e.fillStyle="#2a2a2a",e.beginPath(),e.arc(0,-a*.55,a*.12,0,Math.PI*2),e.fill(),e.strokeStyle="#2a2a2a",e.lineWidth=a*.02,e.beginPath(),e.moveTo(0,-a*.6),e.quadraticCurveTo(-a*.15,-a*.8,-a*.2,-a*.75),e.stroke(),e.beginPath(),e.moveTo(0,-a*.6),e.quadraticCurveTo(a*.15,-a*.8,a*.2,-a*.75),e.stroke(),e.fillStyle=d.outline,e.beginPath(),e.arc(-a*.2,-a*.75,a*.04,0,Math.PI*2),e.fill(),e.beginPath(),e.arc(a*.2,-a*.75,a*.04,0,Math.PI*2),e.fill(),e.restore()},drawHeartMoon(e,o,n){const s=o.x,l=o.y,a=o.size,h=.3+Math.sin(n*.002+o.glowPhase)*.2;e.save(),e.globalAlpha=o.opacity,e.translate(s,l);const i=e.createRadialGradient(0,0,a*.5,0,0,a*2.5);i.addColorStop(0,`rgba(255, 182, 193, ${h*.8})`),i.addColorStop(.4,`rgba(255, 105, 180, ${h*.5})`),i.addColorStop(.7,`rgba(255, 20, 147, ${h*.3})`),i.addColorStop(1,"rgba(255, 20, 147, 0)"),e.fillStyle=i,e.beginPath(),e.arc(0,0,a*2.5,0,Math.PI*2),e.fill();const r=e.createRadialGradient(-a*.3,-a*.3,0,0,0,a);r.addColorStop(0,"#ffcce0"),r.addColorStop(.5,"#ffb3d9"),r.addColorStop(1,"#ff69b4"),e.fillStyle=r,e.beginPath(),e.moveTo(0,a*.3),e.bezierCurveTo(-a*.5,a*.5,-a,a*.2,-a,-a*.2),e.bezierCurveTo(-a,-a*.6,-a*.5,-a*.7,0,-a*.3),e.bezierCurveTo(a*.5,-a*.7,a,-a*.6,a,-a*.2),e.bezierCurveTo(a,a*.2,a*.5,a*.5,0,a*.3),e.closePath(),e.fill(),e.strokeStyle="#ff1493",e.lineWidth=a*.02,e.stroke(),e.fillStyle="rgba(255, 240, 245, 0.4)",e.beginPath(),e.arc(-a*.4,-a*.1,a*.15,0,Math.PI*2),e.fill(),e.beginPath(),e.arc(a*.3,0,a*.12,0,Math.PI*2),e.fill(),e.beginPath(),e.arc(-a*.2,a*.15,a*.1,0,Math.PI*2),e.fill(),e.fillStyle=`rgba(255, 255, 255, ${h})`;const d=[{x:-a*.3,y:-a*.4},{x:a*.2,y:-a*.3},{x:0,y:0},{x:-a*.5,y:a*.1},{x:a*.4,y:a*.15}];for(const f of d){e.save(),e.translate(f.x,f.y),e.beginPath();const t=a*.08;e.moveTo(0,-t),e.lineTo(t*.2,-t*.2),e.lineTo(t,0),e.lineTo(t*.2,t*.2),e.lineTo(0,t),e.lineTo(-t*.2,t*.2),e.lineTo(-t,0),e.lineTo(-t*.2,-t*.2),e.closePath(),e.fill(),e.restore()}e.restore()},drawNeonSign(e,o,n){const s=o.x,l=o.y,a=o.size;if(o.phaseTime+=16,o.phase==="fade-in"&&o.phaseTime>=o.fadeInDuration)o.phase="stable",o.phaseTime=0,o.opacity=o.targetOpacity,o.crackleIntensity=0;else if(o.phase==="stable"&&o.phaseTime>=o.stableDuration)o.phase="fade-out",o.phaseTime=0;else if(o.phase==="fade-out"&&o.phaseTime>=o.fadeOutDuration){o.active=!1;return}o.phase==="fade-in"?(o.opacity=o.phaseTime/o.fadeInDuration*o.targetOpacity,o.crackleIntensity=1-o.phaseTime/o.fadeInDuration):o.phase==="fade-out"&&(o.opacity=o.targetOpacity*(1-o.phaseTime/o.fadeOutDuration)),e.save(),e.translate(s,l);const h="#ff1493",i="#ff0066",r="#ffffff",d="Happy Valentine's Day",f=a;e.font=`bold ${f}px 'Arial Black', sans-serif`,e.textAlign="center",e.textBaseline="middle";const t=e.measureText(d).width;let M=0;o.crackleIntensity>0&&(M=(Math.random()-.5)*o.crackleIntensity*5);const u=d.split("");let P=-t/2;for(let m=0;m<u.length;m++){const y=u[m],b=e.measureText(y).width,T=(o.crackleIntensity>0?Math.random()<o.crackleIntensity*.3:!1)?o.opacity*(.3+Math.random()*.7):o.opacity;e.save(),e.translate(P+b/2,M),e.globalAlpha=T;const v=.8+Math.sin(n*.002+o.glowPhase+m*.5)*.2;e.shadowColor=h,e.shadowBlur=a*.8*v,e.fillStyle=h,e.fillText(y,0,0),e.shadowBlur=a*.5*v,e.fillStyle=i,e.fillText(y,0,0),e.shadowBlur=a*.3*v,e.fillStyle=r,e.fillText(y,0,0),e.shadowBlur=0,e.fillStyle=r,e.globalAlpha=T*.9,e.fillText(y,0,0),e.restore(),P+=b}if(o.crackleIntensity>.3){for(let m=0;m<3;m++)if(Math.random()<o.crackleIntensity*.5){const y=(Math.random()-.5)*t,b=(Math.random()-.5)*a*.3,g=2+Math.random()*3;e.save(),e.globalAlpha=o.opacity*Math.random(),e.fillStyle=Math.random()<.5?h:r,e.shadowColor=h,e.shadowBlur=g*4,e.beginPath(),e.arc(y,b,g,0,Math.PI*2),e.fill(),e.restore()}}(o.phase==="stable"||o.phase==="fade-out")&&(e.globalAlpha=o.opacity*.1,e.fillStyle="#ff1493",e.fillRect(-t*.55,-a*.6,t*1.1,a*1.2)),e.restore()}};
1
+ import{drawIllustratedButterfly as T}from"./butterfly-drawing.js";export default{name:"valentines",displayName:"Valentine's Day",emoji:"\u{1F495}",intensityConfig:{light:{count:40,speedRange:[.3,.8],sizeRange:[2,4],garlands:1,cupidChance:3e-4},medium:{count:80,speedRange:[.4,1.2],sizeRange:[2,5],garlands:2,cupidChance:5e-4},heavy:{count:150,speedRange:[.5,1.5],sizeRange:[3,6],garlands:3,cupidChance:8e-4}},particles:["heart","rose-petal","sparkle","lips"],decorations:["cupid","love-letter","heart-garland","envelope","butterfly","heart-moon","neon-sign"],colors:{primary:"#ff1493",secondary:"#ff69b4",accent:"#dc143c",light:"#ffb6c1",gold:"#ffd700"},createHeart(e,o,s){const n=Math.random();let l,a,i;const t=["#ff1493","#ff69b4","#dc143c","#ffb6c1","#ff0080"];return n<.33?(l=s.sizeRange[0]+Math.random()*(s.sizeRange[1]-s.sizeRange[0])*1.5,a=s.speedRange[0]+Math.random()*(s.speedRange[1]-s.speedRange[0]),i=.8+Math.random()*.2):n<.66?(l=s.sizeRange[0]+Math.random()*(s.sizeRange[1]-s.sizeRange[0]),a=s.speedRange[0]+Math.random()*(s.speedRange[1]-s.speedRange[0]),i=.6+Math.random()*.2):(l=s.sizeRange[0]+Math.random()*(s.sizeRange[1]-s.sizeRange[0])*.8,a=s.speedRange[0]+Math.random()*(s.speedRange[1]-s.speedRange[0])*.8,i=.4+Math.random()*.2),{type:"heart",x:Math.random()*e,y:Math.random()*o,size:l,vx:(Math.random()-.5)*.3,vy:(Math.random()-.7)*.2,speed:a*.3,opacity:i,color:t[Math.floor(Math.random()*t.length)],windOffset:Math.random()*Math.PI*2,windSpeed:.005+Math.random()*.01,rotation:Math.random()*Math.PI*2,rotationSpeed:(Math.random()-.5)*.01,pulsePhase:Math.random()*Math.PI*2,pulseSpeed:.002+Math.random()*.003,floatPhase:Math.random()*Math.PI*2,floatSpeed:.001+Math.random()*.002,active:!0,depth:n}},createRosePetal(e,o,s){const n=["#ff1493","#ff69b4","#dc143c","#ff758f","#ffb6c1"];return{type:"rose-petal",x:Math.random()*e,y:-20,size:1.5+Math.random()*2,speed:s.speedRange[0]+Math.random()*(s.speedRange[1]-s.speedRange[0])*.6,opacity:.7+Math.random()*.3,color:n[Math.floor(Math.random()*n.length)],windOffset:Math.random()*Math.PI*2,windSpeed:.02+Math.random()*.03,rotation:Math.random()*Math.PI*2,rotationSpeed:(Math.random()-.5)*.05,flutterPhase:Math.random()*Math.PI*2,active:!0}},createSparkle(e,o,s){return{type:"sparkle",x:Math.random()*e,y:Math.random()*o,size:.5+Math.random()*1.5,opacity:1,life:20+Math.random()*30,maxLife:20+Math.random()*30,color:Math.random()<.5?"#ffd700":"#ffffff",twinklePhase:Math.random()*Math.PI*2,active:!0,static:!0}},createLips(e,o,s){const n=["#dc143c","#ff1493","#c71585","#ff0066"];return{type:"lips",x:Math.random()*e,y:Math.random()*o,size:3+Math.random()*3,vx:(Math.random()-.5)*.4,vy:(Math.random()-.7)*.2,speed:.3+Math.random()*.2,opacity:.8+Math.random()*.2,color:n[Math.floor(Math.random()*n.length)],rotation:Math.random()*Math.PI*2,rotationSpeed:(Math.random()-.5)*.01,pulsePhase:Math.random()*Math.PI*2,pulseSpeed:.003+Math.random()*.002,floatPhase:Math.random()*Math.PI*2,floatSpeed:.001+Math.random()*.002,kissPhase:Math.random()*Math.PI*2,active:!0}},createHeartGarland(e,o,s={}){const n=8+Math.floor(Math.random()*5),l=[];for(let a=0;a<n;a++)l.push({offset:a/n,size:8+Math.random()*4,color:Math.random()<.5?"#ff1493":"#ff69b4",pulsePhase:Math.random()*Math.PI*2});return{type:"heart-garland",x:0,y:s.y!==void 0?s.y:50+Math.random()*100,width:e,hearts:l,sag:40+Math.random()*30,swayPhase:Math.random()*Math.PI*2,swayAmplitude:10+Math.random()*10,opacity:.8+Math.random()*.2,active:!0,static:!0}},createLoveLetter(e,o,s={}){const n=Math.random()<.5;return{type:"love-letter",x:n?-50:e+50,y:Math.random()*o*.6,baseY:Math.random()*o*.6,vx:n?1+Math.random()*1:-(1+Math.random()*1),size:12+Math.random()*6,opacity:.9+Math.random()*.1,rotation:(Math.random()-.5)*.3,rotationSpeed:(Math.random()-.5)*.005,waveAmplitude:15+Math.random()*15,waveFrequency:.002+Math.random()*.002,waveOffset:Math.random()*Math.PI*2,time:0,active:!0,static:!1}},createFallingParticle(e,o,s){const n=Math.random();return n<.6?this.createHeart(e,o,s):n<.85?this.createRosePetal(e,o,s):this.createLips(e,o,s)},createInitialDecorations(e,o,s){const n=[],l=s.garlands??2;for(let t=0;t<l;t++)n.push(this.createHeartGarland(e,o,{y:50+t*(o/(l+1))}));const a=4;for(let t=0;t<a;t++)n.push({type:"envelope",x:e/(a+1)*(t+1)+(Math.random()-.5)*30,y:o-25-Math.random()*10,size:15+Math.random()*5,opacity:.85,rotation:(Math.random()-.5)*.3,active:!0,static:!0});const i=s.butterflies??3;for(let t=0;t<i;t++)n.push({type:"butterfly",x:Math.random()*e,y:Math.random()*o*.6+50,baseY:Math.random()*o*.6+50,size:12+Math.random()*6,vx:(Math.random()-.5)*2,vy:0,waveOffset:Math.random()*Math.PI*2,waveFrequency:.003+Math.random()*.002,waveAmplitude:20+Math.random()*15,wingPhase:Math.random()*Math.PI*2,opacity:.9,color:Math.random()<.5?"pink":"purple",active:!0,static:!1});return n.push({type:"heart-moon",x:e*.85,y:o*.15,size:60+Math.random()*20,opacity:.9,glowPhase:Math.random()*Math.PI*2,active:!0,static:!0}),n},spawnSpecialParticle(e,o,s,n){const l=Math.random();if(l<n.cupidChance){if(e.some(i=>i.type==="cupid"))return null;const a=Math.random()<.5;return{type:"cupid",x:a?-100:o+100,y:Math.random()*s*.4+50,baseY:Math.random()*s*.4+50,vx:a?2+Math.random()*1.5:-(2+Math.random()*1.5),size:15+Math.random()*8,opacity:1,waveAmplitude:20+Math.random()*20,waveFrequency:.002+Math.random()*.002,waveOffset:Math.random()*Math.PI*2,time:0,wingPhase:Math.random()*Math.PI*2,active:!0,static:!1}}return l<.001?this.createLoveLetter(o,s):l<.0015?e.some(a=>a.type==="neon-sign"&&a.active)?null:{type:"neon-sign",x:o/2,y:s*.3,size:40+Math.random()*20,opacity:0,targetOpacity:1,phase:"fade-in",phaseTime:0,fadeInDuration:1500,stableDuration:5e3,fadeOutDuration:1e3,crackleIntensity:1,flickerPhase:Math.random()*Math.PI*2,glowPhase:Math.random()*Math.PI*2,tubeFlickers:[],active:!0,static:!1}:l<.02?this.createSparkle(o,s,n):null},drawHeart(e,o,s){const n=o.x,l=o.y,a=o.size,i=1+Math.sin(s*o.pulseSpeed+o.pulsePhase)*.15;e.save(),e.globalAlpha=o.opacity,e.translate(n,l),e.rotate(o.rotation),e.scale(i,i),e.fillStyle=o.color,e.shadowColor=o.color,e.shadowBlur=a*.8,e.beginPath(),e.moveTo(0,a*.3),e.bezierCurveTo(-a,-a*.3,-a*.6,-a,0,-a*.4),e.bezierCurveTo(a*.6,-a,a,-a*.3,0,a*.3),e.closePath(),e.fill(),e.shadowBlur=0,e.fillStyle="rgba(255, 255, 255, 0.3)",e.beginPath(),e.arc(-a*.25,-a*.5,a*.15,0,Math.PI*2),e.fill(),e.restore()},drawRosePetal(e,o,s){const n=o.x,l=o.y,a=o.size,i=Math.sin(s*.01+o.flutterPhase)*.3;e.save(),e.globalAlpha=o.opacity,e.translate(n,l),e.rotate(o.rotation+i),e.fillStyle=o.color,e.strokeStyle="rgba(139, 0, 0, 0.3)",e.lineWidth=.5,e.beginPath(),e.moveTo(0,0),e.bezierCurveTo(a*2,-a,a*2,a,0,0),e.closePath(),e.fill(),e.beginPath(),e.moveTo(0,0),e.quadraticCurveTo(a,0,a*1.5,0),e.stroke(),e.restore()},drawSparkle(e,o,s){const n=o.x,l=o.y,a=o.size,i=o.life/o.maxLife,t=(Math.sin(s*.01+o.twinklePhase)+1)*.5;e.save(),e.globalAlpha=o.opacity*i*t,e.translate(n,l),e.fillStyle=o.color,e.shadowColor=o.color,e.shadowBlur=a*2,e.beginPath();for(let r=0;r<4;r++){const d=r/4*Math.PI*2-Math.PI/2,f=Math.cos(d)*a*3,h=Math.sin(d)*a*3,M=d+Math.PI/4,u=Math.cos(M)*a*.5,P=Math.sin(M)*a*.5;r===0?e.moveTo(f,h):(e.lineTo(u,P),e.lineTo(f,h))}e.closePath(),e.fill(),e.restore()},drawLips(e,o,s){const n=o.x,l=o.y,a=o.size,i=1+Math.sin(s*o.pulseSpeed+o.pulsePhase)*.1,t=Math.sin(s*.008+o.kissPhase);e.save(),e.globalAlpha=o.opacity,e.translate(n,l),e.rotate(o.rotation),e.scale(i,i);const r=e.createRadialGradient(0,0,0,0,0,a*2);r.addColorStop(0,o.color),r.addColorStop(.6,o.color),r.addColorStop(1,"#8b0000"),e.fillStyle=r,e.strokeStyle="#8b0000",e.lineWidth=a*.1,e.beginPath(),e.moveTo(-a,0),e.bezierCurveTo(-a*.8,-a*.8,-a*.3,-a*1,0,-a*.6),e.bezierCurveTo(a*.3,-a*1,a*.8,-a*.8,a,0),e.fill(),e.stroke();const d=t*a*.15;if(e.beginPath(),e.moveTo(-a,0),e.bezierCurveTo(-a*.7,a*(.7+d),-a*.3,a*(1+d),0,a*(.8+d)),e.bezierCurveTo(a*.3,a*(1+d),a*.7,a*(.7+d),a,0),e.fill(),e.stroke(),e.globalAlpha=o.opacity*.5,e.fillStyle="rgba(255, 255, 255, 0.6)",e.beginPath(),e.ellipse(-a*.4,-a*.4,a*.2,a*.15,-.3,0,Math.PI*2),e.fill(),e.beginPath(),e.ellipse(a*.4,-a*.4,a*.2,a*.15,.3,0,Math.PI*2),e.fill(),e.beginPath(),e.ellipse(0,a*.5,a*.3,a*.2,0,0,Math.PI*2),e.fill(),t>.8){const f=a*1.5,h=Math.sin(o.kissPhase)*f,M=-Math.abs(Math.cos(o.kissPhase))*f;e.globalAlpha=o.opacity*(t-.8)*5,e.fillStyle="#ffd700",e.shadowColor="#ffd700",e.shadowBlur=a*.5,e.beginPath(),e.arc(h,M,a*.15,0,Math.PI*2),e.fill();for(let u=0;u<4;u++){const P=u/4*Math.PI*2+s*.01,m=h+Math.cos(P)*a*.4,y=M+Math.sin(P)*a*.4;e.beginPath(),e.moveTo(h,M),e.lineTo(m,y),e.lineWidth=a*.05,e.strokeStyle="#ffffff",e.stroke()}}e.restore()},drawCupid(e,o,s){const n=o.x,l=o.y,a=o.size,i=o.vx>0?1:-1;e.save(),e.globalAlpha=o.opacity,e.translate(n,l),i===-1&&e.scale(-1,1);const t=Math.sin(s*.015+o.wingPhase)*(Math.PI/6);e.fillStyle="#ffffff",e.strokeStyle="#ffb6c1",e.lineWidth=1.5,e.save(),e.translate(-a*.2,-a*.3),e.rotate(-t),e.beginPath(),e.moveTo(0,0),e.bezierCurveTo(-a*1.2,-a*.8,-a*1.5,-a*.3,-a*1.2,a*.3),e.bezierCurveTo(-a*.8,a*.2,-a*.3,0,0,0),e.closePath(),e.fill(),e.stroke(),e.restore(),e.fillStyle="#ffdbac",e.beginPath(),e.ellipse(0,0,a*.4,a*.5,0,0,Math.PI*2),e.fill(),e.beginPath(),e.ellipse(a*.3,-a*.6,a*.35,a*.4,0,0,Math.PI*2),e.fill(),e.fillStyle="#ffd700",e.beginPath(),e.arc(a*.1,-a*.9,a*.22,0,Math.PI*2),e.fill(),e.beginPath(),e.arc(a*.4,-a*.95,a*.18,0,Math.PI*2),e.fill(),e.beginPath(),e.arc(a*.15,-a*.75,a*.15,0,Math.PI*2),e.fill(),e.fillStyle="#000000",e.beginPath(),e.arc(a*.42,-a*.62,a*.05,0,Math.PI*2),e.fill(),e.fillStyle="rgba(255, 105, 180, 0.3)",e.beginPath(),e.arc(a*.25,-a*.5,a*.12,0,Math.PI*2),e.fill(),e.strokeStyle="#ffdbac",e.lineWidth=a*.15,e.lineCap="round",e.beginPath(),e.moveTo(-a*.15,a*.4),e.lineTo(-a*.3,a*.8),e.stroke(),e.beginPath(),e.moveTo(-a*.05,a*.4),e.lineTo(-a*.15,a*.75),e.stroke(),e.strokeStyle="#ffdbac",e.lineWidth=a*.12,e.beginPath(),e.moveTo(a*.1,-a*.2),e.lineTo(a*.6,-a*.3),e.stroke(),e.beginPath(),e.moveTo(a*.05,0),e.lineTo(a*.9,-a*.35),e.stroke(),e.strokeStyle="#8B4513",e.lineWidth=2.5,e.beginPath(),e.moveTo(a*.9,-a*.6),e.quadraticCurveTo(a*1.1,-a*.35,a*.9,-a*.1),e.stroke(),e.strokeStyle="#daa520",e.lineWidth=1.5,e.beginPath(),e.moveTo(a*.9,-a*.6),e.lineTo(a*.6,-a*.35),e.lineTo(a*.9,-a*.1),e.stroke(),e.strokeStyle="#8B4513",e.lineWidth=2,e.beginPath(),e.moveTo(a*.6,-a*.35),e.lineTo(a*1.8,-a*.35),e.stroke(),e.fillStyle="#dc143c",e.beginPath(),e.moveTo(a*1.8,-a*.35),e.lineTo(a*2,-a*.35),e.lineTo(a*1.9,-a*.25),e.closePath(),e.fill(),e.beginPath(),e.moveTo(a*1.8,-a*.35),e.lineTo(a*2,-a*.35),e.lineTo(a*1.9,-a*.45),e.closePath(),e.fill(),e.fillStyle="#ff69b4",e.beginPath(),e.moveTo(a*.6,-a*.35),e.lineTo(a*.5,-a*.25),e.lineTo(a*.5,-a*.45),e.closePath(),e.fill(),e.fillStyle="#ffffff",e.strokeStyle="#ffb6c1",e.lineWidth=1.5,e.save(),e.translate(a*.1,-a*.3),e.rotate(t),e.beginPath(),e.moveTo(0,0),e.bezierCurveTo(-a*1,-a*.6,-a*1.3,-a*.2,-a*1,a*.2),e.bezierCurveTo(-a*.6,a*.15,-a*.2,0,0,0),e.closePath(),e.fill(),e.stroke(),e.restore(),e.restore()},drawLoveLetter(e,o,s){const n=o.x,l=o.y,a=o.size;e.save(),e.globalAlpha=o.opacity,e.translate(n,l),e.rotate(o.rotation),e.fillStyle="#fff5e6",e.strokeStyle="#daa520",e.lineWidth=1,e.fillRect(-a,-a*.7,a*2,a*1.4),e.strokeRect(-a,-a*.7,a*2,a*1.4),e.fillStyle="#ffe4b3",e.beginPath(),e.moveTo(-a,-a*.7),e.lineTo(0,0),e.lineTo(a,-a*.7),e.closePath(),e.fill(),e.stroke(),e.fillStyle="#dc143c",e.shadowColor="#dc143c",e.shadowBlur=5,e.beginPath(),e.moveTo(0,a*.1),e.bezierCurveTo(-a*.3,-a*.1,-a*.2,-a*.3,0,-a*.15),e.bezierCurveTo(a*.2,-a*.3,a*.3,-a*.1,0,a*.1),e.closePath(),e.fill(),e.shadowBlur=0,e.restore()},drawHeartGarland(e,o,s){const n=o.y,l=o.width,a=o.sag,i=o.hearts;e.save(),e.globalAlpha=o.opacity;const t=Math.sin(s*.001+o.swayPhase)*o.swayAmplitude;e.strokeStyle="#daa520",e.lineWidth=2,e.beginPath(),e.moveTo(0,n),e.quadraticCurveTo(l/2,n+a+t,l,n),e.stroke(),i.forEach(r=>{const d=r.offset*l,f=n+Math.sin(Math.PI*r.offset)*(a+t),h=1+Math.sin(s*.003+r.pulsePhase)*.1;e.save(),e.translate(d,f),e.scale(h,h),e.fillStyle=r.color,e.shadowColor=r.color,e.shadowBlur=r.size*.5,e.beginPath(),e.moveTo(0,r.size*.3),e.bezierCurveTo(-r.size,-r.size*.3,-r.size*.6,-r.size,0,-r.size*.4),e.bezierCurveTo(r.size*.6,-r.size,r.size,-r.size*.3,0,r.size*.3),e.closePath(),e.fill(),e.shadowBlur=0,e.restore()}),e.restore()},drawEnvelope(e,o,s){const n=o.x,l=o.y,a=o.size;e.save(),e.globalAlpha=o.opacity,e.translate(n,l),e.rotate(o.rotation),e.fillStyle="#fff5f5",e.strokeStyle="#ff69b4",e.lineWidth=a*.05,e.fillRect(-a*.7,-a*.5,a*1.4,a),e.strokeRect(-a*.7,-a*.5,a*1.4,a),e.fillStyle="#ffe4e1",e.beginPath(),e.moveTo(-a*.7,-a*.5),e.lineTo(0,a*.1),e.lineTo(a*.7,-a*.5),e.closePath(),e.fill(),e.stroke(),e.fillStyle="#ff1493",e.shadowColor="#ff1493",e.shadowBlur=a*.3,e.beginPath(),e.moveTo(0,a*.25),e.bezierCurveTo(-a*.3,-a*.1,-a*.2,-a*.3,0,-a*.15),e.bezierCurveTo(a*.2,-a*.3,a*.3,-a*.1,0,a*.25),e.closePath(),e.fill(),e.shadowBlur=0,e.restore()},drawButterfly(e,o,s=0){const n=s*(o.waveFrequency||0)+(o.waveOffset||0),l=o.waveAmplitude||0,a=o.color==="purple";T(e,{x:o.x+Math.sin(n)*l,y:(o.baseY??o.y)+Math.cos(s*(o.waveFrequency||0)*.7+(o.waveOffset||0))*l*.6,size:o.size*1.6,heading:-Math.PI/2,flapPhase:s*.01+(o.wingPhase||0),colourUpper:a?"#b48aba":"#d897b3",colourLower:a?"#896eaa":"#bb7798",alpha:o.opacity??1})},drawHeartMoon(e,o,s){const n=o.x,l=o.y,a=o.size,i=.3+Math.sin(s*.002+o.glowPhase)*.2;e.save(),e.globalAlpha=o.opacity,e.translate(n,l);const t=e.createRadialGradient(0,0,a*.5,0,0,a*2.5);t.addColorStop(0,`rgba(255, 182, 193, ${i*.8})`),t.addColorStop(.4,`rgba(255, 105, 180, ${i*.5})`),t.addColorStop(.7,`rgba(255, 20, 147, ${i*.3})`),t.addColorStop(1,"rgba(255, 20, 147, 0)"),e.fillStyle=t,e.beginPath(),e.arc(0,0,a*2.5,0,Math.PI*2),e.fill();const r=e.createRadialGradient(-a*.3,-a*.3,0,0,0,a);r.addColorStop(0,"#ffcce0"),r.addColorStop(.5,"#ffb3d9"),r.addColorStop(1,"#ff69b4"),e.fillStyle=r,e.beginPath(),e.moveTo(0,a*.3),e.bezierCurveTo(-a*.5,a*.5,-a,a*.2,-a,-a*.2),e.bezierCurveTo(-a,-a*.6,-a*.5,-a*.7,0,-a*.3),e.bezierCurveTo(a*.5,-a*.7,a,-a*.6,a,-a*.2),e.bezierCurveTo(a,a*.2,a*.5,a*.5,0,a*.3),e.closePath(),e.fill(),e.strokeStyle="#ff1493",e.lineWidth=a*.02,e.stroke(),e.fillStyle="rgba(255, 240, 245, 0.4)",e.beginPath(),e.arc(-a*.4,-a*.1,a*.15,0,Math.PI*2),e.fill(),e.beginPath(),e.arc(a*.3,0,a*.12,0,Math.PI*2),e.fill(),e.beginPath(),e.arc(-a*.2,a*.15,a*.1,0,Math.PI*2),e.fill(),e.fillStyle=`rgba(255, 255, 255, ${i})`;const d=[{x:-a*.3,y:-a*.4},{x:a*.2,y:-a*.3},{x:0,y:0},{x:-a*.5,y:a*.1},{x:a*.4,y:a*.15}];for(const f of d){e.save(),e.translate(f.x,f.y),e.beginPath();const h=a*.08;e.moveTo(0,-h),e.lineTo(h*.2,-h*.2),e.lineTo(h,0),e.lineTo(h*.2,h*.2),e.lineTo(0,h),e.lineTo(-h*.2,h*.2),e.lineTo(-h,0),e.lineTo(-h*.2,-h*.2),e.closePath(),e.fill(),e.restore()}e.restore()},drawNeonSign(e,o,s){const n=o.x,l=o.y,a=o.size;if(o.phaseTime+=16,o.phase==="fade-in"&&o.phaseTime>=o.fadeInDuration)o.phase="stable",o.phaseTime=0,o.opacity=o.targetOpacity,o.crackleIntensity=0;else if(o.phase==="stable"&&o.phaseTime>=o.stableDuration)o.phase="fade-out",o.phaseTime=0;else if(o.phase==="fade-out"&&o.phaseTime>=o.fadeOutDuration){o.active=!1;return}o.phase==="fade-in"?(o.opacity=o.phaseTime/o.fadeInDuration*o.targetOpacity,o.crackleIntensity=1-o.phaseTime/o.fadeInDuration):o.phase==="fade-out"&&(o.opacity=o.targetOpacity*(1-o.phaseTime/o.fadeOutDuration)),e.save(),e.translate(n,l);const i="#ff1493",t="#ff0066",r="#ffffff",d="Happy Valentine's Day",f=a;e.font=`bold ${f}px 'Arial Black', sans-serif`,e.textAlign="center",e.textBaseline="middle";const h=e.measureText(d).width;let M=0;o.crackleIntensity>0&&(M=(Math.random()-.5)*o.crackleIntensity*5);const u=d.split("");let P=-h/2;for(let m=0;m<u.length;m++){const y=u[m],b=e.measureText(y).width,v=(o.crackleIntensity>0?Math.random()<o.crackleIntensity*.3:!1)?o.opacity*(.3+Math.random()*.7):o.opacity;e.save(),e.translate(P+b/2,M),e.globalAlpha=v;const p=.8+Math.sin(s*.002+o.glowPhase+m*.5)*.2;e.shadowColor=i,e.shadowBlur=a*.8*p,e.fillStyle=i,e.fillText(y,0,0),e.shadowBlur=a*.5*p,e.fillStyle=t,e.fillText(y,0,0),e.shadowBlur=a*.3*p,e.fillStyle=r,e.fillText(y,0,0),e.shadowBlur=0,e.fillStyle=r,e.globalAlpha=v*.9,e.fillText(y,0,0),e.restore(),P+=b}if(o.crackleIntensity>.3){for(let m=0;m<3;m++)if(Math.random()<o.crackleIntensity*.5){const y=(Math.random()-.5)*h,b=(Math.random()-.5)*a*.3,g=2+Math.random()*3;e.save(),e.globalAlpha=o.opacity*Math.random(),e.fillStyle=Math.random()<.5?i:r,e.shadowColor=i,e.shadowBlur=g*4,e.beginPath(),e.arc(y,b,g,0,Math.PI*2),e.fill(),e.restore()}}(o.phase==="stable"||o.phase==="fade-out")&&(e.globalAlpha=o.opacity*.1,e.fillStyle="#ff1493",e.fillRect(-h*.55,-a*.6,h*1.1,a*1.2)),e.restore()}};