From e9967fa22781d922ba4eb8fb44fe72d200ac4b14 Mon Sep 17 00:00:00 2001
From: Jake Vanderwerf <get@jakevanderwerf.ca>
Date: Mon, 10 Nov 2025 21:04:10 +0000
Subject: [PATCH] =IconsManager.php update

---
 base/_setup.php                             |    1 
 assets/css/icons.css                        |    1 
 inc/meta/MetaRenderer.php                   |    8 
 src/summary/render.php                      |    8 
 assets/js/dash/TaxonomyCreator.js           |   56 
 base/email.php                              |   97 
 assets/css/dash.min.css                     |    2 
 inc/helpers/legacy.php                      |    4 
 jvb.php                                     |  105 
 inc/helpers/formatting.php                  |   14 
 inc/managers/OperationQueue.php             |    5 
 src/feed/render.php                         |    8 
 assets/js/min/form.min.js                   |    2 
 iconsOld.php                                |  761 +++++++
 inc/managers/DashboardManager.php           |   63 
 inc/rest/routes/ContentRoutes.php           |  255 +
 assets/js/min/referral.min.js               |    2 
 inc/forms/PostSelectorOld.php               |    2 
 inc/meta/MetaFormOld.php                    |   44 
 inc/rest/routes/ApprovalRoutes.php          |    7 
 inc/managers/_setup.php                     |    7 
 inc/helpers/crud.php                        |    2 
 JVBase.php                                  |    9 
 build/summary/render.php                    |   19 
 inc/managers/LoginManager.php               |  802 ++-----
 inc/integrations/Square.php                 |    6 
 inc/meta/MetaManager.php                    |    4 
 assets/js/concise/Queue.js                  |    6 
 inc/managers/EmailManager.php               |  324 +-
 inc/integrations/Integrations.php           |    2 
 assets/js/dash/FavouritesManager.js         |    6 
 assets/js/concise/quill.js                  |   28 
 inc/helpers/breadcrumbs.php                 |    2 
 assets/js/concise/UploadManager.js          |  107 
 assets/js/min/news.min.js                   |    2 
 assets/css/forms.min.css                    |    2 
 assets/js/concise/View.js                   |   22 
 inc/rest/routes/ReferralRoutes.php          |   65 
 templates/dashboard/sections/favourites.php |   18 
 inc/managers/MagicLinkManager.php           |   14 
 src/glossary/view.js                        |   17 
 inc/rest/_setup.php                         |    9 
 inc/meta/MetaForm.php                       |   37 
 assets/js/min/populate.min.js               |    2 
 inc/integrations/GoogleMyBusiness.php       |   17 
 assets/js/concise/TaxonomySelector.js       |   26 
 assets/js/Gallery.js                        |  350 +-
 assets/js/min/view.min.js                   |    2 
 inc/rest/routes/Invitations.php             |   57 
 inc/integrations/PostMark.php               |   52 
 assets/js/min/favouritesManager.min.js      |    2 
 inc/rest/routes/MagicLinkRoutes.php         |   88 
 inc/rest/routes/LoginRoutes.php             |  667 ++++++
 build/glossary/view.js                      |    2 
 inc/integrations/Helcim.php                 |    4 
 inc/helpers/terms.php                       |    4 
 inc/users/UserSettings.php                  |   12 
 inc/forms/TaxonomySelectorOld.php           |    6 
 assets/js/dash/NewsManager.js               |    2 
 assets/js/dash/UtilityFunctions.js          |    7 
 assets/js/concise/Media.js                  |  235 -
 inc/helpers/ui.php                          |   28 
 inc/forms/TaxonomySelector.php              |    8 
 inc/meta/_setup.php                         |    3 
 inc/managers/CRUDManager.php                |  128 
 assets/js/min/queue.min.js                  |    2 
 inc/blocks/CustomBlocks.php                 |  109 
 assets/js/min/utility.min.js                |    2 
 assets/js/min/media.min.js                  |    1 
 inc/managers/TaxonomyRelationships.php      |   13 
 inc/managers/NotificationManager.php        |    4 
 assets/js/min/gallery.min.js                |    2 
 inc/integrations/Facebook.php               |    2 
 inc/managers/IconsManager.php               |  434 ++++
 inc/helpers/all.php                         |    2 
 assets/js/concise/Referral.js               |    4 
 assets/js/min/selector.min.js               |    2 
 assets/js/concise/FormController.js         |  266 ++
 inc/managers/DirectoryManager.php           |    2 
 inc/registry/CheckCustomTables.php          |    6 
 inc/blocks/FeedBlock.php                    |   12 
 cleanup.php                                 |    4 
 build/feed/render.php                       |    8 
 inc/managers/UserTermsManager.php           |   33 
 assets/js/min/crud.min.js                   |    2 
 assets/js/concise/PopulateForm.js           |   75 
 assets/js/dash/CRUD.js                      |   10 
 inc/helpers/forms.php                       |    2 
 inc/helpers/renderFields.php                |   38 
 build/glossary/view.asset.php               |    2 
 inc/rest/routes/FavouritesRoutes.php        |    8 
 inc/helpers/media.php                       |    6 
 assets/js/min/quill.min.js                  |    2 
 inc/managers/ReferralManager.php            |  221 +
 inc/helpers/members.php                     |    8 
 assets/css/icons.min.css                    |    1 
 inc/managers/AdminPages.php                 |    2 
 assets/css/nav.min.css                      |    2 
 inc/managers/LoginManagerOld.php            |    6 
 assets/js/min/uploader.min.js               |    2 
 assets/js/concise/JVBase.js                 |    1 
 assets/js/min/creator.min.js                |    2 
 templates/dashboard/sections/news.php       |   18 
 /dev/null                                   |   54 
 inc/managers/RegisterManager.php            |    4 
 inc/rest/RestRouteManager.php               |  420 +++
 inc/templates.php                           |    6 
 107 files changed, 4,593 insertions(+), 1,864 deletions(-)

diff --git a/JVBase.php b/JVBase.php
index 8fd1666..8894d0c 100644
--- a/JVBase.php
+++ b/JVBase.php
@@ -25,6 +25,7 @@
 use JVBase\rest\routes\QueueRoutes;
 use JVBase\rest\routes\ErrorRoutes;
 use JVBase\rest\routes\FormRoutes;
+use JVBase\rest\routes\LoginRoutes;
 use JVBase\rest\routes\NewsRoutes;
 use JVBase\rest\routes\ReferralRoutes;
 use JVBase\rest\routes\MagicLinkRoutes;
@@ -61,6 +62,7 @@
 		'gmb' => 'JVBase\integrations\GoogleMyBusiness',
 		'cloudflare' => 'JVBase\integrations\Cloudflare',
 		'umami' => 'JVBase\integrations\Umami',
+		'postmark' => 'JVBase\integrations\PostMark',
 	];
 
     public static function getInstance():JVB
@@ -104,6 +106,7 @@
 		}
 
         $this->routes = [
+			'login'			=> new LoginRoutes(),
 			'integrations'	=> new IntegrationsRoutes(),
 		];
         if (jvbSiteUsesFeedBlock()) {
@@ -117,10 +120,10 @@
             $this->routes['term'] = new TermRoutes();
         }
 
+		$this->routes['queue']  = new QueueRoutes();
+		$this->routes['settings']= new SettingsRoutes();
+		$this->routes['upload'] = new UploadRoutes();
         if (jvbSiteHasDashboard()) {
-            $this->routes['upload'] = new UploadRoutes();
-            $this->routes['settings']= new SettingsRoutes();
-            $this->routes['queue']  = new QueueRoutes();
             $this->routes['error']  = new ErrorRoutes();
             $this->routes['admin']  = new AdminRoutes();
             $this->routes['content']= new ContentRoutes();
diff --git a/assets/css/dash.min.css b/assets/css/dash.min.css
index e0f5864..751536b 100644
--- a/assets/css/dash.min.css
+++ b/assets/css/dash.min.css
@@ -1 +1 @@
-:target{outline:0!important;padding:0!important}.dashboard h1:first-of-type{margin-top:4rem!important}main>footer{max-width:100%!important;position:fixed;z-index:var(--z-top);bottom:0;left:0;right:0;width:100%;margin:4rem 0 0 0!important;height:var(--height);padding:0!important;background-color:var(--base);box-shadow:var(--shadow)}main>*{max-width:min(768px,90vw)!important;margin:0 auto!important}main h1{margin:0!important;font-size:var(--large)}.item-grid .item{position:relative}img{width:100%;height:auto;aspect-ratio:1;object-fit:cover}.replace{margin-bottom:var(--offHeight)!important}.item-grid{margin-bottom:4rem}.item-grid:has(.select-item:checked) .item{padding:.75rem;opacity:.8;filter:var(--filter)}.item-grid .item:has(.select-item:checked){padding:.5rem;filter:none;opacity:1;background-color:var(--action-0)}.grid-view .item>input[type=checkbox]:not(.label-button)+label{padding-left:0;margin:0}.grid-view .item>input[type=checkbox]+label::before{transform:unset;top:.5rem;left:.5rem}.grid-view .item>input[type=checkbox]+label::after{top:.5rem;left:.75rem;transform:translateY(20%) rotate(45deg)}.grid-view .item .item-actions{position:absolute;bottom:0;right:0}.list-view h3,.list-view p{margin:0!important}@media (min-width:768px){.grid-view{grid-template-columns:repeat(auto-fill,minmax(200px,1fr))}.grid-view .item .item-actions{bottom:unset;top:0}}.bulk-controls{margin:1rem 0}.bulk-controls .selected-count{font-weight:400;font-size:var(--small);text-transform:none;font-style:italic;display:flex;gap:.25rem;margin-left:2rem}.selected-count::before{content:'{'}.selected-count::after{content:'}'}.bulk-edit-form .selected{display:grid;grid-template-columns:repeat(auto-fill,minmax(100px,1fr));gap:4px}.selected label{padding:.5rem;opacity:.6;filter:var(--filter);border:2px solid transparent;transition:filter var(--transition-base),opacity var(--transition-base),border var(--transition-base),padding var(--transition-base)}.selected label:has(:checked){border-color:var(--action-0);padding:0;opacity:1;filter:none;transition:filter var(--transition-base),opacity var(--transition-base),border var(--transition-base),padding var(--transition-base)}form.table img{max-height:4rem}.all-filters{margin:2rem 0;padding:1rem 0;border-top:1px solid var(--base-200);border-bottom:1px solid var(--base-200)}details.uploader+.items-list .all-filters{border-top:none}.all-filters .filters{width:100%}.controls .radio-options,.filters.row.start{--align:center;--justify:flex-start;--gap:.5rem}.all-filters span.label{text-transform:uppercase;font-size:var(--small);font-weight:900;width:15vw;display:inline-flex;align-items:center;padding-right:2rem}.controls .icon{--w:1.4rem}.all-filters .btn+label,.all-filters button{height:fit-content;padding:.5rem!important;min-width:0;min-height:0}.all-filters .btn+label:focus,.all-filters .btn+label:hover,.all-filters button:focus,.all-filters button:hover{background-color:transparent;color:var(--action-0);border-color:var(--action-0)}.search-container:not(.open) .clear-search,.search-container:not(.open) input[type=search]{transform:scaleX(0);transform-origin:left;width:0;padding:0;transition:transform var(--transition-base),width var(--transition-base),padding var(--transition-base)}.search-container button{padding:.5rem}.search-container .icon{--w:1.5rem}.search-container.open .clear-search,.search-container.open input[type=search]{transform:scaleX(1);transform-origin:left;transition:transform var(--transition-base),width var(--transition-base),padding var(--transition-base)}.all-filters>.search,.search-container,input[type=search]{width:100%}form.table textarea{width:250px;padding:.5rem}.multi-select summary{--gap:2rem;padding-right:2.5rem}dialog.bulk-edit[open],dialog.create[open],dialog.edit[open]{height:85vh;top:5vh}.tab-content h2{display:none}.group-fields.hours .group-fields,.group-fields.hours .group-fields .field{display:flex;justify-content:space-between;align-items:center}.group-fields.hours .group-fields{padding:1rem .5rem;gap:1rem}.group-fields.hours .group-fields:nth-of-type(2n+1){background-color:var(--base)}.group-fields.hours .group-fields .field{margin:0}.group-fields.hours .true-false{flex:1}.group-fields.hours .time{position:relative}.group-fields.hours .time label{margin:0;font-size:var(--small);position:absolute;top:-1rem;left:0;color:var(--contrast-200)}.today_hours{width:min(500px,90vw)}.today_hours .group-fields{width:100%;padding:0;display:flex;justify-content:center;gap:.5rem}@media (min-width:768px){.today_hours .group-fields{padding:2rem}}.today_hours .field{margin:0}.dash .true-false{margin:0}.dash [type=submit]{width:90%}.dashboard.dash h2{text-transform:none;font-size:var(--large)}.dashboard.dash .replace>ul{display:flex;list-style:none;align-items:flex-start;justify-content:flex-start;flex-wrap:wrap;gap:.5rem}.dashboard.settings nav.tabs{--height:3.5rem;--x:var(--offHeight);position:fixed;bottom:var(--height);left:var(--x);right:var(--x);z-index:99;width:calc(100% - var(--x) - var(--x));background-color:var(--base)}nav.integrations,nav.integrations a,nav.integrations li,nav.integrations ul{height:auto}.replace{overflow:hidden}body.dash form#options{display:flex;flex-flow:column nowrap;justify-content:center;align-items:center}.item-grid.integrations{grid-template-columns:repeat(2,1fr);gap:2rem}.integration{background:var(--base);border:2px solid var(--base-200);border-radius:var(--outerRadius);padding:1rem;position:relative;transition:all var(--transition-base);box-shadow:var(--shadow)}.integration.connected{border-color:var(--success)}.integration.disconnected,.integration.error{border-color:var(--error)}.integration.hasChanges{border-color:var(--warning)}.integration .header{margin-bottom:.75rem;padding-bottom:.75rem;border-bottom:2px solid var(--base-200)}.integration h3{letter-spacing:1px;font-size:var(--medium);margin:0}.integration .meta{margin-bottom:1rem;text-align:right;color:var(--contrast-200);font-size:var(--small)}.integration .setup{font-size:var(--small);font-weight:700;text-transform:uppercase}.integration .setup .indicator{font-size:var(--medium)}.integration .connected .indicator,.integration .setup .connected{color:var(--success)}.integration .disconnected .indicator,.integration .setup .disconnected{color:var(--error)}.integration.hasChanges .disconnected{color:var(--warning)}.connection-status.connected{background-color:var(--successBack);color:var(--successText)}.connection-status.disconnected{background-color:var(--errorBack);color:var(--errorText)}.integration code{display:inline-block;width:90%;margin:0 .5rem;user-select:all;padding:.75rem;border:2px solid var(--base);background-color:var(--base-200);word-break:break-all}.integration details+details{margin-top:1rem}.integration .actions{margin-top:1rem}.hint{line-height:1.2;font-style:italic;font-size:var(--small)}.hasChanges button[data-action=save_credentials]{border-color:var(--warning);animation:pulse-color 1s infinite;animation-delay:1s}.flash{animation:flash .5s}.flash.connected{--b:var(--success)}.flash.disconnected{--b:var(--error)}.flash.syncing{--b:var(--success)}.flash.error,.flash.hasChanges{--b:var(--warning)}@keyframes flash{0%,100%{border-color:inherit}50%{border-color:var(--b)}}.location.field{width:80vw}.location.field>p{text-align:center}.location.field>p+p{margin:0 .5rem 0 0}.location.field .location-map{height:20vh}.location.field .location-links{padding:.5rem 0;display:flex;justify-content:space-evenly}.field.upload [data-upload-id],.item-grid .item{touch-action:none}.empty-state{grid-column:1/-1;padding:1rem 10vw;margin:0 10vw;border-radius:var(--outerRadius);background-color:var(--base-100)}.jvb-oauth-connect{position:relative;transition:opacity .2s}.jvb-oauth-connect.loading{opacity:.6;pointer-events:none}.jvb-oauth-connect.loading::after{content:'';position:absolute;right:-30px;top:50%;transform:translateY(-50%);width:16px;height:16px;border:2px solid #ccc;border-top-color:#0073aa;border-radius:50%;animation:oauth-spin .8s linear infinite}@keyframes oauth-spin{to{transform:translateY(-50%) rotate(360deg)}}.integration-status-message{padding:12px 16px;margin:16px 0;border-radius:4px;display:none;font-size:14px;line-height:1.5}.integration-status-message.success{display:block;background:#d4edda;color:#155724;border-left:4px solid #28a745}.integration-status-message.error{display:block;background:#f8d7da;color:#721c24;border-left:4px solid #dc3545}.integration-status-message.info{display:block;background:#d1ecf1;color:#0c5460;border-left:4px solid #17a2b8}.connection-status{display:inline-flex;align-items:center;gap:8px;padding:6px 12px;border-radius:4px;font-size:13px;font-weight:500}.connection-status.connected{background:#d4edda;color:#155724}.connection-status.disconnected{background:#f8d7da;color:#721c24}.status-indicator{font-size:10px;line-height:1}.connection-status.connected .status-indicator{color:#28a745}.connection-status.disconnected .status-indicator{color:#dc3545}
\ No newline at end of file
+:target{outline:0!important;padding:0!important}.dashboard>header{justify-content:flex-end}.dashboard>header img{width:var(--height)}.dashboard h1:first-of-type{margin-top:4rem!important}main>footer{max-width:100%!important;position:fixed;z-index:var(--z-top);bottom:0;left:0;right:0;width:100%;margin:4rem 0 0 0!important;height:var(--height);padding:0!important;background-color:var(--base);box-shadow:var(--shadow)}main>*{max-width:min(768px,90vw)!important;margin:0 auto!important}main h1{margin:0!important;font-size:var(--large)}.item-grid .item{position:relative}img{width:100%;height:auto;aspect-ratio:1;object-fit:cover}.replace{margin-bottom:var(--offHeight)!important}.item-grid{margin-bottom:4rem}.item-grid:has(.select-item:checked) .item{padding:.75rem;opacity:.8;filter:var(--filter)}.item-grid .item:has(.select-item:checked){padding:.5rem;filter:none;opacity:1;background-color:var(--action-0)}.grid-view .item>input[type=checkbox]:not(.label-button)+label{padding-left:0;margin:0}.grid-view .item>input[type=checkbox]+label::before{transform:unset;top:.5rem;left:.5rem}.grid-view .item>input[type=checkbox]+label::after{top:.5rem;left:.75rem;transform:translateY(20%) rotate(45deg)}.grid-view .item .item-actions{position:absolute;bottom:0;right:0}.list-view h3,.list-view p{margin:0!important}@media (min-width:768px){.grid-view{grid-template-columns:repeat(auto-fill,minmax(200px,1fr))}.grid-view .item .item-actions{bottom:unset;top:0}}.bulk-controls{margin:1rem 0}.bulk-controls .selected-count{font-weight:400;font-size:var(--small);text-transform:none;font-style:italic;display:flex;gap:.25rem;margin-left:2rem}.selected-count::before{content:'{'}.selected-count::after{content:'}'}.bulk-edit-form .selected{display:grid;grid-template-columns:repeat(auto-fill,minmax(100px,1fr));gap:4px}.selected label{padding:.5rem;opacity:.6;filter:var(--filter);border:2px solid transparent;transition:filter var(--transition-base),opacity var(--transition-base),border var(--transition-base),padding var(--transition-base)}.selected label:has(:checked){border-color:var(--action-0);padding:0;opacity:1;filter:none;transition:filter var(--transition-base),opacity var(--transition-base),border var(--transition-base),padding var(--transition-base)}form.table img{max-height:4rem}.all-filters{margin:2rem 0;padding:1rem 0;border-top:1px solid var(--base-200);border-bottom:1px solid var(--base-200)}details.uploader+.items-list .all-filters{border-top:none}.all-filters .filters{width:100%}.controls .radio-options,.filters.row.start{--align:center;--justify:flex-start;--gap:.5rem}.all-filters span.label{text-transform:uppercase;font-size:var(--small);font-weight:900;width:15vw;display:inline-flex;align-items:center;padding-right:2rem}.controls .icon{--w:1.4rem}.all-filters .btn+label,.all-filters button{height:fit-content;padding:.5rem!important;min-width:0;min-height:0}.all-filters .btn+label:focus,.all-filters .btn+label:hover,.all-filters button:focus,.all-filters button:hover{background-color:transparent;color:var(--action-0);border-color:var(--action-0)}.search-container:not(.open) .clear-search,.search-container:not(.open) input[type=search]{transform:scaleX(0);transform-origin:left;width:0;padding:0;transition:transform var(--transition-base),width var(--transition-base),padding var(--transition-base)}.search-container button{padding:.5rem}.search-container .icon{--w:1.5rem}.search-container.open .clear-search,.search-container.open input[type=search]{transform:scaleX(1);transform-origin:left;transition:transform var(--transition-base),width var(--transition-base),padding var(--transition-base)}.all-filters>.search,.search-container,input[type=search]{width:100%}form.table textarea{width:250px;padding:.5rem}.multi-select summary{--gap:2rem;padding-right:2.5rem}dialog.bulk-edit[open],dialog.create[open],dialog.edit[open]{height:85vh;top:5vh}.tab-content h2{display:none}.group-fields.hours .group-fields,.group-fields.hours .group-fields .field{display:flex;justify-content:space-between;align-items:center}.group-fields.hours .group-fields{padding:1rem .5rem;gap:1rem}.group-fields.hours .group-fields:nth-of-type(2n+1){background-color:var(--base)}.group-fields.hours .group-fields .field{margin:0}.group-fields.hours .true-false{flex:1}.group-fields.hours .time{position:relative}.group-fields.hours .time label{margin:0;font-size:var(--small);position:absolute;top:-1rem;left:0;color:var(--contrast-200)}.today_hours{width:min(500px,90vw)}.today_hours .group-fields{width:100%;padding:0;display:flex;justify-content:center;gap:.5rem}@media (min-width:768px){.today_hours .group-fields{padding:2rem}}.today_hours .field{margin:0}.dash .true-false{margin:0}.dash [type=submit]{width:90%}.dashboard.dash h2{text-transform:none;font-size:var(--large)}.dashboard.dash .replace>ul{display:flex;list-style:none;align-items:flex-start;justify-content:flex-start;flex-wrap:wrap;gap:.5rem}.dashboard.settings nav.tabs{--height:3.5rem;--x:var(--offHeight);position:fixed;bottom:var(--height);left:var(--x);right:var(--x);z-index:99;width:calc(100% - var(--x) - var(--x));background-color:var(--base)}nav.integrations,nav.integrations a,nav.integrations li,nav.integrations ul{height:auto}.replace{overflow:hidden}body.dash form#options{display:flex;flex-flow:column nowrap;justify-content:center;align-items:center}.item-grid.integrations{grid-template-columns:repeat(2,1fr);gap:2rem}.integration{background:var(--base);border:2px solid var(--base-200);border-radius:var(--outerRadius);padding:1rem;position:relative;transition:all var(--transition-base);box-shadow:var(--shadow)}.integration.connected{border-color:var(--success)}.integration.disconnected,.integration.error{border-color:var(--error)}.integration.hasChanges{border-color:var(--warning)}.integration .header{margin-bottom:.75rem;padding-bottom:.75rem;border-bottom:2px solid var(--base-200)}.integration h3{letter-spacing:1px;font-size:var(--medium);margin:0}.integration .meta{margin-bottom:1rem;text-align:right;color:var(--contrast-200);font-size:var(--small)}.integration .setup{font-size:var(--small);font-weight:700;text-transform:uppercase}.integration .setup .indicator{font-size:var(--medium)}.integration .connected .indicator,.integration .setup .connected{color:var(--success)}.integration .disconnected .indicator,.integration .setup .disconnected{color:var(--error)}.integration.hasChanges .disconnected{color:var(--warning)}.connection-status.connected{background-color:var(--successBack);color:var(--successText)}.connection-status.disconnected{background-color:var(--errorBack);color:var(--errorText)}.integration code{display:inline-block;width:90%;margin:0 .5rem;user-select:all;padding:.75rem;border:2px solid var(--base);background-color:var(--base-200);word-break:break-all}.integration details+details{margin-top:1rem}.integration .actions{margin-top:1rem}.hint{line-height:1.2;font-style:italic;font-size:var(--small)}.hasChanges button[data-action=save_credentials]{border-color:var(--warning);animation:pulse-color 1s infinite;animation-delay:1s}.flash{animation:flash .5s}.flash.connected{--b:var(--success)}.flash.disconnected{--b:var(--error)}.flash.syncing{--b:var(--success)}.flash.error,.flash.hasChanges{--b:var(--warning)}@keyframes flash{0%,100%{border-color:inherit}50%{border-color:var(--b)}}.location.field{width:80vw}.location.field>p{text-align:center}.location.field>p+p{margin:0 .5rem 0 0}.location.field .location-map{height:20vh}.location.field .location-links{padding:.5rem 0;display:flex;justify-content:space-evenly}.field.upload [data-upload-id],.item-grid .item{touch-action:none}.empty-state{grid-column:1/-1;padding:1rem 10vw;margin:0 10vw;border-radius:var(--outerRadius);background-color:var(--base-100)}.jvb-oauth-connect{position:relative;transition:opacity .2s}.jvb-oauth-connect.loading{opacity:.6;pointer-events:none}.jvb-oauth-connect.loading::after{content:'';position:absolute;right:-30px;top:50%;transform:translateY(-50%);width:16px;height:16px;border:2px solid #ccc;border-top-color:#0073aa;border-radius:50%;animation:oauth-spin .8s linear infinite}@keyframes oauth-spin{to{transform:translateY(-50%) rotate(360deg)}}.integration-status-message{padding:12px 16px;margin:16px 0;border-radius:4px;display:none;font-size:14px;line-height:1.5}.integration-status-message.success{display:block;background:#d4edda;color:#155724;border-left:4px solid #28a745}.integration-status-message.error{display:block;background:#f8d7da;color:#721c24;border-left:4px solid #dc3545}.integration-status-message.info{display:block;background:#d1ecf1;color:#0c5460;border-left:4px solid #17a2b8}.connection-status{display:inline-flex;align-items:center;gap:8px;padding:6px 12px;border-radius:4px;font-size:13px;font-weight:500}.connection-status.connected{background:#d4edda;color:#155724}.connection-status.disconnected{background:#f8d7da;color:#721c24}.status-indicator{font-size:10px;line-height:1}.connection-status.connected .status-indicator{color:#28a745}.connection-status.disconnected .status-indicator{color:#dc3545}
\ No newline at end of file
diff --git a/assets/css/forms.min.css b/assets/css/forms.min.css
index 1fae8da..4bd1dd5 100644
--- a/assets/css/forms.min.css
+++ b/assets/css/forms.min.css
@@ -1 +1 @@
-details.uploader .file-upload-container{margin:1rem 0;max-width:100%}@media (min-width:768px){details.uploader .file-upload-container{margin:1rem var(--mr) 1rem var(--ml);max-width:var(--maxWidth)}}.file-upload-wrapper{border:2px dashed var(--action-0);border-radius:4px;padding:2rem;text-align:center;transition:all .3s ease;background:rgba(var(--action-rgb),var(--rgb-subtle));position:relative;cursor:pointer}.file-upload-wrapper h2{margin:0!important;font-size:var(--large)}.dragover,.file-upload-wrapper:hover{background:rgba(var(--action-rgb),var(--rgb-subtle-hover));border-color:var(--action-0)!important}.file-upload-wrapper input[type=file]{position:absolute;left:0;top:0;width:100%;height:100%;opacity:0;cursor:pointer}.file-upload-text{color:var(--contrast);margin:0;font-family:var(--body)}.file-upload-text strong{color:var(--action-0);text-decoration:underline}.field.upload:has(.upload-item) .file-upload-container{display:none}.field.upload{position:relative}.field.upload:not(.uploading) .progress{display:none}.field.upload .actions{position:absolute;top:0;right:0}.item-grid.group,.item-grid.preview,.item-grid.restore{grid-template-columns:repeat(3,1fr)}.item-grid.group .item,.item-grid.preview .item,.item-grid.restore .item{display:block}.item-grid.group button,.item-grid.preview button,.item-grid.restore button{padding:.25rem .5rem}.item-grid.group button .icon,.item-grid.preview button .icon,.item-grid.restore button .icon{--w:1.1em}.item-grid.group .item .preview>input[type=checkbox]:not(.label-button)+label,.item-grid.preview .item .preview>input[type=checkbox]:not(.label-button)+label,.item-grid.restore .item .preview>input[type=checkbox]:not(.label-button)+label{padding-left:0;margin:0}.item-grid.group .item .preview>input[type=checkbox]+label:before,.item-grid.preview .item .preview>input[type=checkbox]+label:before,.item-grid.restore .item .preview>input[type=checkbox]+label:before{transform:unset;top:.5rem;left:.5rem}.item-grid.group .item .preview>input[type=checkbox]+label::after,.item-grid.preview .item .preview>input[type=checkbox]+label::after,.item-grid.restore .item .preview>input[type=checkbox]+label::after{top:.5rem;left:.75rem;transform:translateY(20%) rotate(45deg)}.item-grid.group .item .item-actions,.item-grid.preview .item .item-actions,.item-grid.restore .item .item-actions{position:absolute;top:0;right:0}.item-grid.group summary,.item-grid.preview summary,.item-grid.restore summary{padding:.5rem}.item-grid.group:has([type=checkbox]:checked),.item-grid.preview:has([type=checkbox]:checked),.item-grid.restore:has([type=checkbox]:checked){padding:1rem;background-color:rgba(var(--contrast-rgb),var(--rgb-subtle))}.item-grid.group:has([type=checkbox]:checked) .item,.item-grid.preview:has([type=checkbox]:checked) .item,.item-grid.restore:has([type=checkbox]:checked) .item{padding:.75rem;opacity:.8}.item-grid.group:has([type=checkbox]:checked) .item img,.item-grid.preview:has([type=checkbox]:checked) .item img,.item-grid.restore:has([type=checkbox]:checked) .item img{filter:var(--filter)}.item-grid.group:has([type=checkbox]:checked) details,.item-grid.preview:has([type=checkbox]:checked) details,.item-grid.restore:has([type=checkbox]:checked) details{display:none}.item-grid.group .item:has([type=checkbox]:checked),.item-grid.preview .item:has([type=checkbox]:checked),.item-grid.restore .item:has([type=checkbox]:checked){padding:.5rem;background-color:rgba(var(--action-rgb),var(--rgb-medium));opacity:1}.item-grid.group .item:has([type=checkbox]:checked) img,.item-grid.preview .item:has([type=checkbox]:checked) img,.item-grid.restore .item:has([type=checkbox]:checked) img{filter:none}[type=radio].featured+label .star+.star,[type=radio].featured:checked+label .star{display:none}[type=radio].featured+label .star,[type=radio].featured:checked+label .star+.star{display:inline-block}.restore.item,.upload.item{border-radius:var(--innerRadius);overflow:hidden;background:var(--base);border:1px solid var(--base-200)}.restore.item img,.upload.item img{transition:transform var(--transition-base)}.restore.item:hover img,.upload.item:hover img{transform:scale(1.02);transition:transform var(--transition-base)}.upload-group{background-image:var(--dashed-action);padding:5px;border-radius:var(--innerRadius);background-color:rgba(var(--action-rgb),var(--rgb-subtle))}.submit-uploads{position:fixed;bottom:var(--offHeight);right:var(--offHeight);z-index:var(--z-6);height:var(--height);box-shadow:var(--shadow);border-radius:var(--innerRadius);animation:pulse-color 5s infinite;animation-delay:1s;background-color:var(--action-0);color:var(--action-contrast)}.submit-uploads:hover{background-color:var(--base-200);color:var(--contrast-200)}.empty-group{grid-column:1/-1;padding:20px;background-image:var(--dashed-action);border-radius:var(--innerRadius);margin:10px 0;cursor:pointer;transition:all var(--transition-base);text-align:center;background-color:rgba(var(--action-rgb),var(--rgb-subtle))}.group-display:not([hidden])~.file-upload-container{display:none}.dragging,.upload.item.dragging{opacity:.7;transform:scale(.95) rotate(3deg);z-index:var(--z-top);box-shadow:0 8px 25px rgba(0,0,0,.3)}.dragover{background:rgba(var(--action-rgb),var(--rgb-light))!important;border-color:var(--action-0)!important;transform:scale(1.05);animation:drop-pulse .8s infinite ease-in-out}.drag-preview{position:fixed;z-index:var(--zz-top);width:fit-content;overflow:visible;pointer-events:none;opacity:.9;transform:scale(1.05);transition:transform .2s ease}.drag-preview .drag-items{width:max-content;height:max-content;position:relative}.drag-preview .drag-items .drag-item{width:120px;height:120px;position:absolute;top:0;left:0;background:var(--base);border-radius:var(--outerRadius);box-shadow:var(--shadow)}.drag-preview .drag-items .drag-item:nth-child(1){transform:rotate(-3deg);z-index:3}.drag-preview .drag-items .drag-item:nth-child(2){left:8px;top:-4px;transform:rotate(4deg);z-index:2;transition-delay:30ms}.drag-preview .drag-items .drag-item:nth-child(3){left:-6px;top:-8px;transform:rotate(-5deg);z-index:1;transition-delay:60ms}.drag-preview .drag-items .drag-item:nth-child(4){left:12px;top:-12px;transform:rotate(3deg);z-index:0;transition-delay:90ms}.drag-preview .drag-items .drag-item:nth-child(n+5){left:-10px;top:-16px;transform:rotate(-4deg);z-index:0;opacity:.8}.drag-preview .drag-items img,.drag-preview .drag-items video{width:100%;height:100%;object-fit:cover;display:block}.drag-preview .drag-count{position:absolute;top:-8px;right:-8px;background:var(--base-200);color:var(--contrast);border-radius:50%;width:24px;height:24px;display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:700;box-shadow:var(--shadow);z-index:var(--z-3)}.item.dragging{opacity:.5;transform:scale(.95);filter:grayscale(50%);transition:opacity .2s ease,transform .2s ease,filter .2s ease}@keyframes drop-pulse{0%,100%{background-color:rgba(var(--action-rgb),var(--rgb-light));transform:scale(1.02)}50%{background-color:var(rgba(var(--action-rgb),var(--rgb-medium)));transform:scale(1.04)}}.group-actions{display:flex;gap:.25rem}@media (max-width:767px){body:not(.uploading):has(.group-display:not([hidden])){overflow:hidden}body:not(.uploading):has(.group-display:not([hidden])) .qtoggle{z-index:var(--z-1)}.group-display.group-display{position:fixed;top:var(--height);bottom:var(--height);left:0;right:0;max-height:var(--maxHeight);overflow:hidden;z-index:var(--z-6);width:calc(100% - 1rem);height:calc(100% - 1rem);padding:0 0 3rem;--justify:flex-start;--align:flex-start;--gap:0}.group-display::before{content:'';display:block;z-index:-1;top:-.5rem;bottom:-.5rem;left:-.5rem;right:-.5rem;position:absolute;background-color:rgba(var(--base-rgb),var(--rgb-heavy));filter:blur(5px)}.group-display .preview-wrap,.group-display .sidebar{height:50%;overflow:hidden auto;position:relative;padding:.5rem}.group-display .preview-wrap{top:0}.group-display .preview-wrap .selected{display:flex;justify-content:space-between;align-items:center}.group-display .sidebar{bottom:0;flex-wrap:nowrap;overflow:hidden auto;background-color:var(--contrast-200);color:var(--base)}.group-display .sidebar>.hint{color:var(--contrast)}.group-display .sidebar .header{display:none}.group-display .preview-actions{top:0;flex-shrink:0}.group-display .preview-wrap>.hint,.group-display .sidebar>.hint{bottom:0;margin:0;text-align:center}.group-display .preview-actions,.group-display .preview-wrap>.hint,.group-display .sidebar>.hint{position:absolute;left:0;right:0;background-color:rgba(var(--base-rgb),var(--rgb-heavy));z-index:var(--z-3);box-shadow:var(--shadow)}.group-display .item-grid{height:100%;overflow:hidden auto;grid-template-columns:repeat(3,1fr);padding:2rem 0}.group-display .sidebar>.item-grid{grid-template-columns:repeat(1,1fr);gap:1rem;padding:0}.group-display .sidebar .empty-group{order:0;position:sticky;height:fit-content;top:0;z-index:var(--z-3);background-color:rgba(var(--action-rgb),var(--rgb-heavy))}.group-display .sidebar .upload-group{order:1}.group-display .sidebar .empty-group p{margin:0}.group-display .field,.group-display .field label{margin:0;padding:0}.group-display .sidebar h4{margin:.25rem}.group-display .item{width:100%;height:max-content}.submit-uploads{bottom:var(--height);left:0;right:0;width:100%;height:3rem}body.uploading .group-display.group-display{position:relative;top:unset;bottom:unset;right:unset;left:unset}}@media (min-width:768px){.group-display.group-display{--wrap:nowrap;--dir:row;--gap:1rem;--align:flex-start}.group-display .preview-wrap,.group-display .sidebar{--justify:flex-start;max-height:calc(100vh - var(--doubleHeight));overflow:hidden auto}.group-display .preview-wrap,.group-display .sidebar{width:50%}.preview-actions,.preview-wrap .hint{position:sticky;z-index:var(--z-3);box-shadow:var(--shadow);background-color:var(--base);width:100%}.preview-actions{top:0;left:0;right:0}.preview-actions .field{margin:0}.preview-wrap .hint,.sidebar>.hint{bottom:-1rem;padding-bottom:1rem;margin:0;left:0;right:0;text-align:center}}.restore-uploads{position:fixed;top:var(--offHeight);bottom:var(--offHeight);left:1rem;right:1rem;border-radius:var(--outerRadius);padding:1rem;z-index:var(--z-top);box-shadow:var(--shadow);background-color:var(--base-200);overflow:hidden auto}dialog nav.tabs{position:sticky;top:0;background-color:var(--base-50);z-index:var(--z-6);box-shadow:var(--shadow-down);margin-bottom:2rem}.editor-container .ql-toolbar{display:flex;background-color:var(--base-50);justify-content:flex-start;flex-wrap:wrap;padding:.25rem;gap:.5rem 1rem;border-top-left-radius:var(--innerRadius);border-top-right-radius:var(--innerRadius);border-bottom:4px solid var(--base-50)}.ql-toolbar .ql-formats{display:flex;gap:.25rem}.editor-container .ql-container{--padding:1rem;background-color:var(--base);border-bottom-left-radius:var(--innerRadius);border-bottom-right-radius:var(--innerRadius);height:fit-content;padding:2px;border:1px solid var(--base-200)}.editor-container .ql-container .ql-editor{padding:var(--padding);width:100%;height:100%}.ql-editor img{max-width:50%;height:auto}.ql-clipboard{left:-100000px;height:1px;overflow-y:hidden;position:absolute;top:50%}.ql-hidden{display:none}.ql-tooltip{position:absolute;transform:translateY(10px);background-color:var(--base-100);border:1px solid var(--base);box-shadow:0 0 5px var(--overlay-heavy);color:var(--contrast);padding:5px 12px;white-space:nowrap}[data-type=single] .item-grid{display:flex}.repeater-row details summary::after{margin-left:0}.repeater-row details summary button{margin-left:auto}/*!* Group actions buttons - more visible *!*//*!* Group item grid - distinct from preview grid *!*//*!* Group count hint *!*//*!* ============================================================================*//*!* Base drag preview *!*//*!* Single item drag preview *!*//*!* Multi-item drag preview container *!*//*!* Items being dragged - reduce opacity on originals *!*//*!* Count badge on multi-item preview *!*//*!* ============================================================================*//*!* Ensure progress bar is visible when needed *!*//*!* Progress bar track *!*//*!* Progress bar fill *!*//*!* Progress details - styled for row layout with text and count *!*//*!* Individual item progress - overlay style *!*//*!* Item progress icon and status text *!*//*!* ============================================================================*//*!* Hide uploader when we have uploads *!*//*!* Show group display when we have uploads *!*//*!* ============================================================================*//*!* Selected items - more obvious *!*//*!* Selection checkbox - always visible on hover or when checked *!*//*!* Selection controls - more prominent *!*//*!* ============================================================================*//*!* Smooth dragover animation *!*//*!* ============================================================================*//*!* ============================================================================*//*!* Notification container - fixed overlay *!*//*!* Content card *!*//*!* Message section *!*//*!* Scrollable field list *!*//*!* Item grid for restore preview *!*//*!* Restore item *!*//*!* Checked state *!*//*!* Preview section *!*//*!* Item info *!*//*!* Checkbox controls *!*//*!* Actions section *!*//*!* Selection controls *!*//*!* Action buttons *!*//*!* Restore button - primary action *!*//*!* Scrap cache button - destructive action *!*//*!* Dismiss button - secondary action *!*//*!* Mobile responsive *!*//*!* Animation *!*//*!* Scrollbar styling for restore field list *!*/form{--step-size:2.5rem}.form-progress{padding:0 1rem}.form-progress .progress{background:var(--base-100);border-radius:var(--innerRadius);padding:1rem}.form-progress .bar{height:6px;background:var(--base-200);border-radius:3px;overflow:hidden;margin-bottom:.5rem}.form-progress .fill{height:100%;background:linear-gradient(90deg,var(--action-0),var(--action-200));width:0%;transition:width .4s ease;border-radius:3px}.form-progress .step-text{font-size:var(--small);font-weight:600;color:var(--contrast-200)}form nav.tabs{position:relative;top:0;left:0;right:0;padding:1rem 0;gap:0;z-index:0}form nav.tabs button{position:relative;background:0 0;border:none;padding:.5rem 1rem .5rem 3rem;z-index:1}form nav.tabs .step-number{width:2.5rem;height:100%;border-radius:50% 0 0 50%;position:absolute;left:0;top:0;background:var(--base-200);color:var(--contrast-50);display:flex;align-items:center;justify-content:center;font-weight:700;font-size:var(--small);border:3px solid var(--base)}form nav.tabs button.pending .step-number{background:var(--base-100);color:var(--contrast-200)}form nav.tabs button.active .step-number,form nav.tabs button.current .step-number{background:var(--action-0);color:var(--action-contrast);border-color:var(--action-200)}form nav.tabs button.completed .step-number{background:var(--successBack);color:var(--successBack);border-color:var(--successText)}form nav.tabs button.completed .step-number::before{content:'✓';font-size:1.2rem;color:var(--successText);position:absolute}form nav.tabs button.completed h2{color:var(--contrast-200)}.step-navigation{margin-top:2rem;padding-top:2rem;border-top:1px solid var(--base-200);gap:1rem}.step-navigation .prev-step{background:var(--base-100)}.step-navigation .next-step,.step-navigation button[type=submit]{margin-left:auto}.field input.error,.field select.error,.field textarea.error{border-color:var(--errorBack)}.error-message{color:var(--errorText);font-size:var(--small);margin-top:.25rem;display:block}@media (max-width:768px){form nav.tabs button{min-width:80px;font-size:var(--small)}form nav.tabs button h2{font-size:var(--small)}form{--step-size:2rem}}.field-input-wrapper{position:relative;display:flex;align-items:center;gap:.5rem}.field-input-wrapper input,.field-input-wrapper select,.field-input-wrapper textarea{flex:1}.validation-icon{display:flex;align-items:center;justify-content:center;font-size:1.25rem;animation:scaleIn .3s ease;--w:1.25rem}.validation-icon.error{color:var(--error)}.validation-icon.success{color:var(--success)}@keyframes scaleIn{from{transform:scale(0);opacity:0}to{transform:scale(1);opacity:1}}.validation-message{color:var(--error-0);font-size:var(--small);margin-top:.25rem;display:block;animation:slideDown .2s ease}@keyframes slideDown{from{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.field.has-error input,.field.has-error select,.field.has-error textarea{border-color:var(--error);background-color:var(--errorBack)}.field.has-error input:focus,.field.has-error select:focus,.field.has-error textarea:focus{outline-color:var(--error);box-shadow:0 0 0 3px rgba(var(--error-rgb),.2)}.field.has-success input,.field.has-success select,.field.has-success textarea{border-color:var(--success)}.field label .required{color:var(--error);margin-left:.25rem}.form-summary{padding:2rem;border-radius:8px;margin-top:2rem;border:2px dashed var(--contrast-200)}.form-summary .message{margin-bottom:2rem}.form-summary .result+.result{position:relative;margin-top:1.5rem;padding-top:1.5rem}.form-summary .result+.result::before{position:absolute;top:0;left:16.5%;content:'';width:67%;height:1px;border-bottom:1px solid var(--base-200)}.form-summary h2{margin:1rem 0}.form-summary h4{background-color:var(--base-100);padding:.5rem 2rem;position:relative;left:-2rem;color:var(--contrast-200);font-size:.875rem;text-transform:uppercase;letter-spacing:.05em;margin-bottom:.75rem}.form-summary p{color:var(--text);margin:0}.group-summary,.repeater-summary{background:var(--base-100);padding:1rem;border-radius:4px;margin-top:.5rem}.repeater-row{margin-bottom:1rem}.repeater-row:last-child{margin-bottom:0}.ql-toolbar button{--height:fit-content;padding:.5rem}
\ No newline at end of file
+details.uploader .file-upload-container{margin:1rem 0;max-width:100%}@media (min-width:768px){details.uploader .file-upload-container{margin:1rem var(--mr) 1rem var(--ml);max-width:var(--maxWidth)}}.file-upload-wrapper{border:2px dashed var(--action-0);border-radius:4px;padding:2rem;text-align:center;transition:all .3s ease;background:rgba(var(--action-rgb),var(--rgb-subtle));position:relative;cursor:pointer}.file-upload-wrapper h2{margin:0!important;font-size:var(--large)}.dragover,.file-upload-wrapper:hover{background:rgba(var(--action-rgb),var(--rgb-subtle-hover));border-color:var(--action-0)!important}.file-upload-wrapper input[type=file]{position:absolute;left:0;top:0;width:100%;height:100%;opacity:0;cursor:pointer}.file-upload-text{color:var(--contrast);margin:0;font-family:var(--body)}.file-upload-text strong{color:var(--action-0);text-decoration:underline}.field.upload:has(.upload-item) .file-upload-container{display:none}.field.upload{position:relative}.field.upload:not(.uploading) .progress{display:none}.field.upload .actions{position:absolute;top:0;right:0}.item-grid.group,.item-grid.preview,.item-grid.restore{grid-template-columns:repeat(3,1fr)}.item-grid.group .item,.item-grid.preview .item,.item-grid.restore .item{display:block}.item-grid.group button,.item-grid.preview button,.item-grid.restore button{padding:.25rem .5rem}.item-grid.group button .icon,.item-grid.preview button .icon,.item-grid.restore button .icon{--w:1.1em}.item-grid.group .item .preview>input[type=checkbox]:not(.label-button)+label,.item-grid.preview .item .preview>input[type=checkbox]:not(.label-button)+label,.item-grid.restore .item .preview>input[type=checkbox]:not(.label-button)+label{padding-left:0;margin:0}.item-grid.group .item .preview>input[type=checkbox]+label:before,.item-grid.preview .item .preview>input[type=checkbox]+label:before,.item-grid.restore .item .preview>input[type=checkbox]+label:before{transform:unset;top:.5rem;left:.5rem}.item-grid.group .item .preview>input[type=checkbox]+label::after,.item-grid.preview .item .preview>input[type=checkbox]+label::after,.item-grid.restore .item .preview>input[type=checkbox]+label::after{top:.5rem;left:.75rem;transform:translateY(20%) rotate(45deg)}.item-grid.group .item .item-actions,.item-grid.preview .item .item-actions,.item-grid.restore .item .item-actions{position:absolute;top:0;right:0}.item-grid.group summary,.item-grid.preview summary,.item-grid.restore summary{padding:.5rem}.item-grid.group:has([type=checkbox]:checked),.item-grid.preview:has([type=checkbox]:checked),.item-grid.restore:has([type=checkbox]:checked){padding:1rem;background-color:rgba(var(--contrast-rgb),var(--rgb-subtle))}.item-grid.group:has([type=checkbox]:checked) .item,.item-grid.preview:has([type=checkbox]:checked) .item,.item-grid.restore:has([type=checkbox]:checked) .item{padding:.75rem;opacity:.8}.item-grid.group:has([type=checkbox]:checked) .item img,.item-grid.preview:has([type=checkbox]:checked) .item img,.item-grid.restore:has([type=checkbox]:checked) .item img{filter:var(--filter)}.item-grid.group:has([type=checkbox]:checked) details,.item-grid.preview:has([type=checkbox]:checked) details,.item-grid.restore:has([type=checkbox]:checked) details{display:none}.item-grid.group .item:has([type=checkbox]:checked),.item-grid.preview .item:has([type=checkbox]:checked),.item-grid.restore .item:has([type=checkbox]:checked){padding:.5rem;background-color:rgba(var(--action-rgb),var(--rgb-medium));opacity:1}.item-grid.group .item:has([type=checkbox]:checked) img,.item-grid.preview .item:has([type=checkbox]:checked) img,.item-grid.restore .item:has([type=checkbox]:checked) img{filter:none}[type=radio].featured+label .star+.star,[type=radio].featured:checked+label .star{display:none}[type=radio].featured+label .star,[type=radio].featured:checked+label .star+.star{display:inline-block}.restore.restore.item,.upload.upload.item{border-radius:var(--innerRadius);aspect-ratio:unset;overflow:hidden;background:var(--base);border:1px solid var(--base-200)}.restore-item [for=select-item],.upload.item [for=select-item]{aspect-ratio:1}.upload.item:has(details[open]){grid-column:1/-1}.restore.item img,.upload.item img{transition:transform var(--transition-base)}.restore.item:hover img,.upload.item:hover img{transform:scale(1.02);transition:transform var(--transition-base)}.upload-group{background-image:var(--dashed-action);padding:5px;border-radius:var(--innerRadius);background-color:rgba(var(--action-rgb),var(--rgb-subtle))}.submit-uploads{position:fixed;bottom:var(--offHeight);right:var(--offHeight);z-index:var(--z-6);height:var(--height);box-shadow:var(--shadow);border-radius:var(--innerRadius);animation:pulse-color 5s infinite;animation-delay:1s;background-color:var(--action-0);color:var(--action-contrast)}.submit-uploads:hover{background-color:var(--base-200);color:var(--contrast-200)}.empty-group{grid-column:1/-1;padding:20px;background-image:var(--dashed-action);border-radius:var(--innerRadius);margin:10px 0;cursor:pointer;transition:all var(--transition-base);text-align:center;background-color:rgba(var(--action-rgb),var(--rgb-subtle))}.group-display:not([hidden])~.file-upload-container{display:none}.dragging,.upload.item.dragging{opacity:.7;transform:scale(.95) rotate(3deg);z-index:var(--z-top);box-shadow:0 8px 25px rgba(0,0,0,.3)}.dragover{background:rgba(var(--action-rgb),var(--rgb-light))!important;border-color:var(--action-0)!important;transform:scale(1.05);animation:drop-pulse .8s infinite ease-in-out}.drag-preview{position:fixed;z-index:var(--zz-top);width:fit-content;overflow:visible;pointer-events:none;opacity:.9;transform:scale(1.05);transition:transform .2s ease}.drag-preview .drag-items{width:max-content;height:max-content;position:relative}.drag-preview .drag-items .drag-item{width:120px;height:120px;position:absolute;top:0;left:0;background:var(--base);border-radius:var(--outerRadius);box-shadow:var(--shadow)}.drag-preview .drag-items .drag-item:nth-child(1){transform:rotate(-3deg);z-index:3}.drag-preview .drag-items .drag-item:nth-child(2){left:8px;top:-4px;transform:rotate(4deg);z-index:2;transition-delay:30ms}.drag-preview .drag-items .drag-item:nth-child(3){left:-6px;top:-8px;transform:rotate(-5deg);z-index:1;transition-delay:60ms}.drag-preview .drag-items .drag-item:nth-child(4){left:12px;top:-12px;transform:rotate(3deg);z-index:0;transition-delay:90ms}.drag-preview .drag-items .drag-item:nth-child(n+5){left:-10px;top:-16px;transform:rotate(-4deg);z-index:0;opacity:.8}.drag-preview .drag-items img,.drag-preview .drag-items video{width:100%;height:100%;object-fit:cover;display:block}.drag-preview .drag-count{position:absolute;top:-8px;right:-8px;background:var(--base-200);color:var(--contrast);border-radius:50%;width:24px;height:24px;display:flex;align-items:center;justify-content:center;font-size:12px;font-weight:700;box-shadow:var(--shadow);z-index:var(--z-3)}.item.dragging{opacity:.5;transform:scale(.95);filter:grayscale(50%);transition:opacity .2s ease,transform .2s ease,filter .2s ease}@keyframes drop-pulse{0%,100%{background-color:rgba(var(--action-rgb),var(--rgb-light));transform:scale(1.02)}50%{background-color:var(rgba(var(--action-rgb),var(--rgb-medium)));transform:scale(1.04)}}.group-actions{display:flex;gap:.25rem}@media (max-width:767px){body:not(.uploading):has(.group-display:not([hidden])){overflow:hidden}body:not(.uploading):has(.group-display:not([hidden])) .qtoggle{z-index:var(--z-1)}.group-display.group-display{position:fixed;top:var(--height);bottom:var(--height);left:0;right:0;max-height:var(--maxHeight);overflow:hidden;z-index:var(--z-6);width:calc(100% - 1rem);height:calc(100% - 1rem);padding:0 0 3rem;--justify:flex-start;--align:flex-start;--gap:0}.group-display::before{content:'';display:block;z-index:-1;top:-.5rem;bottom:-.5rem;left:-.5rem;right:-.5rem;position:absolute;background-color:rgba(var(--base-rgb),var(--rgb-heavy));filter:blur(5px)}.group-display .preview-wrap,.group-display .sidebar{height:50%;overflow:hidden auto;position:relative;padding:.5rem}.group-display .preview-wrap{top:0}.group-display .preview-wrap .selected{display:flex;justify-content:space-between;align-items:center}.group-display .sidebar{bottom:0;flex-wrap:nowrap;overflow:hidden auto;background-color:var(--contrast-200);color:var(--base)}.group-display .sidebar>.hint{color:var(--contrast)}.group-display .sidebar .header{display:none}.group-display .preview-actions{top:0;flex-shrink:0}.group-display .preview-wrap>.hint,.group-display .sidebar>.hint{bottom:0;margin:0;text-align:center}.group-display .preview-actions,.group-display .preview-wrap>.hint,.group-display .sidebar>.hint{position:absolute;left:0;right:0;background-color:rgba(var(--base-rgb),var(--rgb-heavy));z-index:var(--z-3);box-shadow:var(--shadow)}.group-display .item-grid{height:100%;overflow:hidden auto;grid-template-columns:repeat(3,1fr);padding:2rem 0}.group-display .sidebar>.item-grid{grid-template-columns:repeat(1,1fr);gap:1rem;padding:0}.group-display .sidebar .empty-group{order:0;position:sticky;height:fit-content;top:0;z-index:var(--z-3);background-color:rgba(var(--action-rgb),var(--rgb-heavy))}.group-display .sidebar .upload-group{order:1}.group-display .sidebar .empty-group p{margin:0}.group-display .field,.group-display .field label{margin:0;padding:0}.group-display .sidebar h4{margin:.25rem}.group-display .item{width:100%;height:max-content}.submit-uploads{bottom:var(--height);left:0;right:0;width:100%;height:3rem}body.uploading .group-display.group-display{position:relative;top:unset;bottom:unset;right:unset;left:unset}}@media (min-width:768px){.group-display.group-display{--wrap:nowrap;--dir:row;--gap:1rem;--align:flex-start}.group-display .preview-wrap,.group-display .sidebar{--justify:flex-start;max-height:calc(100vh - var(--doubleHeight));overflow:hidden auto}.group-display .preview-wrap,.group-display .sidebar{width:50%}.preview-actions,.preview-wrap .hint{position:sticky;z-index:var(--z-3);box-shadow:var(--shadow);background-color:var(--base);width:100%}.preview-actions{top:0;left:0;right:0}.preview-actions .field{margin:0}.preview-wrap .hint,.sidebar>.hint{bottom:-1rem;padding-bottom:1rem;margin:0;left:0;right:0;text-align:center}}.restore-uploads{position:fixed;top:var(--offHeight);bottom:var(--offHeight);left:1rem;right:1rem;border-radius:var(--outerRadius);padding:1rem;z-index:var(--z-top);box-shadow:var(--shadow);background-color:var(--base-200);overflow:hidden auto}dialog nav.tabs{position:sticky;top:0;background-color:var(--base-50);z-index:var(--z-6);box-shadow:var(--shadow-down);margin-bottom:2rem}.editor-container .ql-toolbar{display:flex;background-color:var(--base-50);justify-content:flex-start;flex-wrap:wrap;padding:.25rem;gap:.5rem 1rem;border-top-left-radius:var(--innerRadius);border-top-right-radius:var(--innerRadius);border-bottom:4px solid var(--base-50)}.ql-toolbar .ql-formats{display:flex;gap:.25rem}.editor-container .ql-container{--padding:1rem;background-color:var(--base);border-bottom-left-radius:var(--innerRadius);border-bottom-right-radius:var(--innerRadius);height:fit-content;padding:2px;border:1px solid var(--base-200)}.editor-container .ql-container .ql-editor{padding:var(--padding);width:100%;height:100%}.ql-editor img{max-width:50%;height:auto}.ql-clipboard{left:-100000px;height:1px;overflow-y:hidden;position:absolute;top:50%}.ql-hidden{display:none}.ql-tooltip{position:absolute;transform:translateY(10px);background-color:var(--base-100);border:1px solid var(--base);box-shadow:0 0 5px var(--overlay-heavy);color:var(--contrast);padding:5px 12px;white-space:nowrap}[data-type=single] .item-grid{display:flex}.repeater-row details summary::after{margin-left:0}.repeater-row details summary button{margin-left:auto}/*!* Group actions buttons - more visible *!*//*!* Group item grid - distinct from preview grid *!*//*!* Group count hint *!*//*!* ============================================================================*//*!* Base drag preview *!*//*!* Single item drag preview *!*//*!* Multi-item drag preview container *!*//*!* Items being dragged - reduce opacity on originals *!*//*!* Count badge on multi-item preview *!*//*!* ============================================================================*//*!* Ensure progress bar is visible when needed *!*//*!* Progress bar track *!*//*!* Progress bar fill *!*//*!* Progress details - styled for row layout with text and count *!*//*!* Individual item progress - overlay style *!*//*!* Item progress icon and status text *!*//*!* ============================================================================*//*!* Hide uploader when we have uploads *!*//*!* Show group display when we have uploads *!*//*!* ============================================================================*//*!* Selected items - more obvious *!*//*!* Selection checkbox - always visible on hover or when checked *!*//*!* Selection controls - more prominent *!*//*!* ============================================================================*//*!* Smooth dragover animation *!*//*!* ============================================================================*//*!* ============================================================================*//*!* Notification container - fixed overlay *!*//*!* Content card *!*//*!* Message section *!*//*!* Scrollable field list *!*//*!* Item grid for restore preview *!*//*!* Restore item *!*//*!* Checked state *!*//*!* Preview section *!*//*!* Item info *!*//*!* Checkbox controls *!*//*!* Actions section *!*//*!* Selection controls *!*//*!* Action buttons *!*//*!* Restore button - primary action *!*//*!* Scrap cache button - destructive action *!*//*!* Dismiss button - secondary action *!*//*!* Mobile responsive *!*//*!* Animation *!*//*!* Scrollbar styling for restore field list *!*/form{--step-size:2.5rem}.form-progress{padding:0 1rem}.form-progress .progress{background:var(--base-100);border-radius:var(--innerRadius);padding:1rem}.form-progress .bar{height:6px;background:var(--base-200);border-radius:3px;overflow:hidden;margin-bottom:.5rem}.form-progress .fill{height:100%;background:linear-gradient(90deg,var(--action-0),var(--action-200));width:0%;transition:width .4s ease;border-radius:3px}.form-progress .step-text{font-size:var(--small);font-weight:600;color:var(--contrast-200)}form nav.tabs{position:relative;top:0;left:0;right:0;padding:1rem 0;gap:0;z-index:0}form nav.tabs button{position:relative;background:0 0;border:none;padding:.5rem 1rem .5rem 3rem;z-index:1}form nav.tabs .step-number{width:2.5rem;height:100%;border-radius:50% 0 0 50%;position:absolute;left:0;top:0;background:var(--base-200);color:var(--contrast-50);display:flex;align-items:center;justify-content:center;font-weight:700;font-size:var(--small);border:3px solid var(--base)}form nav.tabs button.pending .step-number{background:var(--base-100);color:var(--contrast-200)}form nav.tabs button.active .step-number,form nav.tabs button.current .step-number{background:var(--action-0);color:var(--action-contrast);border-color:var(--action-200)}form nav.tabs button.completed .step-number{background:var(--successBack);color:var(--successBack);border-color:var(--successText)}form nav.tabs button.completed .step-number::before{content:'✓';font-size:1.2rem;color:var(--successText);position:absolute}form nav.tabs button.completed h2{color:var(--contrast-200)}.step-navigation{margin-top:2rem;padding-top:2rem;border-top:1px solid var(--base-200);gap:1rem}.step-navigation .prev-step{background:var(--base-100)}.step-navigation .next-step,.step-navigation button[type=submit]{margin-left:auto}.field input.error,.field select.error,.field textarea.error{border-color:var(--errorBack)}.error-message{color:var(--errorText);font-size:var(--small);margin-top:.25rem;display:block}@media (max-width:768px){form nav.tabs button{min-width:80px;font-size:var(--small)}form nav.tabs button h2{font-size:var(--small)}form{--step-size:2rem}}.field-input-wrapper{position:relative;display:flex;align-items:center;gap:.5rem}.field-input-wrapper input,.field-input-wrapper select,.field-input-wrapper textarea{flex:1}.validation-icon{display:flex;align-items:center;justify-content:center;font-size:1.25rem;animation:scaleIn .3s ease;--w:1.25rem}.validation-icon.error{color:var(--error)}.validation-icon.success{color:var(--success)}@keyframes scaleIn{from{transform:scale(0);opacity:0}to{transform:scale(1);opacity:1}}.validation-message{color:var(--error-0);font-size:var(--small);margin-top:.25rem;display:block;animation:slideDown .2s ease}@keyframes slideDown{from{opacity:0;transform:translateY(-4px)}to{opacity:1;transform:translateY(0)}}.field.has-error input,.field.has-error select,.field.has-error textarea{border-color:var(--error);background-color:var(--errorBack)}.field.has-error input:focus,.field.has-error select:focus,.field.has-error textarea:focus{outline-color:var(--error);box-shadow:0 0 0 3px rgba(var(--error-rgb),.2)}.field.has-success input,.field.has-success select,.field.has-success textarea{border-color:var(--success)}.field label .required{color:var(--error);margin-left:.25rem}.form-summary{padding:2rem;border-radius:8px;margin-top:2rem;border:2px dashed var(--contrast-200)}.form-summary .message{margin-bottom:2rem}.form-summary .result+.result{position:relative;margin-top:1.5rem;padding-top:1.5rem}.form-summary .result+.result::before{position:absolute;top:0;left:16.5%;content:'';width:67%;height:1px;border-bottom:1px solid var(--base-200)}.form-summary h2{margin:1rem 0}.form-summary h4{background-color:var(--base-100);padding:.5rem 2rem;position:relative;left:-2rem;color:var(--contrast-200);font-size:.875rem;text-transform:uppercase;letter-spacing:.05em;margin-bottom:.75rem}.form-summary p{color:var(--text);margin:0}.group-summary,.repeater-summary{background:var(--base-100);padding:1rem;border-radius:4px;margin-top:.5rem}.repeater-row{margin-bottom:1rem}.repeater-row:last-child{margin-bottom:0}.ql-toolbar button{--height:fit-content;padding:.5rem}.success-message{color:var(--success,#16a34a);background-color:var(--success-bg,#f0fdf4);border:1px solid var(--success,#16a34a);padding:.75rem 1rem;border-radius:var(--radius);margin-bottom:1rem;display:flex;align-items:center;gap:.5rem}.success-message .success-icon{width:1.25rem;height:1.25rem;flex-shrink:0}.success-box{background-color:var(--success-bg,#f0fdf4);border:2px solid var(--success,#16a34a);padding:1.5rem;border-radius:var(--outerRadius);margin-bottom:1rem;text-align:center}.success-box h3{color:var(--success,#16a34a);margin-bottom:.5rem}.success-box p{margin:.5rem 0}.form-success{opacity:.9}.form-success .field:not(.form-success-message):not(.success-box){display:none}.form-success button[type=submit]{opacity:.6;pointer-events:none}.field-error input,.field-error select,.field-error textarea{border-color:var(--error,#dc2626)}.error-message{color:var(--error,#dc2626);font-size:var(--small);margin-top:.25rem;display:block}.form-error{background-color:var(--error-bg,#fee);border:1px solid var(--error,#dc2626);padding:.75rem;border-radius:var(--radius);margin-bottom:1rem}.has-success input,.has-success select,.has-success textarea{border-color:var(--success,#16a34a)}.form-error{display:flex;align-items:center;gap:.5rem}.form-error .error-icon{width:1.25rem;height:1.25rem;flex-shrink:0}.autocomplete-dropdown{width:100%;background-color:var(--base-100);padding:.5rem;box-shadow:var(--shadow)}
\ No newline at end of file
diff --git a/assets/css/icons.css b/assets/css/icons.css
new file mode 100644
index 0000000..429920e
--- /dev/null
+++ b/assets/css/icons.css
@@ -0,0 +1 @@
+.icon-house{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTcuOSwxMTAuMWwtODAtODBhMTQsMTQsMCwwLDAtMTkuOCwwbC04MCw4MEExMy45MiwxMy45MiwwLDAsMCwzNCwxMjB2OTZhNiw2LDAsMCwwLDYsNmg2NGE2LDYsMCwwLDAsNi02VjE1OGgzNnY1OGE2LDYsMCwwLDAsNiw2aDY0YTYsNiwwLDAsMCw2LTZWMTIwQTEzLjkyLDEzLjkyLDAsMCwwLDIxNy45LDExMC4xWk0yMTAsMjEwSDE1OFYxNTJhNiw2LDAsMCwwLTYtNkgxMDRhNiw2LDAsMCwwLTYsNnY1OEg0NlYxMjBhMiwyLDAsMCwxLC41OC0xLjQybDgwLTgwYTIsMiwwLDAsMSwyLjg0LDBsODAsODBBMiwyLDAsMCwxLDIxMCwxMjBaIi8+PC9zdmc+');}.icon-sun-dim{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMjIsNDBWMzJhNiw2LDAsMCwxLDEyLDB2OGE2LDYsMCwwLDEtMTIsMFptNjgsODhhNjIsNjIsMCwxLDEtNjItNjJBNjIuMDcsNjIuMDcsMCwwLDEsMTkwLDEyOFptLTEyLDBhNTAsNTAsMCwxLDAtNTAsNTBBNTAuMDYsNTAuMDYsMCwwLDAsMTc4LDEyOFpNNTkuNzYsNjguMjRhNiw2LDAsMSwwLDguNDgtOC40OGwtOC04YTYsNiwwLDAsMC04LjQ4LDguNDhabTAsMTE5LjUyLTgsOGE2LDYsMCwxLDAsOC40OCw4LjQ4bDgtOGE2LDYsMCwxLDAtOC40OC04LjQ4Wm0xMzYtMTM2LTgsOGE2LDYsMCwxLDAsOC40OCw4LjQ4bDgtOGE2LDYsMCwwLDAtOC40OC04LjQ4Wm0uNDgsMTM2YTYsNiwwLDAsMC04LjQ4LDguNDhsOCw4YTYsNiwwLDAsMCw4LjQ4LTguNDhaTTQwLDEyMkgzMmE2LDYsMCwwLDAsMCwxMmg4YTYsNiwwLDAsMCwwLTEyWm04OCw4OGE2LDYsMCwwLDAtNiw2djhhNiw2LDAsMCwwLDEyLDB2LThBNiw2LDAsMCwwLDEyOCwyMTBabTk2LTg4aC04YTYsNiwwLDAsMCwwLDEyaDhhNiw2LDAsMCwwLDAtMTJaIi8+PC9zdmc+');}.icon-moon{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzIuMTMsMTQzLjY0YTYsNiwwLDAsMC02LTEuNDlBOTAuMDcsOTAuMDcsMCwwLDEsMTEzLjg2LDI5Ljg1YTYsNiwwLDAsMC03LjQ5LTcuNDhBMTAyLjg4LDEwMi44OCwwLDAsMCw1NC40OCw1OC42OCwxMDIsMTAyLDAsMCwwLDE5Ny4zMiwyMDEuNTJhMTAyLjg4LDEwMi44OCwwLDAsMCwzNi4zMS01MS44OUE2LDYsMCwwLDAsMjMyLjEzLDE0My42NFptLTQyLDQ4LjI5YTkwLDkwLDAsMCwxLTEyNi0xMjZBOTAuOSw5MC45LDAsMCwxLDk5LjY1LDM3LjY2LDEwMi4wNiwxMDIuMDYsMCwwLDAsMjE4LjM0LDE1Ni4zNSw5MC45LDkwLjksMCwwLDEsMTkwLjEsMTkxLjkzWiIvPjwvc3ZnPg==');}.icon-sign-out{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMTgsMjE2YTYsNiwwLDAsMS02LDZINDhhNiw2LDAsMCwxLTYtNlY0MGE2LDYsMCwwLDEsNi02aDY0YTYsNiwwLDAsMSwwLDEySDU0VjIxMGg1OEE2LDYsMCwwLDEsMTE4LDIxNlptMTEwLjI0LTkyLjI0LTQwLTQwYTYsNiwwLDAsMC04LjQ4LDguNDhMMjA5LjUxLDEyMkgxMTJhNiw2LDAsMCwwLDAsMTJoOTcuNTFsLTI5Ljc1LDI5Ljc2YTYsNiwwLDEsMCw4LjQ4LDguNDhsNDAtNDBBNiw2LDAsMCwwLDIyOC4yNCwxMjMuNzZaIi8+PC9zdmc+');}.icon-star{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzcuMjgsOTcuODdBMTQuMTgsMTQuMTgsMCwwLDAsMjI0Ljc2LDg4bC02MC4yNS00Ljg3LTIzLjIyLTU2LjJhMTQuMzcsMTQuMzcsMCwwLDAtMjYuNTgsMEw5MS40OSw4My4xMSwzMS4yNCw4OGExNC4xOCwxNC4xOCwwLDAsMC0xMi41Miw5Ljg5QTE0LjQzLDE0LjQzLDAsMCwwLDIzLDExMy4zMkw2OSwxNTIuOTNsLTE0LDU5LjI1YTE0LjQsMTQuNCwwLDAsMCw1LjU5LDE1LDE0LjEsMTQuMSwwLDAsMCwxNS45MS42TDEyOCwxOTYuMTJsNTEuNTgsMzEuNzFhMTQuMSwxNC4xLDAsMCwwLDE1LjkxLS42LDE0LjQsMTQuNCwwLDAsMCw1LjU5LTE1bC0xNC01OS4yNUwyMzMsMTEzLjMyQTE0LjQzLDE0LjQzLDAsMCwwLDIzNy4yOCw5Ny44N1ptLTEyLjE0LDYuMzctNDguNjksNDJhNiw2LDAsMCwwLTEuOTIsNS45MmwxNC44OCw2Mi43OWEyLjM1LDIuMzUsMCwwLDEtLjk1LDIuNTcsMi4yNCwyLjI0LDAsMCwxLTIuNi4xTDEzMS4xNCwxODRhNiw2LDAsMCwwLTYuMjgsMEw3MC4xNCwyMTcuNjFhMi4yNCwyLjI0LDAsMCwxLTIuNi0uMSwyLjM1LDIuMzUsMCwwLDEtMS0yLjU3bDE0Ljg4LTYyLjc5YTYsNiwwLDAsMC0xLjkyLTUuOTJsLTQ4LjY5LTQyYTIuMzcsMi4zNywwLDAsMS0uNzMtMi42NSwyLjI4LDIuMjgsMCwwLDEsMi4wNy0xLjY1bDYzLjkyLTUuMTZhNiw2LDAsMCwwLDUuMDYtMy42OWwyNC42My01OS42YTIuMzUsMi4zNSwwLDAsMSw0LjM4LDBsMjQuNjMsNTkuNmE2LDYsMCwwLDAsNS4wNiwzLjY5bDYzLjkyLDUuMTZhMi4yOCwyLjI4LDAsMCwxLDIuMDcsMS42NUEyLjM3LDIuMzcsMCwwLDEsMjI1LjE0LDEwNC4yNFoiLz48L3N2Zz4=');}.icon-plus-square{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMDgsMzRINDhBMTQsMTQsMCwwLDAsMzQsNDhWMjA4YTE0LDE0LDAsMCwwLDE0LDE0SDIwOGExNCwxNCwwLDAsMCwxNC0xNFY0OEExNCwxNCwwLDAsMCwyMDgsMzRabTIsMTc0YTIsMiwwLDAsMS0yLDJINDhhMiwyLDAsMCwxLTItMlY0OGEyLDIsMCwwLDEsMi0ySDIwOGEyLDIsMCwwLDEsMiwyWm0tMzYtODBhNiw2LDAsMCwxLTYsNkgxMzR2MzRhNiw2LDAsMCwxLTEyLDBWMTM0SDg4YTYsNiwwLDAsMSwwLTEyaDM0Vjg4YTYsNiwwLDAsMSwxMiwwdjM0aDM0QTYsNiwwLDAsMSwxNzQsMTI4WiIvPjwvc3ZnPg==');}.icon-trash{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTYsNTBIMTc0VjQwYTIyLDIyLDAsMCwwLTIyLTIySDEwNEEyMiwyMiwwLDAsMCw4Miw0MFY1MEg0MGE2LDYsMCwwLDAsMCwxMkg1MFYyMDhhMTQsMTQsMCwwLDAsMTQsMTRIMTkyYTE0LDE0LDAsMCwwLDE0LTE0VjYyaDEwYTYsNiwwLDAsMCwwLTEyWk05NCw0MGExMCwxMCwwLDAsMSwxMC0xMGg0OGExMCwxMCwwLDAsMSwxMCwxMFY1MEg5NFpNMTk0LDIwOGEyLDIsMCwwLDEtMiwySDY0YTIsMiwwLDAsMS0yLTJWNjJIMTk0Wk0xMTAsMTA0djY0YTYsNiwwLDAsMS0xMiwwVjEwNGE2LDYsMCwwLDEsMTIsMFptNDgsMHY2NGE2LDYsMCwwLDEtMTIsMFYxMDRhNiw2LDAsMCwxLDEyLDBaIi8+PC9zdmc+');}.icon-cloud-arrow-up{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xODguMjQsMTY0LjI0YTYsNiwwLDAsMS04LjQ4LDBMMTU4LDE0Mi40OVYyMDhhNiw2LDAsMCwxLTEyLDBWMTQyLjQ5bC0yMS43NiwyMS43NWE2LDYsMCwwLDEtOC40OC04LjQ4bDMyLTMyYTYsNiwwLDAsMSw4LjQ4LDBsMzIsMzJBNiw2LDAsMCwxLDE4OC4yNCwxNjQuMjRaTTE2MCw0MkE4Ni4xLDg2LjEsMCwwLDAsODIuNDMsOTAuODgsNjIsNjIsMCwxLDAsNzIsMjE0aDQwYTYsNiwwLDAsMCwwLTEySDcyYTUwLDUwLDAsMCwxLDAtMTAwLDUwLjY4LDUwLjY4LDAsMCwxLDUuOTEuMzZBODUuNTQsODUuNTQsMCwwLDAsNzQsMTI4YTYsNiwwLDAsMCwxMiwwLDc0LDc0LDAsMSwxLDEwMy42LDY3Ljg1LDYsNiwwLDAsMCw0LjgsMTFBODYsODYsMCwwLDAsMTYwLDQyWiIvPjwvc3ZnPg==');}.icon-arrow-elbow-left-up{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzgsMTkyYTYsNiwwLDAsMS02LDZIODhhNiw2LDAsMCwxLTYtNlY2Mi40OUw0NC4yNCwxMDAuMjRhNiw2LDAsMCwxLTguNDgtOC40OGw0OC00OGE2LDYsMCwwLDEsOC40OCwwbDQ4LDQ4YTYsNiwwLDEsMS04LjQ4LDguNDhMOTQsNjIuNDlWMTg2SDIzMkE2LDYsMCwwLDEsMjM4LDE5MloiLz48L3N2Zz4=');}.icon-arrow-elbow-right-up{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjAuMjQsMTAwLjI0YTYsNiwwLDAsMS04LjQ4LDBMMTc0LDYyLjQ5VjE5MmE2LDYsMCwwLDEtNiw2SDI0YTYsNiwwLDAsMSwwLTEySDE2MlY2Mi40OWwtMzcuNzYsMzcuNzVhNiw2LDAsMCwxLTguNDgtOC40OGw0OC00OGE2LDYsMCwwLDEsOC40OCwwbDQ4LDQ4QTYsNiwwLDAsMSwyMjAuMjQsMTAwLjI0WiIvPjwvc3ZnPg==');}.icon-x{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMDQuMjQsMTk1Ljc2YTYsNiwwLDEsMS04LjQ4LDguNDhMMTI4LDEzNi40OSw2MC4yNCwyMDQuMjRhNiw2LDAsMCwxLTguNDgtOC40OEwxMTkuNTEsMTI4LDUxLjc2LDYwLjI0YTYsNiwwLDAsMSw4LjQ4LTguNDhMMTI4LDExOS41MWw2Ny43Ni02Ny43NWE2LDYsMCwwLDEsOC40OCw4LjQ4TDEzNi40OSwxMjhaIi8+PC9zdmc+');}.icon-magnifying-glass{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjguMjQsMjE5Ljc2bC01MS4zOC01MS4zOGE4Ni4xNSw4Ni4xNSwwLDEsMC04LjQ4LDguNDhsNTEuMzgsNTEuMzhhNiw2LDAsMCwwLDguNDgtOC40OFpNMzgsMTEyYTc0LDc0LDAsMSwxLDc0LDc0QTc0LjA5LDc0LjA5LDAsMCwxLDM4LDExMloiLz48L3N2Zz4=');}.icon-squares-four{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMDQsNDJINTZBMTQsMTQsMCwwLDAsNDIsNTZ2NDhhMTQsMTQsMCwwLDAsMTQsMTRoNDhhMTQsMTQsMCwwLDAsMTQtMTRWNTZBMTQsMTQsMCwwLDAsMTA0LDQyWm0yLDYyYTIsMiwwLDAsMS0yLDJINTZhMiwyLDAsMCwxLTItMlY1NmEyLDIsMCwwLDEsMi0yaDQ4YTIsMiwwLDAsMSwyLDJabTk0LTYySDE1MmExNCwxNCwwLDAsMC0xNCwxNHY0OGExNCwxNCwwLDAsMCwxNCwxNGg0OGExNCwxNCwwLDAsMCwxNC0xNFY1NkExNCwxNCwwLDAsMCwyMDAsNDJabTIsNjJhMiwyLDAsMCwxLTIsMkgxNTJhMiwyLDAsMCwxLTItMlY1NmEyLDIsMCwwLDEsMi0yaDQ4YTIsMiwwLDAsMSwyLDJabS05OCwzNEg1NmExNCwxNCwwLDAsMC0xNCwxNHY0OGExNCwxNCwwLDAsMCwxNCwxNGg0OGExNCwxNCwwLDAsMCwxNC0xNFYxNTJBMTQsMTQsMCwwLDAsMTA0LDEzOFptMiw2MmEyLDIsMCwwLDEtMiwySDU2YTIsMiwwLDAsMS0yLTJWMTUyYTIsMiwwLDAsMSwyLTJoNDhhMiwyLDAsMCwxLDIsMlptOTQtNjJIMTUyYTE0LDE0LDAsMCwwLTE0LDE0djQ4YTE0LDE0LDAsMCwwLDE0LDE0aDQ4YTE0LDE0LDAsMCwwLDE0LTE0VjE1MkExNCwxNCwwLDAsMCwyMDAsMTM4Wm0yLDYyYTIsMiwwLDAsMS0yLDJIMTUyYTIsMiwwLDAsMS0yLTJWMTUyYTIsMiwwLDAsMSwyLTJoNDhhMiwyLDAsMCwxLDIsMloiLz48L3N2Zz4=');}.icon-rows{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMDgsMTM4SDQ4YTE0LDE0LDAsMCwwLTE0LDE0djQwYTE0LDE0LDAsMCwwLDE0LDE0SDIwOGExNCwxNCwwLDAsMCwxNC0xNFYxNTJBMTQsMTQsMCwwLDAsMjA4LDEzOFptMiw1NGEyLDIsMCwwLDEtMiwySDQ4YTIsMiwwLDAsMS0yLTJWMTUyYTIsMiwwLDAsMSwyLTJIMjA4YTIsMiwwLDAsMSwyLDJaTTIwOCw1MEg0OEExNCwxNCwwLDAsMCwzNCw2NHY0MGExNCwxNCwwLDAsMCwxNCwxNEgyMDhhMTQsMTQsMCwwLDAsMTQtMTRWNjRBMTQsMTQsMCwwLDAsMjA4LDUwWm0yLDU0YTIsMiwwLDAsMS0yLDJINDhhMiwyLDAsMCwxLTItMlY2NGEyLDIsMCwwLDEsMi0ySDIwOGEyLDIsMCwwLDEsMiwyWiIvPjwvc3ZnPg==');}.icon-table{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjQsNTBIMzJhNiw2LDAsMCwwLTYsNlYxOTJhMTQsMTQsMCwwLDAsMTQsMTRIMjE2YTE0LDE0LDAsMCwwLDE0LTE0VjU2QTYsNiwwLDAsMCwyMjQsNTBaTTM4LDExMEg4MnYzNkgzOFptNTYsMEgyMTh2MzZIOTRaTTIxOCw2MlY5OEgzOFY2MlpNMzgsMTkyVjE1OEg4MnYzNkg0MEEyLDIsMCwwLDEsMzgsMTkyWm0xNzgsMkg5NFYxNThIMjE4djM0QTIsMiwwLDAsMSwyMTYsMTk0WiIvPjwvc3ZnPg==');}.icon-infinity{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yNDYsMTI4YTU0LDU0LDAsMCwxLTkyLjE4LDM4LjE4LDMuMDcsMy4wNywwLDAsMS0uMjUtLjI2bC02MC02Ny43NGE0Miw0MiwwLDEsMCwwLDU5LjY0bDguNTctOS42N2E2LDYsMCwxLDEsOSw4bC04LjY5LDkuODFhMy4wNywzLjA3LDAsMCwxLS4yNS4yNiw1NCw1NCwwLDEsMSwwLTc2LjM2LDMuMDcsMy4wNywwLDAsMSwuMjUuMjZsNjAsNjcuNzRhNDIsNDIsMCwxLDAsMC01OS42NGwtOC41Nyw5LjY3YTYsNiwwLDEsMS05LThsOC42OS05LjgxYTMuMDcsMy4wNywwLDAsMSwuMjUtLjI2QTU0LDU0LDAsMCwxLDI0NiwxMjhaIi8+PC9zdmc+');}.icon-eye{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yNDUuNDgsMTI1LjU3Yy0uMzQtLjc4LTguNjYtMTkuMjMtMjcuMjQtMzcuODFDMjAxLDcwLjU0LDE3MS4zOCw1MCwxMjgsNTBTNTUsNzAuNTQsMzcuNzYsODcuNzZjLTE4LjU4LDE4LjU4LTI2LjksMzctMjcuMjQsMzcuODFhNiw2LDAsMCwwLDAsNC44OGMuMzQuNzcsOC42NiwxOS4yMiwyNy4yNCwzNy44QzU1LDE4NS40Nyw4NC42MiwyMDYsMTI4LDIwNnM3My0yMC41Myw5MC4yNC0zNy43NWMxOC41OC0xOC41OCwyNi45LTM3LDI3LjI0LTM3LjhBNiw2LDAsMCwwLDI0NS40OCwxMjUuNTdaTTEyOCwxOTRjLTMxLjM4LDAtNTguNzgtMTEuNDItODEuNDUtMzMuOTNBMTM0Ljc3LDEzNC43NywwLDAsMSwyMi42OSwxMjgsMTM0LjU2LDEzNC41NiwwLDAsMSw0Ni41NSw5NS45NEM2OS4yMiw3My40Miw5Ni42Miw2MiwxMjgsNjJzNTguNzgsMTEuNDIsODEuNDUsMzMuOTRBMTM0LjU2LDEzNC41NiwwLDAsMSwyMzMuMzEsMTI4QzIyNi45NCwxNDAuMjEsMTk1LDE5NCwxMjgsMTk0Wm0wLTExMmE0Niw0NiwwLDEsMCw0Niw0NkE0Ni4wNiw0Ni4wNiwwLDAsMCwxMjgsODJabTAsODBhMzQsMzQsMCwxLDEsMzQtMzRBMzQsMzQsMCwwLDEsMTI4LDE2MloiLz48L3N2Zz4=');}.icon-eye-closed{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjkuMjEsMTY1YTYsNiwwLDAsMS0xMC40Miw2bC0yMC0zNS4wOGExMjIsMTIyLDAsMCwxLTM5LDE4LjA5bDYuMTcsMzdhNiw2LDAsMCwxLTQuOTMsNi45MSw2Ljg1LDYuODUsMCwwLDEtMSwuMDgsNiw2LDAsMCwxLTUuOTEtNUwxNDgsMTU2LjQ0YTEyOC44NiwxMjguODYsMCwwLDEtNDAsMEwxMDEuOTIsMTkzQTYsNiwwLDAsMSw5NiwxOThhNi44NSw2Ljg1LDAsMCwxLTEtLjA4QTYsNiwwLDAsMSw5MC4wOCwxOTFsNi4xNy0zN2ExMjIsMTIyLDAsMCwxLTM5LTE4LjA5TDM3LjIxLDE3MWE2LDYsMCwxLDEtMTAuNDItNmwyMC44NS0zNi40OGExNTIsMTUyLDAsMCwxLTIwLjMxLTIwLjc3LDYsNiwwLDAsMSw5LjM0LTcuNTRDNTMuNTQsMTIxLjExLDgzLjA3LDE0NiwxMjgsMTQ2czc0LjQ2LTI0Ljg5LDkxLjMzLTQ1Ljc3YTYsNiwwLDAsMSw5LjM0LDcuNTQsMTUyLDE1MiwwLDAsMS0yMC4zMSwyMC43N1oiLz48L3N2Zz4=');}.icon-calendar{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMDgsMzRIMTgyVjI0YTYsNiwwLDAsMC0xMiwwVjM0SDg2VjI0YTYsNiwwLDAsMC0xMiwwVjM0SDQ4QTE0LDE0LDAsMCwwLDM0LDQ4VjIwOGExNCwxNCwwLDAsMCwxNCwxNEgyMDhhMTQsMTQsMCwwLDAsMTQtMTRWNDhBMTQsMTQsMCwwLDAsMjA4LDM0Wk00OCw0Nkg3NFY1NmE2LDYsMCwwLDAsMTIsMFY0Nmg4NFY1NmE2LDYsMCwwLDAsMTIsMFY0NmgyNmEyLDIsMCwwLDEsMiwyVjgySDQ2VjQ4QTIsMiwwLDAsMSw0OCw0NlpNMjA4LDIxMEg0OGEyLDIsMCwwLDEtMi0yVjk0SDIxMFYyMDhBMiwyLDAsMCwxLDIwOCwyMTBabS05OC05MHY2NGE2LDYsMCwwLDEtMTIsMFYxMjkuNzFsLTcuMzIsMy42NmE2LDYsMCwxLDEtNS4zNi0xMC43NGwxNi04QTYsNiwwLDAsMSwxMTAsMTIwWm01OS41NywyOS4yNUwxNDgsMTc4aDIwYTYsNiwwLDAsMSwwLDEySDEzNmE2LDYsMCwwLDEtNC44LTkuNkwxNjAsMTQyYTEwLDEwLDAsMSwwLTE2LjY1LTExQTYsNiwwLDEsMSwxMzMsMTI1YTIyLDIyLDAsMSwxLDM2LjYyLDI0LjI2WiIvPjwvc3ZnPg==');}.icon-alphabetical{--icon:url('data:image/svg+xml;base64,PHN2ZyB3aWR0aD0iMzIiIGhlaWdodD0iMzIiIGZpbGw9ImN1cnJlbnRDb2xvciIgdmVyc2lvbj0iMS4xIiB2aWV3Qm94PSIwIDAgMTgzLjc4IDE4NC4wNiIgeG1sbnM9Imh0dHA6Ly93d3cudzMub3JnLzIwMDAvc3ZnIj48cGF0aCBkPSJtNTkuNTg2IDY5Ljc0MmMtMC44NTEzIDAtMS40NjEgMC4xOTY1Ni0xLjgzNjYgMC41OTcxOC0wLjM1MDU0IDAuMzc1NTgtMC41Mjk1OCAxLjAyMjktMC41Mjk1OCAxLjk0OTNzMC4xNzkwMyAxLjU5MzcgMC41Mjk1OCAxLjk5NDRjMC4zNzU1OCAwLjM3NTU4IDAuOTg1MjkgMC41NjMzOCAxLjgzNjYgMC41NjMzOGg3LjAxOTdsLTEyLjQyOCAzNC4zNjZoLTIuMTA3Yy0wLjg1MTMgMC0xLjQ2MSAwLjE5NjU2LTEuODM2NiAwLjU5NzE4LTAuMzUwNTQgMC4zNzU1OC0wLjUyOTU3IDEuMDM0MS0wLjUyOTU3IDEuOTYwNiAwIDAuOTI2NDQgMC4xNzkwMyAxLjU4MjUgMC41Mjk1NyAxLjk4MyAwLjM3NTU4IDAuMzc1NTkgMC45ODUyOSAwLjU2MzM4IDEuODM2NiAwLjU2MzM4aDEyLjU1MmMwLjg1MTMgMCAxLjQ1MjItMC4xODc3OSAxLjgwMjgtMC41NjMzOCAwLjM3NTU4LTAuNDAwNjIgMC41NjMzNy0xLjA1NjYgMC41NjMzNy0xLjk4MyAwLTAuOTI2NDUtMC4xODc3OS0xLjU4NS0wLjU2MzM3LTEuOTYwNi0wLjM1MDU0LTAuNDAwNjItMC45NTE0Ny0wLjU5NzE4LTEuODAyOC0wLjU5NzE4aC00LjU1MjFsMy4xMjExLTguOTM0OWgxOC4yMmwzLjA3NiA4LjkzNDloLTUuMDcwNGMtMC44NTEzIDAtMS40NjEgMC4xOTY1Ni0xLjgzNjYgMC41OTcxOC0wLjM1MDU0IDAuMzc1NTgtMC41Mjk1OCAxLjAzNDEtMC41Mjk1OCAxLjk2MDYgMCAwLjkyNjQ0IDAuMTc5MDMgMS41ODI1IDAuNTI5NTggMS45ODMgMC4zNzU1OCAwLjM3NTU5IDAuOTg1MjkgMC41NjMzOCAxLjgzNjYgMC41NjMzOGgxMy4yOTZjMC44NTEzIDAgMS40NTIyLTAuMTg3NzkgMS44MDI4LTAuNTYzMzggMC4zNzU1OC0wLjQwMDYyIDAuNTYzMzctMS4wNTY2IDAuNTYzMzctMS45ODMgMC0wLjkyNjQ1LTAuMTg3NzktMS41ODUtMC41NjMzNy0xLjk2MDYtMC4zNTA1NC0wLjQwMDYyLTAuOTUxNDctMC41OTcxOC0xLjgwMjgtMC41OTcxOGgtMi4yODczbC0xMy4yNjItMzcuMDM2Yy0wLjMwMDQ3LTAuODUxMy0wLjc1OTk0LTEuNDYxLTEuMzg1OS0xLjgzNjYtMC42MDA5My0wLjQwMDYyLTEuNDA5Ny0wLjU5NzE4LTIuNDExMy0wLjU5NzE4em00NC4xNDYgMGMtMC44NTEzIDAtMS40NzIzIDAuMTk2NTYtMS44NDc4IDAuNTk3MTgtMC4zNTA1NSAwLjM3NTU4LTAuNTE4MyAxLjAyMjktMC41MTgzIDEuOTQ5M3YxMS45MWMwIDAuODc2MzMgMC4yMDUzMiAxLjUwNjEgMC42MzA5OCAxLjg4MTcgMC40MjU2NiAwLjM3NTU4IDEuMTU5MyAwLjU2MzM3IDIuMTg1OSAwLjU2MzM3czEuNzQ5LTAuMTg3NzkgMi4xNzQ3LTAuNTYzMzdjMC40MjU2OS0wLjM3NTU4IDAuNjQyMjYtMS4wMDUzIDAuNjQyMjYtMS44ODE3di05LjM1MTdoMTguODUxbC0yNC43NTQgMzUuMzAxYy0wLjM1MDU0IDAuNTI1ODItMC41MTgzMSAxLjA3MTctMC41MTgzMSAxLjYyMjYgMCAwLjkyNjQ1IDAuMTY3NzcgMS41ODI1IDAuNTE4MzEgMS45ODMxIDAuMzc1NTggMC4zNzU1OCAwLjk5NjU0IDAuNTYzMzggMS44NDc4IDAuNTYzMzhoMjguNzY2YzAuODUxMyAwIDEuNDUyMi0wLjE4NzggMS44MDI4LTAuNTYzMzggMC4zNzU1OC0wLjQwMDYyIDAuNTYzMzgtMS4wNTY2IDAuNTYzMzgtMS45ODMxdi0xMi42NjVjMC0wLjg3NjMzLTAuMjE2NTgtMS40OTQ4LTAuNjQyMjUtMS44NzA0LTAuNDI1NjYtMC4zNzU1OC0xLjE0OC0wLjU2MzM4LTIuMTc0Ny0wLjU2MzM4LTEuMDI2NiAwLTEuNzQ5IDAuMTg3NzktMi4xNzQ3IDAuNTYzMzgtMC40MjU2NiAwLjM3NTU4LTAuNjQyMjQgMC45OTQwMi0wLjY0MjI0IDEuODcwNHYxMC4xMDdoLTE5Ljk3OGwyNC45MDEtMzUuNDU5YzAuMjUwMzktMC4zNTA1NCAwLjM3MTgzLTAuODM4ODMgMC4zNzE4My0xLjQ2NDggMC0wLjkyNjQ1LTAuMTg3OC0xLjU3MzctMC41NjMzOC0xLjk0OTMtMC4zNTA1NS0wLjQwMDYyLTAuOTUxNDctMC41OTcxOC0xLjgwMjgtMC41OTcxOHptLTMxLjc1MiA1LjEwNDJoMC43MDk4NWw2Ljk4NTkgMjAuMzE1aC0xNC43MTZ6bS0zNy43MjMtNDkuMTgzYy00LjczNDIgMC04LjYzMTMgMy44OTctOC42MzEzIDguNjMxM3YxMTUuNDdjMCA0LjczNDIgMy44OTcgOC42MzEzIDguNjMxMyA4LjYzMTNoMTE1LjI2YzQuNzM0MiAwIDguNjQyMS0zLjg5NyA4LjY0MjEtOC42MzEzdi0xMTUuNDdjMC00LjczNDItMy45MDgyLTguNjMxMy04LjY0MjEtOC42MzEzem0wIDUuNzI0aDExNS4yNmMxLjY1OCAwIDIuOTA3IDEuMjQ5MSAyLjkwNyAyLjkwNzF2MTE1LjQ3YzAgMS42NTgtMS4yNDkxIDIuOTA3LTIuOTA3IDIuOTA3aC0xMTUuMjZjLTEuNjU4IDAtMi44OTU4LTEuMjQ5MS0yLjg5NTgtMi45MDd2LTExNS40N2MwLTEuNjU4IDEuMjM3OC0yLjkwNzEgMi44OTU4LTIuOTA3MXoiIGZpbGw9ImN1cnJlbnRDb2xvciIgc3Ryb2tlLXdpZHRoPSIuNzIxMTQiLz48L3N2Zz4=');}.icon-sort-ascending{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMjYsMTI4YTYsNiwwLDAsMS02LDZINDhhNiw2LDAsMCwxLDAtMTJoNzJBNiw2LDAsMCwxLDEyNiwxMjhaTTQ4LDcwSDE4NGE2LDYsMCwwLDAsMC0xMkg0OGE2LDYsMCwwLDAsMCwxMlptNTYsMTE2SDQ4YTYsNiwwLDAsMCwwLDEyaDU2YTYsNiwwLDAsMCwwLTEyWm0xMjQuMjQtMjIuMjRhNiw2LDAsMCwwLTguNDgsMEwxOTAsMTkzLjUxVjExMmE2LDYsMCwwLDAtMTIsMHY4MS41MWwtMjkuNzYtMjkuNzVhNiw2LDAsMCwwLTguNDgsOC40OGw0MCw0MGE2LDYsMCwwLDAsOC40OCwwbDQwLTQwQTYsNiwwLDAsMCwyMjguMjQsMTYzLjc2WiIvPjwvc3ZnPg==');}.icon-sort-descending{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik00MiwxMjhhNiw2LDAsMCwxLDYtNmg3MmE2LDYsMCwwLDEsMCwxMkg0OEE2LDYsMCwwLDEsNDIsMTI4Wm02LTU4aDU2YTYsNiwwLDAsMCwwLTEySDQ4YTYsNiwwLDAsMCwwLDEyWk0xODQsMTg2SDQ4YTYsNiwwLDAsMCwwLDEySDE4NGE2LDYsMCwwLDAsMC0xMlpNMjI4LjI0LDgzLjc2bC00MC00MGE2LDYsMCwwLDAtOC40OCwwbC00MCw0MGE2LDYsMCwwLDAsOC40OCw4LjQ4TDE3OCw2Mi40OVYxNDRhNiw2LDAsMCwwLDEyLDBWNjIuNDlsMjkuNzYsMjkuNzVhNiw2LDAsMCwwLDguNDgtOC40OFoiLz48L3N2Zz4=');}.icon-floppy-disk{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTcuOSw3My40MiwxODIuNTgsMzguMWExMy45LDEzLjksMCwwLDAtOS44OS00LjFINDhBMTQsMTQsMCwwLDAsMzQsNDhWMjA4YTE0LDE0LDAsMCwwLDE0LDE0SDIwOGExNCwxNCwwLDAsMCwxNC0xNFY4My4zMUExMy45LDEzLjksMCwwLDAsMjE3LjksNzMuNDJaTTE3MCwyMTBIODZWMTUyYTIsMiwwLDAsMSwyLTJoODBhMiwyLDAsMCwxLDIsMlptNDAtMmEyLDIsMCwwLDEtMiwySDE4MlYxNTJhMTQsMTQsMCwwLDAtMTQtMTRIODhhMTQsMTQsMCwwLDAtMTQsMTR2NThINDhhMiwyLDAsMCwxLTItMlY0OGEyLDIsMCwwLDEsMi0ySDE3Mi42OWEyLDIsMCwwLDEsMS40MS41OEwyMDkuNDIsODEuOWEyLDIsMCwwLDEsLjU4LDEuNDFaTTE1OCw3MmE2LDYsMCwwLDEtNiw2SDk2YTYsNiwwLDAsMSwwLTEyaDU2QTYsNiwwLDAsMSwxNTgsNzJaIi8+PC9zdmc+');}.icon-columns{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMDQsMzRINjRBMTQsMTQsMCwwLDAsNTAsNDhWMjA4YTE0LDE0LDAsMCwwLDE0LDE0aDQwYTE0LDE0LDAsMCwwLDE0LTE0VjQ4QTE0LDE0LDAsMCwwLDEwNCwzNFptMiwxNzRhMiwyLDAsMCwxLTIsMkg2NGEyLDIsMCwwLDEtMi0yVjQ4YTIsMiwwLDAsMSwyLTJoNDBhMiwyLDAsMCwxLDIsMlpNMTkyLDM0SDE1MmExNCwxNCwwLDAsMC0xNCwxNFYyMDhhMTQsMTQsMCwwLDAsMTQsMTRoNDBhMTQsMTQsMCwwLDAsMTQtMTRWNDhBMTQsMTQsMCwwLDAsMTkyLDM0Wm0yLDE3NGEyLDIsMCwwLDEtMiwySDE1MmEyLDIsMCwwLDEtMi0yVjQ4YTIsMiwwLDAsMSwyLTJoNDBhMiwyLDAsMCwxLDIsMloiLz48L3N2Zz4=');}.icon-arrows-left-right{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTIuMjQsMTcxLjc2YTYsNiwwLDAsMSwwLDguNDhsLTMyLDMyYTYsNiwwLDAsMS04LjQ4LTguNDhMMTkzLjUxLDE4Mkg0OGE2LDYsMCwwLDEsMC0xMkgxOTMuNTFsLTIxLjc1LTIxLjc2YTYsNiwwLDAsMSw4LjQ4LTguNDhaTTc1Ljc2LDExNi4yNGE2LDYsMCwwLDAsOC40OC04LjQ4TDYyLjQ5LDg2SDIwOGE2LDYsMCwwLDAsMC0xMkg2Mi40OUw4NC4yNCw1Mi4yNGE2LDYsMCwwLDAtOC40OC04LjQ4bC0zMiwzMmE2LDYsMCwwLDAsMCw4LjQ4WiIvPjwvc3ZnPg==');}.icon-arrows-clockwise{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjIsNDhWOTZhNiw2LDAsMCwxLTYsNkgxNjhhNiw2LDAsMCwxLDAtMTJoMzMuNTJMMTgzLjQ3LDcyYTgxLjUxLDgxLjUxLDAsMCwwLTU3LjUzLTI0aC0uNDZBODEuNSw4MS41LDAsMCwwLDY4LjE5LDcxLjI4YTYsNiwwLDEsMS04LjM4LTguNTgsOTMuMzgsOTMuMzgsMCwwLDEsNjUuNjctMjYuNzZIMTI2YTkzLjQ1LDkzLjQ1LDAsMCwxLDY2LDI3LjUzbDE4LDE4VjQ4YTYsNiwwLDAsMSwxMiwwWk0xODcuODEsMTg0LjcyYTgxLjUsODEuNSwwLDAsMS01Ny4yOSwyMy4zNGgtLjQ2YTgxLjUxLDgxLjUxLDAsMCwxLTU3LjUzLTI0TDU0LjQ4LDE2Nkg4OGE2LDYsMCwwLDAsMC0xMkg0MGE2LDYsMCwwLDAtNiw2djQ4YTYsNiwwLDAsMCwxMiwwVjE3NC40OGwxOCwxOC4wNWE5My40NSw5My40NSwwLDAsMCw2NiwyNy41M2guNTJhOTMuMzgsOTMuMzgsMCwwLDAsNjUuNjctMjYuNzYsNiw2LDAsMSwwLTguMzgtOC41OFoiLz48L3N2Zz4=');}.icon-check-circle{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xNzIuMjQsOTkuNzZhNiw2LDAsMCwxLDAsOC40OGwtNTYsNTZhNiw2LDAsMCwxLTguNDgsMGwtMjQtMjRhNiw2LDAsMCwxLDguNDgtOC40OEwxMTIsMTUxLjUxbDUxLjc2LTUxLjc1QTYsNiwwLDAsMSwxNzIuMjQsOTkuNzZaTTIzMCwxMjhBMTAyLDEwMiwwLDEsMSwxMjgsMjYsMTAyLjEyLDEwMi4xMiwwLDAsMSwyMzAsMTI4Wm0tMTIsMGE5MCw5MCwwLDEsMC05MCw5MEE5MC4xLDkwLjEsMCwwLDAsMjE4LDEyOFoiLz48L3N2Zz4=');}.icon-x-circle{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xNjQuMjQsMTAwLjI0LDEzNi40OCwxMjhsMjcuNzYsMjcuNzZhNiw2LDAsMSwxLTguNDgsOC40OEwxMjgsMTM2LjQ4bC0yNy43NiwyNy43NmE2LDYsMCwwLDEtOC40OC04LjQ4TDExOS41MiwxMjgsOTEuNzYsMTAwLjI0YTYsNiwwLDAsMSw4LjQ4LTguNDhMMTI4LDExOS41MmwyNy43Ni0yNy43NmE2LDYsMCwwLDEsOC40OCw4LjQ4Wk0yMzAsMTI4QTEwMiwxMDIsMCwxLDEsMTI4LDI2LDEwMi4xMiwxMDIuMTIsMCwwLDEsMjMwLDEyOFptLTEyLDBhOTAsOTAsMCwxLDAtOTAsOTBBOTAuMSw5MC4xLDAsMCwwLDIxOCwxMjhaIi8+PC9zdmc+');}.icon-user{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjkuMTksMjEzYy0xNS44MS0yNy4zMi00MC42My00Ni40OS02OS40Ny01NC42MmE3MCw3MCwwLDEsMC02My40NCwwQzY3LjQ0LDE2Ni41LDQyLjYyLDE4NS42NywyNi44MSwyMTNhNiw2LDAsMSwwLDEwLjM4LDZDNTYuNCwxODUuODEsOTAuMzQsMTY2LDEyOCwxNjZzNzEuNiwxOS44MSw5MC44MSw1M2E2LDYsMCwxLDAsMTAuMzgtNlpNNzAsOTZhNTgsNTgsMCwxLDEsNTgsNThBNTguMDcsNTguMDcsMCwwLDEsNzAsOTZaIi8+PC9zdmc+');}.icon-crosshair{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzIsMTIySDIyMS44QTk0LjEzLDk0LjEzLDAsMCwwLDEzNCwzNC4yVjI0YTYsNiwwLDAsMC0xMiwwVjM0LjJBOTQuMTMsOTQuMTMsMCwwLDAsMzQuMiwxMjJIMjRhNiw2LDAsMCwwLDAsMTJIMzQuMkE5NC4xMyw5NC4xMywwLDAsMCwxMjIsMjIxLjhWMjMyYTYsNiwwLDAsMCwxMiwwVjIyMS44QTk0LjEzLDk0LjEzLDAsMCwwLDIyMS44LDEzNEgyMzJhNiw2LDAsMCwwLDAtMTJabS05OCw4Ny43NlYyMDBhNiw2LDAsMCwwLTEyLDB2OS43NkE4Mi4wOSw4Mi4wOSwwLDAsMSw0Ni4yNCwxMzRINTZhNiw2LDAsMCwwLDAtMTJINDYuMjRBODIuMDksODIuMDksMCwwLDEsMTIyLDQ2LjI0VjU2YTYsNiwwLDAsMCwxMiwwVjQ2LjI0QTgyLjA5LDgyLjA5LDAsMCwxLDIwOS43NiwxMjJIMjAwYTYsNiwwLDAsMCwwLDEyaDkuNzZBODIuMDksODIuMDksMCwwLDEsMTM0LDIwOS43NlpNMTI4LDkwYTM4LDM4LDAsMSwwLDM4LDM4QTM4LDM4LDAsMCwwLDEyOCw5MFptMCw2NGEyNiwyNiwwLDEsMSwyNi0yNkEyNiwyNiwwLDAsMSwxMjgsMTU0WiIvPjwvc3ZnPg==');}.icon-person{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMjgsNzBBMzAsMzAsMCwxLDAsOTgsNDAsMzAsMzAsMCwwLDAsMTI4LDcwWm0wLTQ4YTE4LDE4LDAsMSwxLTE4LDE4QTE4LDE4LDAsMCwxLDEyOCwyMlptODguODgsMTEzLjQyTDE3MS42Nyw4NC4xNkEzMCwzMCwwLDAsMCwxNDkuMTcsNzRIMTA2LjgzYTMwLDMwLDAsMCwwLTIyLjUsMTAuMTVMMzkuMTIsMTM1LjQyQTE4LDE4LDAsMCwwLDY0LjQ2LDE2MWwyMS4xMS0xNi45M0w2Ny40NCwyMTIuOTJhMTgsMTgsMCwwLDAsMzIuNzUsMTQuOTRMMTI4LDE4MGwyNy44MSw0Ny45MWExOCwxOCwwLDAsMCwzMi43NS0xNC45NGwtMTguMTMtNjguODdMMTkxLjU0LDE2MWExOCwxOCwwLDAsMCwyNS4zNC0yNS41NlptLTguNjMsMTYuODJhNiw2LDAsMCwxLTguNDksMCw0LjE1LDQuMTUsMCwwLDAtLjQ5LS40NGwtMzUuNTEtMjguNDhhNiw2LDAsMCwwLTkuNTYsNi4ybDIyLjg3LDg2LjkzYTcuNjYsNy42NiwwLDAsMCwuMzcsMSw2LDYsMCwwLDEtMTAuODgsNS4wNyw0LjM3LDQuMzcsMCwwLDAtLjI1LS40OEwxMzMuMTksMTY1YTYsNiwwLDAsMC0xMC4zOCwwTDg5LjY5LDIyMi4wNWE0LjM3LDQuMzcsMCwwLDAtLjI1LjQ4LDYsNiwwLDAsMS0xMC44OC01LjA3LDcuNjYsNy42NiwwLDAsMCwuMzctMWwyMi44Ny04Ni45M0E2LDYsMCwwLDAsOTkuMjcsMTIzLDYuMDcsNi4wNywwLDAsMCw5NiwxMjJhNiw2LDAsMCwwLTMuNzYsMS4zMkw1Ni43MywxNTEuOGE0LjE1LDQuMTUsMCwwLDAtLjQ5LjQ0LDYsNiwwLDAsMS04LjQ5LTguNDlsLjI2LS4yN0w5My4zMyw5Mi4wOUExOCwxOCwwLDAsMSwxMDYuODMsODZoNDIuMzRhMTgsMTgsMCwwLDEsMTMuNSw2LjA5TDIwOCwxNDMuNDhsLjI2LjI3QTYsNiwwLDAsMSwyMDguMjUsMTUyLjI0WiIvPjwvc3ZnPg==');}.icon-folder{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTYsNzRIMTMwLjQ5bC0yNy45LTI3LjlhMTMuOTQsMTMuOTQsMCwwLDAtOS45LTQuMUg0MEExNCwxNCwwLDAsMCwyNiw1NlYyMDAuNjJBMTMuMzksMTMuMzksMCwwLDAsMzkuMzgsMjE0SDIxNi44OUExMy4xMiwxMy4xMiwwLDAsMCwyMzAsMjAwLjg5Vjg4QTE0LDE0LDAsMCwwLDIxNiw3NFpNNDAsNTRIOTIuNjlhMiwyLDAsMCwxLDEuNDEuNTlMMTEzLjUxLDc0SDM4VjU2QTIsMiwwLDAsMSw0MCw1NFpNMjE4LDIwMC44OWExLjExLDEuMTEsMCwwLDEtMS4xMSwxLjExSDM5LjM4QTEuNCwxLjQsMCwwLDEsMzgsMjAwLjYyVjg2SDIxNmEyLDIsMCwwLDEsMiwyWiIvPjwvc3ZnPg==');}.icon-dots-six{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik03MCw5MkExMCwxMCwwLDEsMSw2MCw4MiwxMCwxMCwwLDAsMSw3MCw5MlptNTgtMTBhMTAsMTAsMCwxLDAsMTAsMTBBMTAsMTAsMCwwLDAsMTI4LDgyWm02OCwyMGExMCwxMCwwLDEsMC0xMC0xMEExMCwxMCwwLDAsMCwxOTYsMTAyWk02MCwxNTRhMTAsMTAsMCwxLDAsMTAsMTBBMTAsMTAsMCwwLDAsNjAsMTU0Wm02OCwwYTEwLDEwLDAsMSwwLDEwLDEwQTEwLDEwLDAsMCwwLDEyOCwxNTRabTY4LDBhMTAsMTAsMCwxLDAsMTAsMTBBMTAsMTAsMCwwLDAsMTk2LDE1NFoiLz48L3N2Zz4=');}.icon-cake{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzAsMTEyYTIyLDIyLDAsMCwwLTIyLTIySDEzNFY3Ny40QTMwLjA1LDMwLjA1LDAsMCwwLDE1OCw0OGMwLTI2LjktMjUuNTgtNDQuMjctMjYuNjctNDVhNiw2LDAsMCwwLTYuNjYsMEMxMjMuNTgsMy43Myw5OCwyMS4xLDk4LDQ4YTMwLjA1LDMwLjA1LDAsMCwwLDI0LDI5LjRWOTBINDhhMjIsMjIsMCwwLDAtMjIsMjJ2MjMuMzNhMzguODEsMzguODEsMCwwLDAsOCwyMy41N1YyMDBhMjIsMjIsMCwwLDAsMjIsMjJIMjAwYTIyLDIyLDAsMCwwLDIyLTIyVjE1OC45YTM4LjgxLDM4LjgxLDAsMCwwLDgtMjMuNTdaTTExMCw0OGMwLTE1LjQsMTItMjcuNDEsMTgtMzIuNDQsNiw1LDE4LDE3LDE4LDMyLjQ0YTE4LDE4LDAsMCwxLTM2LDBaTTM4LDExMmExMCwxMCwwLDAsMSwxMC0xMEgyMDhhMTAsMTAsMCwwLDEsMTAsMTB2MjMuMzNjMCwxNC4zMi0xMS4zMywyNi4yOC0yNS4yNiwyNi42NkEyNiwyNiwwLDAsMSwxNjYsMTM2YTYsNiwwLDAsMC0xMiwwLDI2LDI2LDAsMCwxLTUyLDAsNiw2LDAsMCwwLTEyLDAsMjYsMjYsMCwwLDEtMjYuNzQsMjZDNDkuMzMsMTYxLjYxLDM4LDE0OS42NSwzOCwxMzUuMzNabTE2Miw5OEg1NmExMCwxMCwwLDAsMS0xMC0xMFYxNjkuMzFBMzYuOCwzNi44LDAsMCwwLDYyLjk0LDE3NCwzNy44MiwzNy44MiwwLDAsMCw5MC41LDE2My4yNCwzOC44MywzOC44MywwLDAsMCw5NiwxNTYuNWEzOCwzOCwwLDAsMCw2NCwwLDM4LjgzLDM4LjgzLDAsMCwwLDUuNTIsNi43NEEzNy43MywzNy43MywwLDAsMCwxOTIsMTc0aDEuMDdBMzYuOCwzNi44LDAsMCwwLDIxMCwxNjkuMzFWMjAwQTEwLDEwLDAsMCwxLDIwMCwyMTBaIi8+PC9zdmc+');}.icon-hourglass{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xOTgsNzUuNjRWNDBhMTQsMTQsMCwwLDAtMTQtMTRINzJBMTQsMTQsMCwwLDAsNTgsNDBWNzZhMTQuMDYsMTQuMDYsMCwwLDAsNS42LDExLjJMMTE4LDEyOCw2My42LDE2OC44QTE0LjA2LDE0LjA2LDAsMCwwLDU4LDE4MHYzNmExNCwxNCwwLDAsMCwxNCwxNEgxODRhMTQsMTQsMCwwLDAsMTQtMTRWMTgwLjM2YTE0LjA4LDE0LjA4LDAsMCwwLTUuNTYtMTEuMTdMMTM4LDEyOGw1NC40OS00MS4xOUExNC4wOCwxNC4wOCwwLDAsMCwxOTgsNzUuNjRaTTE4NiwxODAuMzZWMjE2YTIsMiwwLDAsMS0yLDJINzJhMiwyLDAsMCwxLTItMlYxODBhMiwyLDAsMCwxLC44LTEuNkwxMjgsMTM1LjUxbDU3LjIyLDQzLjI1QTIsMiwwLDAsMSwxODYsMTgwLjM2Wm0wLTEwNC43MmEyLDIsMCwwLDEtLjc5LDEuNkwxMjgsMTIwLjQ5LDcwLjgsNzcuNkEyLDIsMCwwLDEsNzAsNzZWNDBhMiwyLDAsMCwxLDItMkgxODRhMiwyLDAsMCwxLDIsMloiLz48L3N2Zz4=');}.icon-hash{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjQsOTBIMTczbDguODktNDguOTNhNiw2LDAsMSwwLTExLjgtMi4xNEwxNjAuODEsOTBIMTA5bDguODktNDguOTNhNiw2LDAsMCwwLTExLjgtMi4xNEw5Ni44MSw5MEg0OGE2LDYsMCwwLDAsMCwxMkg5NC42M2wtOS40Niw1MkgzMmE2LDYsMCwwLDAsMCwxMkg4M0w3NC4xLDIxNC45M2E2LDYsMCwwLDAsNC44Myw3QTUuNjQsNS42NCwwLDAsMCw4MCwyMjJhNiw2LDAsMCwwLDUuODktNC45M0w5NS4xOSwxNjZIMTQ3bC04Ljg5LDQ4LjkzYTYsNiwwLDAsMCw0LjgzLDcsNS42NCw1LjY0LDAsMCwwLDEuMDguMSw2LDYsMCwwLDAsNS44OS00LjkzTDE1OS4xOSwxNjZIMjA4YTYsNiwwLDAsMCwwLTEySDE2MS4zN2w5LjQ2LTUySDIyNGE2LDYsMCwwLDAsMC0xMlptLTc0LjgzLDY0SDk3LjM3bDkuNDYtNTJoNTEuOFoiLz48L3N2Zz4=');}.icon-pencil-simple{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjUuOSw3NC43OCwxODEuMjEsMzAuMDlhMTQsMTQsMCwwLDAtMTkuOCwwTDM4LjEsMTUzLjQxYTEzLjk0LDEzLjk0LDAsMCwwLTQuMSw5LjlWMjA4YTE0LDE0LDAsMCwwLDE0LDE0SDkyLjY5YTEzLjk0LDEzLjk0LDAsMCwwLDkuOS00LjFMMjI1LjksOTQuNThhMTQsMTQsMCwwLDAsMC0xOS44Wk05NC4xLDIwOS40MWEyLDIsMCwwLDEtMS40MS41OUg0OGEyLDIsMCwwLDEtMi0yVjE2My4zMWEyLDIsMCwwLDEsLjU5LTEuNDFMMTM2LDcyLjQ4LDE4My41MSwxMjBaTTIxNy40MSw4Ni4xLDE5MiwxMTEuNTEsMTQ0LjQ5LDY0LDE2OS45LDM4LjU4YTIsMiwwLDAsMSwyLjgzLDBsNDQuNjgsNDQuNjlhMiwyLDAsMCwxLDAsMi44M1oiLz48L3N2Zz4=');}.icon-caret-double-down{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTIuMjQsMTMxLjc2YTYsNiwwLDAsMSwwLDguNDhsLTgwLDgwYTYsNiwwLDAsMS04LjQ4LDBsLTgwLTgwYTYsNiwwLDAsMSw4LjQ4LTguNDhMMTI4LDIwNy41MWw3NS43Ni03NS43NUE2LDYsMCwwLDEsMjEyLjI0LDEzMS43NlptLTg4LjQ4LDguNDhhNiw2LDAsMCwwLDguNDgsMGw4MC04MGE2LDYsMCwwLDAtOC40OC04LjQ4TDEyOCwxMjcuNTEsNTIuMjQsNTEuNzZhNiw2LDAsMCwwLTguNDgsOC40OFoiLz48L3N2Zz4=');}.icon-caret-double-right{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xNDAuMjQsMTMyLjI0bC04MCw4MGE2LDYsMCwwLDEtOC40OC04LjQ4TDEyNy41MSwxMjgsNTEuNzYsNTIuMjRhNiw2LDAsMCwxLDguNDgtOC40OGw4MCw4MEE2LDYsMCwwLDEsMTQwLjI0LDEzMi4yNFptODAtOC40OC04MC04MGE2LDYsMCwwLDAtOC40OCw4LjQ4TDIwNy41MSwxMjhsLTc1Ljc1LDc1Ljc2YTYsNiwwLDEsMCw4LjQ4LDguNDhsODAtODBBNiw2LDAsMCwwLDIyMC4yNCwxMjMuNzZaIi8+PC9zdmc+');}.icon-dots-six-vertical{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMDIsNjBBMTAsMTAsMCwxLDEsOTIsNTAsMTAsMTAsMCwwLDEsMTAyLDYwWm02MiwxMGExMCwxMCwwLDEsMC0xMC0xMEExMCwxMCwwLDAsMCwxNjQsNzBaTTkyLDExOGExMCwxMCwwLDEsMCwxMCwxMEExMCwxMCwwLDAsMCw5MiwxMThabTcyLDBhMTAsMTAsMCwxLDAsMTAsMTBBMTAsMTAsMCwwLDAsMTY0LDExOFpNOTIsMTg2YTEwLDEwLDAsMSwwLDEwLDEwQTEwLDEwLDAsMCwwLDkyLDE4NlptNzIsMGExMCwxMCwwLDEsMCwxMCwxMEExMCwxMCwwLDAsMCwxNjQsMTg2WiIvPjwvc3ZnPg==');}.icon-drop-simple{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xNzIuNTMsNDkuMDZhMjUxLjQyLDI1MS40MiwwLDAsMC00MS4wOS0zOCw2LDYsMCwwLDAtNi44OCwwLDI1MS40MiwyNTEuNDIsMCwwLDAtNDEuMDksMzhDNTYuMzQsODAuMjYsNDIsMTEzLjA5LDQyLDE0NGE4Niw4NiwwLDAsMCwxNzIsMEMyMTQsMTEzLjA5LDE5OS42Niw4MC4yNiwxNzIuNTMsNDkuMDZaTTEyOCwyMThhNzQuMDksNzQuMDksMCwwLDEtNzQtNzRjMC01OS42Miw1OS0xMDguOTMsNzQtMTIwLjUxQzE0MywzNS4wNywyMDIsODQuMzgsMjAyLDE0NEE3NC4wOSw3NC4wOSwwLDAsMSwxMjgsMjE4WiIvPjwvc3ZnPg==');}.icon-door{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzIsMjE4SDIwNlY0MGExNCwxNCwwLDAsMC0xNC0xNEg2NEExNCwxNCwwLDAsMCw1MCw0MFYyMThIMjRhNiw2LDAsMCwwLDAsMTJIMjMyYTYsNiwwLDAsMCwwLTEyWk02Miw0MGEyLDIsMCwwLDEsMi0ySDE5MmEyLDIsMCwwLDEsMiwyVjIxOEg2MlptMTA0LDkyYTEwLDEwLDAsMSwxLTEwLTEwQTEwLDEwLDAsMCwxLDE2NiwxMzJaIi8+PC9zdmc+');}.icon-hand-heart{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjkuMTIsMTQyLjY1YTIyLjQzLDIyLjQzLDAsMCwwLTE5LjU1LTMuODhsLTQuMzIsMUMyMjcsMTE5LjU1LDIzOCw5OS41MSwyMzgsODBjMC0yNS4zNi0yMC4zOS00Ni00NS40Ni00NkE0NS41MSw0NS41MSwwLDAsMCwxNTYsNTJhNDUuNTEsNDUuNTEsMCwwLDAtMzYuNTQtMThDOTQuMzksMzQsNzQsNTQuNjQsNzQsODBjMCwxMS4zOCwzLjYzLDIyLjQ5LDExLjI5LDM0LjM2YTI5LjczLDI5LjczLDAsMCwwLTE2LjU2LDguNDNMNDUuNTIsMTQ2SDE2QTE0LDE0LDAsMCwwLDIsMTYwdjQwYTE0LDE0LDAsMCwwLDE0LDE0SDEyMGE2LDYsMCwwLDAsMS40Ni0uMThsNjQtMTZhNy4xNiw3LjE2LDAsMCwwLC44OS0uM0wyMjUuMTcsMTgxbC4zMy0uMTVhMjIuNiwyMi42LDAsMCwwLDMuNjItMzguMThaTTExOS40Niw0NmEzMy4xNiwzMy4xNiwwLDAsMSwzMSwyMC4yOCw2LDYsMCwwLDAsMTEuMSwwLDMzLjE2LDMzLjE2LDAsMCwxLDMxLTIwLjI4QzIxMC42OCw0NiwyMjYsNjEuNTcsMjI2LDgwYzAsMjAuMjQtMTYuMTgsNDMtNDYuOCw2NS43NWwtMTQuODcsMy40MkEyNiwyNiwwLDAsMCwxNDAsMTE0SDk5LjY3QzkwLjM2LDEwMS42Nyw4Niw5MC44MSw4Niw4MCw4Niw2MS41NywxMDEuMzIsNDYsMTE5LjQ2LDQ2Wk0xNCwyMDBWMTYwYTIsMiwwLDAsMSwyLTJINDJ2NDRIMTZBMiwyLDAsMCwxLDE0LDIwMFptMjA2LjI4LTMwLTM4LjIsMTYuMjdMMTE5LjI2LDIwMkg1NFYxNTQuNDlsMjMuMjEtMjMuMjJBMTcuODgsMTcuODgsMCwwLDEsODkuOTQsMTI2SDE0MGExNCwxNCwwLDAsMSwwLDI4SDExMmE2LDYsMCwwLDAsMCwxMmgzMmE2LDYsMCwwLDAsMS4zNC0uMTVsNjctMTUuNDEuMjQtLjA2QTEwLjYsMTAuNiwwLDAsMSwyMjAuMjgsMTcwWiIvPjwvc3ZnPg==');}.icon-plugs-connected{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzYuMjQsMTkuNzZhNiw2LDAsMCwwLTguNDgsMEwxNzMuOTQsNzMuNTdsLTYuNzktNi43OGEzMCwzMCwwLDAsMC00Mi40MiwwTDEwMCw5MS41MWwtNy43Ni03Ljc1YTYsNiwwLDAsMC04LjQ4LDguNDhMOTEuNTEsMTAwLDY2Ljc5LDEyNC43M2EzMCwzMCwwLDAsMCwwLDQyLjQybDYuNzgsNi43OUwxOS43NiwyMjcuNzZhNiw2LDAsMSwwLDguNDgsOC40OGw1My44Mi01My44MSw2Ljc5LDYuNzhhMzAsMzAsMCwwLDAsNDIuNDIsMEwxNTYsMTY0LjQ5bDcuNzYsNy43NWE2LDYsMCwwLDAsOC40OC04LjQ4TDE2NC40OSwxNTZsMjQuNzItMjQuNzNhMzAsMzAsMCwwLDAsMC00Mi40MmwtNi43OC02Ljc5LDUzLjgxLTUzLjgyQTYsNiwwLDAsMCwyMzYuMjQsMTkuNzZabS0xMTMuNDUsMTYxYTE4LDE4LDAsMCwxLTI1LjQ2LDBMNzUuMjcsMTU4LjY3YTE4LDE4LDAsMCwxLDAtMjUuNDZMMTAwLDEwOC40OSwxNDcuNTEsMTU2Wm01Ny45NC01Ny45NEwxNTYsMTQ3LjUxLDEwOC40OSwxMDBsMjQuNzItMjQuNzNhMTgsMTgsMCwwLDEsMjUuNDYsMGwyMi4wNiwyMi4wNmExOCwxOCwwLDAsMSwwLDI1LjQ2Wk05MC40MywzNC4yM2E2LDYsMCwwLDEsMTEuMTQtNC40Nmw4LDIwYTYsNiwwLDEsMS0xMS4xNCw0LjQ2Wm0tNjQsNTkuNTRhNiw2LDAsMCwxLDcuOC0zLjM0bDIwLDhhNiw2LDAsMSwxLTQuNDYsMTEuMTRsLTIwLThBNiw2LDAsMCwxLDI2LjQzLDkzLjc3Wm0yMDMuMTQsNjguNDZhNiw2LDAsMCwxLTcuOCwzLjM0bC0yMC04YTYsNiwwLDAsMSw0LjQ2LTExLjE0bDIwLDhBNiw2LDAsMCwxLDIyOS41NywxNjIuMjNabS02NCw1OS41NGE2LDYsMCwxLDEtMTEuMTQsNC40NmwtOC0yMGE2LDYsMCwwLDEsMTEuMTQtNC40NloiLz48L3N2Zz4=');}.icon-question{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMzgsMTgwYTEwLDEwLDAsMSwxLTEwLTEwQTEwLDEwLDAsMCwxLDEzOCwxODBaTTEyOCw3NGMtMjEsMC0zOCwxNS4yNS0zOCwzNHY0YTYsNiwwLDAsMCwxMiwwdi00YzAtMTIuMTMsMTEuNjYtMjIsMjYtMjJzMjYsOS44NywyNiwyMi0xMS42NiwyMi0yNiwyMmE2LDYsMCwwLDAtNiw2djhhNiw2LDAsMCwwLDEyLDB2LTIuNDJjMTguMTEtMi41OCwzMi0xNi42NiwzMi0zMy41OEMxNjYsODkuMjUsMTQ5LDc0LDEyOCw3NFptMTAyLDU0QTEwMiwxMDIsMCwxLDEsMTI4LDI2LDEwMi4xMiwxMDIuMTIsMCwwLDEsMjMwLDEyOFptLTEyLDBhOTAsOTAsMCwxLDAtOTAsOTBBOTAuMSw5MC4xLDAsMCwwLDIxOCwxMjhaIi8+PC9zdmc+');}.icon-asterisk{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTMuMTQsMTc5LjA5YTYsNiwwLDAsMS04LjIzLDIuMDZMMTM0LDEzOC42VjIxNmE2LDYsMCwwLDEtMTIsMFYxMzguNkw1MS4wOSwxODEuMTVBNi4wNyw2LjA3LDAsMCwxLDQ4LDE4MmE2LDYsMCwwLDEtMy4xLTExLjE1TDExNi4zNCwxMjgsNDQuOTEsODUuMTVhNiw2LDAsMCwxLDYuMTgtMTAuM0wxMjIsMTE3LjRWNDBhNiw2LDAsMCwxLDEyLDB2NzcuNGw3MC45MS00Mi41NWE2LDYsMCwwLDEsNi4xOCwxMC4zTDEzOS42NiwxMjhsNzEuNDMsNDIuODVBNiw2LDAsMCwxLDIxMy4xNCwxNzkuMDlaIi8+PC9zdmc+');}.icon-arrow-counter-clockwise{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjIsMTI4YTk0LDk0LDAsMCwxLTkyLjc0LDk0SDEyOGE5My40Myw5My40MywwLDAsMS02NC41LTI1LjY1LDYsNiwwLDEsMSw4LjI0LTguNzJBODIsODIsMCwxLDAsNzAsNzBsLS4xOS4xOUwzOS40NCw5OEg3MmE2LDYsMCwwLDEsMCwxMkgyNGE2LDYsMCwwLDEtNi02VjU2YTYsNiwwLDAsMSwxMiwwVjkwLjM0TDYxLjYzLDYxLjRBOTQsOTQsMCwwLDEsMjIyLDEyOFoiLz48L3N2Zz4=');}.icon-clock{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMjgsMjZBMTAyLDEwMiwwLDEsMCwyMzAsMTI4LDEwMi4xMiwxMDIuMTIsMCwwLDAsMTI4LDI2Wm0wLDE5MmE5MCw5MCwwLDEsMSw5MC05MEE5MC4xLDkwLjEsMCwwLDEsMTI4LDIxOFptNjItOTBhNiw2LDAsMCwxLTYsNkgxMjhhNiw2LDAsMCwxLTYtNlY3MmE2LDYsMCwwLDEsMTIsMHY1MGg1MEE2LDYsMCwwLDEsMTkwLDEyOFoiLz48L3N2Zz4=');}.icon-envelope{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjQsNTBIMzJhNiw2LDAsMCwwLTYsNlYxOTJhMTQsMTQsMCwwLDAsMTQsMTRIMjE2YTE0LDE0LDAsMCwwLDE0LTE0VjU2QTYsNiwwLDAsMCwyMjQsNTBabS05Niw4NS44Nkw0Ny40Miw2MkgyMDguNThaTTEwMS42NywxMjgsMzgsMTg2LjM2VjY5LjY0Wm04Ljg4LDguMTRMMTI0LDE0OC40MmE2LDYsMCwwLDAsOC4xLDBsMTMuNC0xMi4yOEwyMDguNTgsMTk0SDQ3LjQzWk0xNTQuMzMsMTI4LDIxOCw2OS42NFYxODYuMzZaIi8+PC9zdmc+');}.icon-facebook-logo{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMjgsMjZBMTAyLDEwMiwwLDEsMCwyMzAsMTI4LDEwMi4xMiwxMDIuMTIsMCwwLDAsMTI4LDI2Wm02LDE5MS44VjE1MGgyNmE2LDYsMCwwLDAsMC0xMkgxMzRWMTEyYTE4LDE4LDAsMCwxLDE4LTE4aDE2YTYsNiwwLDAsMCwwLTEySDE1MmEzMCwzMCwwLDAsMC0zMCwzMHYyNkg5NmE2LDYsMCwwLDAsMCwxMmgyNnY2Ny44YTkwLDkwLDAsMSwxLDEyLDBaIi8+PC9zdmc+');}.icon-twitter-logo{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yNDUuNTQsNjkuNzFBNiw2LDAsMCwwLDI0MCw2NkgyMDguNGE0Ni42LDQ2LjYsMCwwLDAtNDAuMzMtMjQsNDQuOTMsNDQuOTMsMCwwLDAtMzIuMzEsMTMuMTJBNDUuOTIsNDUuOTIsMCwwLDAsMTIyLDg4djguNjZjLTQyLTEwLTc2LjYtNDQuNTItNzctNDQuODhBNiw2LDAsMCwwLDM0LjgsNTUuNDZjLTQuMjUsNDcuMjIsOS40Miw3OC43NSwyMS42NCw5Ni44OWExMDcuNzEsMTA3LjcxLDAsMCwwLDIzLjA3LDI1Yy0xNS40OSwxOS00MS4zNCwyOC44OS00MS42MiwyOWE2LDYsMCwwLDAtMi44OCw5QzM2LDIxNi44Myw0NS43OCwyMzAsODAsMjMwYzY5Ljg4LDAsMTI4LjIzLTU0LDEzMy44Mi0xMjMuMzRsMzAuNDItMzAuNDFBNiw2LDAsMCwwLDI0NS41NCw2OS43MVpNMjAzLjc2LDk5Ljc2YTYsNiwwLDAsMC0xLjc1LDMuODZDMTk3LjkzLDE2Ny43NiwxNDQuMzMsMjE4LDgwLDIxOGMtMTQsMC0yMi43Ni0yLjQxLTI4LjA2LTQuOCwxMS4zLTUuNjgsMjkuNzItMTYuODgsNDEuMDUtMzMuODdhNiw2LDAsMCwwLC44NS00LjY3QTYsNiwwLDAsMCw5MSwxNzAuODJjLS4xMy0uMDgtMTMuMTMtNy44Ni0yNS0yNS43MkM1MiwxMjQsNDUuMzEsOTguNDMsNDYsNjguODhjMTQuODgsMTIuNiw0NS41NywzNS4wOSw4MSw0MWE2LDYsMCwwLDAsNy01LjkyVjg4YTM0LDM0LDAsMCwxLDEwLjE5LTI0LjM0QTMzLjA1LDMzLjA1LDAsMCwxLDE2Ny45Myw1NGMxMy40My4xNywyNiw4LjM3LDMxLjI0LDIwLjRhNiw2LDAsMCwwLDUuNSwzLjZoMjAuODRaIi8+PC9zdmc+');}.icon-caret-down{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTIuMjQsMTAwLjI0bC04MCw4MGE2LDYsMCwwLDEtOC40OCwwbC04MC04MGE2LDYsMCwwLDEsOC40OC04LjQ4TDEyOCwxNjcuNTFsNzUuNzYtNzUuNzVhNiw2LDAsMCwxLDguNDgsOC40OFoiLz48L3N2Zz4=');}.icon-list{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjIsMTI4YTYsNiwwLDAsMS02LDZINDBhNiw2LDAsMCwxLDAtMTJIMjE2QTYsNiwwLDAsMSwyMjIsMTI4Wk00MCw3MEgyMTZhNiw2LDAsMCwwLDAtMTJINDBhNiw2LDAsMCwwLDAsMTJaTTIxNiwxODZINDBhNiw2LDAsMCwwLDAsMTJIMjE2YTYsNiwwLDAsMCwwLTEyWiIvPjwvc3ZnPg==');}.icon-instagram-logo{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMjgsODJhNDYsNDYsMCwxLDAsNDYsNDZBNDYuMDYsNDYuMDYsMCwwLDAsMTI4LDgyWm0wLDgwYTM0LDM0LDAsMSwxLDM0LTM0QTM0LDM0LDAsMCwxLDEyOCwxNjJaTTE3NiwyNkg4MEE1NC4wNiw1NC4wNiwwLDAsMCwyNiw4MHY5NmE1NC4wNiw1NC4wNiwwLDAsMCw1NCw1NGg5NmE1NC4wNiw1NC4wNiwwLDAsMCw1NC01NFY4MEE1NC4wNiw1NC4wNiwwLDAsMCwxNzYsMjZabTQyLDE1MGE0Miw0MiwwLDAsMS00Miw0Mkg4MGE0Miw0MiwwLDAsMS00Mi00MlY4MEE0Miw0MiwwLDAsMSw4MCwzOGg5NmE0Miw0MiwwLDAsMSw0Miw0MlpNMTkwLDc2YTEwLDEwLDAsMSwxLTEwLTEwQTEwLDEwLDAsMCwxLDE5MCw3NloiLz48L3N2Zz4=');}.icon-butterfly{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzEuMSw1MS43MUMyMjYuMDksNDUuMjcsMjE4LjY0LDQyLDIwOSw0MmMtMTYuMzMsMC0zNy40MSwxMS4wNi01Ni40LDI5LjU5QTEzMiwxMzIsMCwwLDAsMTM0LDkzLjk0VjU2YTYsNiwwLDAsMC0xMiwwVjkzLjk0YTEzMiwxMzIsMCwwLDAtMTguNTQtMjIuMzVDODQuNDksNTMuMDYsNjMuNCw0Miw0Ny4wNyw0MmMtOS42OSwwLTE3LjE0LDMuMjctMjIuMTUsOS43MS01LjUzLDcuMTEtNy43MSwxNy42OS02LjY2LDMyLjM0LjkxLDEyLjczLDQuMTIsMjYuNTMsNi44MSwzNy4xMyw2LjI4LDI0Ljc0LDIwLjc3LDMzLDMxLjc4LDM1LjY4QTQyLDQyLDAsMSwwLDEyOCwyMDEuNjJhNDIsNDIsMCwxLDAsNzEuMTYtNDQuNzZjMTEtMi42MywyNS41LTEwLjk0LDMxLjc4LTM1LjY4QzIzNy41NSw5NS4yMSwyNDIuNzIsNjYuNjUsMjMxLjEsNTEuNzFaTTkyLDIxMGEzMC4xMiwzMC4xMiwwLDAsMS0zLjM0LTYwQTYsNiwwLDAsMCw4Ny4zNSwxMzhhNDEuNzEsNDEuNzEsMCwwLDAtMjAuMjgsOGMtOSwuMzEtMjQuMTItMy4xNi0zMC4zNy0yNy43Ni0zLjI1LTEyLjgxLTExLjg5LTQ2LjgzLTIuMzEtNTkuMTVDMzcuMDUsNTUuNjYsNDEuMiw1NCw0Ny4wNyw1NGMxMi44OCwwLDMxLjcyLDEwLjI4LDQ4LDI2LjE4QzExMS42OSw5Ni4zOSwxMjIsMTE0LjU5LDEyMiwxMjcuNjdWMTgwQTMwLDMwLDAsMCwxLDkyLDIxMFptMTI3LjMxLTkxLjc3QzIxMy4wNywxNDIuODMsMTk4LDE0Ni4yOSwxODksMTQ2YTQxLjYyLDQxLjYyLDAsMCwwLTIwLjI4LThBNiw2LDAsMSwwLDE2Ny4zNiwxNTAsMzAuMTEsMzAuMTEsMCwxLDEsMTM0LDE4MFYxMjcuNjdjMC0xMy4wOCwxMC4zMi0zMS4yOCwyNi45My00Ny40OUMxNzcuMjMsNjQuMjgsMTk2LjA3LDU0LDIwOSw1NGM1Ljg3LDAsMTAsMS42NiwxMi42OCw1LjA4QzIzMS4yMSw3MS40LDIyMi41NywxMDUuNDIsMjE5LjMyLDExOC4yM1oiLz48L3N2Zz4=');}.icon-phone{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjEuNTksMTYwLjNsLTQ3LjI0LTIxLjE3YTE0LDE0LDAsMCwwLTEzLjI4LDEuMjIsNC44MSw0LjgxLDAsMCwwLS41Ni40MmwtMjQuNjksMjFhMS44OCwxLjg4LDAsMCwxLTEuNjguMDZjLTE1Ljg3LTcuNjYtMzIuMzEtMjQtNDAtMzkuNjVhMS45MSwxLjkxLDAsMCwxLDAtMS42OGwyMS4wNy0yNWE2LjEzLDYuMTMsMCwwLDAsLjQyLS41OCwxNCwxNCwwLDAsMCwxLjEyLTEzLjI3TDk1LjczLDM0LjQ5YTE0LDE0LDAsMCwwLTE0LjU2LTguMzhBNTQuMjQsNTQuMjQsMCwwLDAsMzQsODBjMCw3OC4zLDYzLjcsMTQyLDE0MiwxNDJhNTQuMjUsNTQuMjUsMCwwLDAsNTMuODktNDcuMTdBMTQsMTQsMCwwLDAsMjIxLjU5LDE2MC4zWk0xNzYsMjEwQzEwNC4zMiwyMTAsNDYsMTUxLjY4LDQ2LDgwQTQyLjIzLDQyLjIzLDAsMCwxLDgyLjY3LDM4aC4yM2EyLDIsMCwwLDEsMS44NCwxLjMxbDIxLjEsNDcuMTFhMiwyLDAsMCwxLDAsMS42N0w4NC43MywxMTMuMTVhNC43Myw0LjczLDAsMCwwLS40My41NywxNCwxNCwwLDAsMC0uOTEsMTMuNzNjOC44NywxOC4xNiwyNy4xNywzNi4zMiw0NS41Myw0NS4xOWExNCwxNCwwLDAsMCwxMy43Ny0xYy4xOS0uMTMuMzgtLjI3LjU2LS40MmwyNC42OC0yMWExLjkyLDEuOTIsMCwwLDEsMS42LS4xbDQ3LjI1LDIxLjE3YTIsMiwwLDAsMSwxLjIxLDJBNDIuMjQsNDIuMjQsMCwwLDEsMTc2LDIxMFoiLz48L3N2Zz4=');}.icon-chat{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTYsNTBINDBBMTQsMTQsMCwwLDAsMjYsNjRWMjI0YTEzLjg4LDEzLjg4LDAsMCwwLDguMDksMTIuNjlBMTQuMTEsMTQuMTEsMCwwLDAsNDAsMjM4YTEzLjg3LDEzLjg3LDAsMCwwLDktMy4zMWwuMDYtLjA1TDgyLjIzLDIwNkgyMTZhMTQsMTQsMCwwLDAsMTQtMTRWNjRBMTQsMTQsMCwwLDAsMjE2LDUwWm0yLDE0MmEyLDIsMCwwLDEtMiwySDgwYTYsNiwwLDAsMC0zLjkyLDEuNDZMNDEuMjYsMjI1LjUzQTIsMiwwLDAsMSwzOCwyMjRWNjRhMiwyLDAsMCwxLDItMkgyMTZhMiwyLDAsMCwxLDIsMloiLz48L3N2Zz4=');}.icon-google-logo{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjIsMTI4YTk0LDk0LDAsMSwxLTIxLjQ5LTU5LjgyLDYsNiwwLDEsMS05LjI1LDcuNjRBODIsODIsMCwxLDAsMjA5Ljc4LDEzNEgxMjhhNiw2LDAsMCwxLDAtMTJoODhBNiw2LDAsMCwxLDIyMiwxMjhaIi8+PC9zdmc+');}.icon-apple-logo{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTkuNCwxNjcuODRDMjAxLjcxLDE1NS42OSwxOTgsMTM1LjEyLDE5OCwxMjBjMC0xOC40MiwxMy44Ni0zNC4yOSwyMi4xMi00Mi4xMmE2LDYsMCwwLDAsMC04LjcxQzIwOCw1Ny43LDE4Ny4wNyw1MCwxNjgsNTBhNzAuMjMsNzAuMjMsMCwwLDAtNDAsMTIuNTUsNjkuNiw2OS42LDAsMCwwLTg5LjMxLDguMDhBNzIuNjMsNzIuNjMsMCwwLDAsMTgsMTIzLjM1YTEyNS4xMSwxMjUuMTEsMCwwLDAsMzkuNTMsODguMzNBMzcuODUsMzcuODUsMCwwLDAsODMuNiwyMjJoODcuN0EzNy44MywzNy44MywwLDAsMCwxOTksMjEwLjA3YTEyMi42LDEyMi42LDAsMCwwLDE3LjU0LTI0LjJjNi41NS0xMiw1Ljc3LTEzLjc1LDUtMTUuNDhBNi4wNyw2LjA3LDAsMCwwLDIxOS40LDE2Ny44NFptLTI5LjIzLDM0QTI1LjgyLDI1LjgyLDAsMCwxLDE3MS4zLDIxMEg4My42QTI1Ljg1LDI1Ljg1LDAsMCwxLDY1Ljc4LDIwMywxMTMuMjEsMTEzLjIxLDAsMCwxLDMwLDEyM2E2MC41NSw2MC41NSwwLDAsMSwxNy4yMS00NEE1Ni44Miw1Ni44MiwwLDAsMSw4OCw2MmguODFhNTcuMzUsNTcuMzUsMCwwLDEsMzUuNDQsMTIuNzEsNiw2LDAsMCwwLDcuNSwwQTU3LjM5LDU3LjM5LDAsMCwxLDE2OCw2MmMxMy44OSwwLDI4LjgxLDQuNjgsMzkuMTEsMTItOS40NCwxMC4xNC0yMS4xLDI2LjU5LTIxLjEsNDYsMCwyMy43OCw3LjgxLDQyLjYsMjIuNjYsNTQuNzdBMTA3LjMzLDEwNy4zMywwLDAsMSwxOTAuMTcsMjAxLjg5Wm0tNjAtMTcxLjM5QTM4LDM4LDAsMCwxLDE2NywyaDFhNiw2LDAsMCwxLDAsMTJoLTFhMjYsMjYsMCwwLDAtMjUuMTgsMTkuNSw2LDYsMCwxLDEtMTEuNjItM1oiLz48L3N2Zz4=');}.icon-tiktok-logo{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjQsNzRhNTAuMDYsNTAuMDYsMCwwLDEtNTAtNTAsNiw2LDAsMCwwLTYtNkgxMjhhNiw2LDAsMCwwLTYsNlYxNTZhMjIsMjIsMCwxLDEtMzEuNDMtMTkuODlBNiw2LDAsMCwwLDk0LDEzMC42OVY4OGE2LDYsMCwwLDAtNy01LjkxQzUyLjIsODguMjgsMjYsMTIwLjA1LDI2LDE1NmE3NCw3NCwwLDAsMCwxNDgsMFYxMTIuOTNBMTAxLjI4LDEwMS4yOCwwLDAsMCwyMjQsMTI2YTYsNiwwLDAsMCw2LTZWODBBNiw2LDAsMCwwLDIyNCw3NFptLTYsMzkuOGE4OS4xMyw4OS4xMywwLDAsMS00Ni41LTE2LjY5QTYsNiwwLDAsMCwxNjIsMTAydjU0YTYyLDYyLDAsMCwxLTEyNCwwYzAtMjcuNzIsMTguNDctNTIuNDgsNDQtNjAuMzh2MzEuNTNBMzQsMzQsMCwxLDAsMTM0LDE1NlYzMGgyOC4yOUE2Mi4wOSw2Mi4wOSwwLDAsMCwyMTgsODUuNzFaIi8+PC9zdmc+');}.icon-link{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzgsODguMThhNTIuNDIsNTIuNDIsMCwwLDEtMTUuNCwzNS42NmwtMzQuNzUsMzQuNzVBNTIuMjgsNTIuMjgsMCwwLDEsMTUwLjYyLDE3NGgtLjA1QTUyLjYzLDUyLjYzLDAsMCwxLDk4LDExOS45YTYsNiwwLDAsMSw2LTUuODRoLjE3YTYsNiwwLDAsMSw1LjgzLDYuMTZBNDAuNjIsNDAuNjIsMCwwLDAsMTUwLjU4LDE2MmgwYTQwLjQsNDAuNCwwLDAsMCwyOC43My0xMS45bDM0Ljc1LTM0Ljc0QTQwLjYzLDQwLjYzLDAsMCwwLDE1Ni42Myw1Ny45bC0xMSwxMWE2LDYsMCwwLDEtOC40OS04LjQ5bDExLTExYTUyLjYyLDUyLjYyLDAsMCwxLDc0LjQzLDBBNTIuODMsNTIuODMsMCwwLDEsMjM4LDg4LjE4Wm0tMTI3LjYyLDk4LjktMTEsMTFBNDAuMzYsNDAuMzYsMCwwLDEsNzAuNiwyMTBoMGE0MC42Myw0MC42MywwLDAsMS0yOC43LTY5LjM2TDc2LjYyLDEwNS45QTQwLjYzLDQwLjYzLDAsMCwxLDE0NiwxMzUuNzdhNiw2LDAsMCwwLDUuODMsNi4xNkgxNTJhNiw2LDAsMCwwLDYtNS44NEE1Mi42Myw1Mi42MywwLDAsMCw2OC4xNCw5Ny40MkwzMy4zOCwxMzIuMTZBNTIuNjMsNTIuNjMsMCwwLDAsNzAuNTYsMjIyaDBhNTIuMjYsNTIuMjYsMCwwLDAsMzcuMjItMTUuNDJsMTEtMTFhNiw2LDAsMSwwLTguNDktOC40OFoiLz48L3N2Zz4=');}.icon-star-fi{--icon:url('data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzQuMjksMTE0Ljg1bC00NSwzOC44M0wyMDMsMjExLjc1YTE2LjQsMTYuNCwwLDAsMS0yNC41LDE3LjgyTDEyOCwxOTguNDksNzcuNDcsMjI5LjU3QTE2LjQsMTYuNCwwLDAsMSw1MywyMTEuNzVsMTMuNzYtNTguMDctNDUtMzguODNBMTYuNDYsMTYuNDYsMCwwLDEsMzEuMDgsODZsNTktNC43NiwyMi43Ni01NS4wOGExNi4zNiwxNi4zNiwwLDAsMSwzMC4yNywwbDIyLjc1LDU1LjA4LDU5LDQuNzZhMTYuNDYsMTYuNDYsMCwwLDEsOS4zNywyOC44NloiLz48L3N2Zz4=');}
\ No newline at end of file
diff --git a/assets/css/icons.min.css b/assets/css/icons.min.css
new file mode 100644
index 0000000..5d3487a
--- /dev/null
+++ b/assets/css/icons.min.css
@@ -0,0 +1 @@
+.icon-align-left{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik00Niw0MFYyMTZhNiw2LDAsMCwxLTEyLDBWNDBhNiw2LDAsMCwxLDEyLDBabTIwLDY0VjY0QTE0LDE0LDAsMCwxLDgwLDUwaDk2YTE0LDE0LDAsMCwxLDE0LDE0djQwYTE0LDE0LDAsMCwxLTE0LDE0SDgwQTE0LDE0LDAsMCwxLDY2LDEwNFptMTIsMGEyLDIsMCwwLDAsMiwyaDk2YTIsMiwwLDAsMCwyLTJWNjRhMiwyLDAsMCwwLTItMkg4MGEyLDIsMCwwLDAtMiwyWm0xNTIsNDh2NDBhMTQsMTQsMCwwLDEtMTQsMTRIODBhMTQsMTQsMCwwLDEtMTQtMTRWMTUyYTE0LDE0LDAsMCwxLDE0LTE0SDIxNkExNCwxNCwwLDAsMSwyMzAsMTUyWm0tMTIsMGEyLDIsMCwwLDAtMi0ySDgwYTIsMiwwLDAsMC0yLDJ2NDBhMiwyLDAsMCwwLDIsMkgyMTZhMiwyLDAsMCwwLDItMloiLz48L3N2Zz4=);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik00Niw0MFYyMTZhNiw2LDAsMCwxLTEyLDBWNDBhNiw2LDAsMCwxLDEyLDBabTIwLDY0VjY0QTE0LDE0LDAsMCwxLDgwLDUwaDk2YTE0LDE0LDAsMCwxLDE0LDE0djQwYTE0LDE0LDAsMCwxLTE0LDE0SDgwQTE0LDE0LDAsMCwxLDY2LDEwNFptMTIsMGEyLDIsMCwwLDAsMiwyaDk2YTIsMiwwLDAsMCwyLTJWNjRhMiwyLDAsMCwwLTItMkg4MGEyLDIsMCwwLDAtMiwyWm0xNTIsNDh2NDBhMTQsMTQsMCwwLDEtMTQsMTRIODBhMTQsMTQsMCwwLDEtMTQtMTRWMTUyYTE0LDE0LDAsMCwxLDE0LTE0SDIxNkExNCwxNCwwLDAsMSwyMzAsMTUyWm0tMTIsMGEyLDIsMCwwLDAtMi0ySDgwYTIsMiwwLDAsMC0yLDJ2NDBhMiwyLDAsMCwwLDIsMkgyMTZhMiwyLDAsMCwwLDItMloiLz48L3N2Zz4=)}.icon-align-right{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjIsNDBWMjE2YTYsNiwwLDAsMS0xMiwwVjQwYTYsNiwwLDAsMSwxMiwwWk0xOTAsNjR2NDBhMTQsMTQsMCwwLDEtMTQsMTRIODBhMTQsMTQsMCwwLDEtMTQtMTRWNjRBMTQsMTQsMCwwLDEsODAsNTBoOTZBMTQsMTQsMCwwLDEsMTkwLDY0Wm0tMTIsMGEyLDIsMCwwLDAtMi0ySDgwYTIsMiwwLDAsMC0yLDJ2NDBhMiwyLDAsMCwwLDIsMmg5NmEyLDIsMCwwLDAsMi0yWm0xMiw4OHY0MGExNCwxNCwwLDAsMS0xNCwxNEg0MGExNCwxNCwwLDAsMS0xNC0xNFYxNTJhMTQsMTQsMCwwLDEsMTQtMTRIMTc2QTE0LDE0LDAsMCwxLDE5MCwxNTJabS0xMiwwYTIsMiwwLDAsMC0yLTJINDBhMiwyLDAsMCwwLTIsMnY0MGEyLDIsMCwwLDAsMiwySDE3NmEyLDIsMCwwLDAsMi0yWiIvPjwvc3ZnPg==);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjIsNDBWMjE2YTYsNiwwLDAsMS0xMiwwVjQwYTYsNiwwLDAsMSwxMiwwWk0xOTAsNjR2NDBhMTQsMTQsMCwwLDEtMTQsMTRIODBhMTQsMTQsMCwwLDEtMTQtMTRWNjRBMTQsMTQsMCwwLDEsODAsNTBoOTZBMTQsMTQsMCwwLDEsMTkwLDY0Wm0tMTIsMGEyLDIsMCwwLDAtMi0ySDgwYTIsMiwwLDAsMC0yLDJ2NDBhMiwyLDAsMCwwLDIsMmg5NmEyLDIsMCwwLDAsMi0yWm0xMiw4OHY0MGExNCwxNCwwLDAsMS0xNCwxNEg0MGExNCwxNCwwLDAsMS0xNC0xNFYxNTJhMTQsMTQsMCwwLDEsMTQtMTRIMTc2QTE0LDE0LDAsMCwxLDE5MCwxNTJabS0xMiwwYTIsMiwwLDAsMC0yLTJINDBhMiwyLDAsMCwwLTIsMnY0MGEyLDIsMCwwLDAsMiwySDE3NmEyLDIsMCwwLDAsMi0yWiIvPjwvc3ZnPg==)}.icon-bell{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjAuMDcsMTc2Ljk0QzIxNC40MSwxNjcuMiwyMDYsMTM5LjczLDIwNiwxMDRhNzgsNzgsMCwxLDAtMTU2LDBjMCwzNS43NC04LjQyLDYzLjItMTQuMDgsNzIuOTRBMTQsMTQsMCwwLDAsNDgsMTk4SDkwLjQ4YTM4LDM4LDAsMCwwLDc1LDBIMjA4YTE0LDE0LDAsMCwwLDEyLjA2LTIxLjA2Wk0xMjgsMjE4YTI2LDI2LDAsMCwxLTI1LjI5LTIwaDUwLjU4QTI2LDI2LDAsMCwxLDEyOCwyMThabTgxLjcxLTMzYTEuOSwxLjksMCwwLDEtMS43LDFINDhhMS45LDEuOSwwLDAsMS0xLjctMSwyLDIsMCwwLDEsMC0yQzUzLjg3LDE3MCw2MiwxMzkuNjksNjIsMTA0YTY2LDY2LDAsMSwxLDEzMiwwYzAsMzUuNjgsOC4xNCw2NS45NSwxNS43MSw3OUEyLDIsMCwwLDEsMjA5LjcxLDE4NVoiLz48L3N2Zz4=);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjAuMDcsMTc2Ljk0QzIxNC40MSwxNjcuMiwyMDYsMTM5LjczLDIwNiwxMDRhNzgsNzgsMCwxLDAtMTU2LDBjMCwzNS43NC04LjQyLDYzLjItMTQuMDgsNzIuOTRBMTQsMTQsMCwwLDAsNDgsMTk4SDkwLjQ4YTM4LDM4LDAsMCwwLDc1LDBIMjA4YTE0LDE0LDAsMCwwLDEyLjA2LTIxLjA2Wk0xMjgsMjE4YTI2LDI2LDAsMCwxLTI1LjI5LTIwaDUwLjU4QTI2LDI2LDAsMCwxLDEyOCwyMThabTgxLjcxLTMzYTEuOSwxLjksMCwwLDEtMS43LDFINDhhMS45LDEuOSwwLDAsMS0xLjctMSwyLDIsMCwwLDEsMC0yQzUzLjg3LDE3MCw2MiwxMzkuNjksNjIsMTA0YTY2LDY2LDAsMSwxLDEzMiwwYzAsMzUuNjgsOC4xNCw2NS45NSwxNS43MSw3OUEyLDIsMCwwLDEsMjA5LjcxLDE4NVoiLz48L3N2Zz4=)}.icon-bell-ringing{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjMuMDUsNjkuMzNBNiw2LDAsMCwxLDIxNSw2Ni43Niw5Niw5NiwwLDAsMCwxODAuOCwyOS4wOGE2LDYsMCwwLDEsNi40LTEwLjE1LDEwOS4yNiwxMDkuMjYsMCwwLDEsMzguNDEsNDIuMzFBNiw2LDAsMCwxLDIyMy4wNSw2OS4zM1pNNDEsNjYuNzZBOTYsOTYsMCwwLDEsNzUuMiwyOS4wOGE2LDYsMCwwLDAtNi40LTEwLjE1QTEwOS4yNiwxMDkuMjYsMCwwLDAsMzAuMzksNjEuMjQsNiw2LDAsMSwwLDQxLDY2Ljc2Wm0xNzksMTEwLjE4QTE0LDE0LDAsMCwxLDIwOCwxOThoLTQyLjVhMzgsMzgsMCwwLDEtNzUsMEg0OGExNCwxNCwwLDAsMS0xMi4wNi0yMS4wNkM0NS4xMywxNjEuMDksNTAsMTM4LjYzLDUwLDExMmE3OCw3OCwwLDAsMSwxNTYsMEMyMDYsMTM5LDIxMC43NCwxNjAuODYsMjIwLjA4LDE3Ni45NFpNMTUzLjI5LDE5OEgxMDIuNzFhMjYsMjYsMCwwLDAsNTAuNTgsMFptNTYuNDItMTVDMTk5LjI5LDE2NSwxOTQsMTQxLjE1LDE5NCwxMTJhNjYsNjYsMCwwLDAtMTMyLDBjMCwyOS4xNi01LjI5LDUzLTE1LjcxLDcxYTIsMiwwLDAsMCwwLDIsMS45LDEuOSwwLDAsMCwxLjcsMUgyMDhhMS45LDEuOSwwLDAsMCwxLjctMUEyLDIsMCwwLDAsMjA5LjcxLDE4M1oiLz48L3N2Zz4=);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjMuMDUsNjkuMzNBNiw2LDAsMCwxLDIxNSw2Ni43Niw5Niw5NiwwLDAsMCwxODAuOCwyOS4wOGE2LDYsMCwwLDEsNi40LTEwLjE1LDEwOS4yNiwxMDkuMjYsMCwwLDEsMzguNDEsNDIuMzFBNiw2LDAsMCwxLDIyMy4wNSw2OS4zM1pNNDEsNjYuNzZBOTYsOTYsMCwwLDEsNzUuMiwyOS4wOGE2LDYsMCwwLDAtNi40LTEwLjE1QTEwOS4yNiwxMDkuMjYsMCwwLDAsMzAuMzksNjEuMjQsNiw2LDAsMSwwLDQxLDY2Ljc2Wm0xNzksMTEwLjE4QTE0LDE0LDAsMCwxLDIwOCwxOThoLTQyLjVhMzgsMzgsMCwwLDEtNzUsMEg0OGExNCwxNCwwLDAsMS0xMi4wNi0yMS4wNkM0NS4xMywxNjEuMDksNTAsMTM4LjYzLDUwLDExMmE3OCw3OCwwLDAsMSwxNTYsMEMyMDYsMTM5LDIxMC43NCwxNjAuODYsMjIwLjA4LDE3Ni45NFpNMTUzLjI5LDE5OEgxMDIuNzFhMjYsMjYsMCwwLDAsNTAuNTgsMFptNTYuNDItMTVDMTk5LjI5LDE2NSwxOTQsMTQxLjE1LDE5NCwxMTJhNjYsNjYsMCwwLDAtMTMyLDBjMCwyOS4xNi01LjI5LDUzLTE1LjcxLDcxYTIsMiwwLDAsMCwwLDIsMS45LDEuOSwwLDAsMCwxLjcsMUgyMDhhMS45LDEuOSwwLDAsMCwxLjctMUEyLDIsMCwwLDAsMjA5LjcxLDE4M1oiLz48L3N2Zz4=)}.icon-calendar{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMDgsMzRIMTgyVjI0YTYsNiwwLDAsMC0xMiwwVjM0SDg2VjI0YTYsNiwwLDAsMC0xMiwwVjM0SDQ4QTE0LDE0LDAsMCwwLDM0LDQ4VjIwOGExNCwxNCwwLDAsMCwxNCwxNEgyMDhhMTQsMTQsMCwwLDAsMTQtMTRWNDhBMTQsMTQsMCwwLDAsMjA4LDM0Wk00OCw0Nkg3NFY1NmE2LDYsMCwwLDAsMTIsMFY0Nmg4NFY1NmE2LDYsMCwwLDAsMTIsMFY0NmgyNmEyLDIsMCwwLDEsMiwyVjgySDQ2VjQ4QTIsMiwwLDAsMSw0OCw0NlpNMjA4LDIxMEg0OGEyLDIsMCwwLDEtMi0yVjk0SDIxMFYyMDhBMiwyLDAsMCwxLDIwOCwyMTBabS05OC05MHY2NGE2LDYsMCwwLDEtMTIsMFYxMjkuNzFsLTcuMzIsMy42NmE2LDYsMCwxLDEtNS4zNi0xMC43NGwxNi04QTYsNiwwLDAsMSwxMTAsMTIwWm01OS41NywyOS4yNUwxNDgsMTc4aDIwYTYsNiwwLDAsMSwwLDEySDEzNmE2LDYsMCwwLDEtNC44LTkuNkwxNjAsMTQyYTEwLDEwLDAsMSwwLTE2LjY1LTExQTYsNiwwLDEsMSwxMzMsMTI1YTIyLDIyLDAsMSwxLDM2LjYyLDI0LjI2WiIvPjwvc3ZnPg==);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMDgsMzRIMTgyVjI0YTYsNiwwLDAsMC0xMiwwVjM0SDg2VjI0YTYsNiwwLDAsMC0xMiwwVjM0SDQ4QTE0LDE0LDAsMCwwLDM0LDQ4VjIwOGExNCwxNCwwLDAsMCwxNCwxNEgyMDhhMTQsMTQsMCwwLDAsMTQtMTRWNDhBMTQsMTQsMCwwLDAsMjA4LDM0Wk00OCw0Nkg3NFY1NmE2LDYsMCwwLDAsMTIsMFY0Nmg4NFY1NmE2LDYsMCwwLDAsMTIsMFY0NmgyNmEyLDIsMCwwLDEsMiwyVjgySDQ2VjQ4QTIsMiwwLDAsMSw0OCw0NlpNMjA4LDIxMEg0OGEyLDIsMCwwLDEtMi0yVjk0SDIxMFYyMDhBMiwyLDAsMCwxLDIwOCwyMTBabS05OC05MHY2NGE2LDYsMCwwLDEtMTIsMFYxMjkuNzFsLTcuMzIsMy42NmE2LDYsMCwxLDEtNS4zNi0xMC43NGwxNi04QTYsNiwwLDAsMSwxMTAsMTIwWm01OS41NywyOS4yNUwxNDgsMTc4aDIwYTYsNiwwLDAsMSwwLDEySDEzNmE2LDYsMCwwLDEtNC44LTkuNkwxNjAsMTQyYTEwLDEwLDAsMSwwLTE2LjY1LTExQTYsNiwwLDEsMSwxMzMsMTI1YTIyLDIyLDAsMSwxLDM2LjYyLDI0LjI2WiIvPjwvc3ZnPg==)}.icon-check{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjguMjQsNzYuMjRsLTEyOCwxMjhhNiw2LDAsMCwxLTguNDgsMGwtNTYtNTZhNiw2LDAsMCwxLDguNDgtOC40OEw5NiwxOTEuNTEsMjE5Ljc2LDY3Ljc2YTYsNiwwLDAsMSw4LjQ4LDguNDhaIi8+PC9zdmc+);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjguMjQsNzYuMjRsLTEyOCwxMjhhNiw2LDAsMCwxLTguNDgsMGwtNTYtNTZhNiw2LDAsMCwxLDguNDgtOC40OEw5NiwxOTEuNTEsMjE5Ljc2LDY3Ljc2YTYsNiwwLDAsMSw4LjQ4LDguNDhaIi8+PC9zdmc+)}.icon-clock{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMjgsMjZBMTAyLDEwMiwwLDEsMCwyMzAsMTI4LDEwMi4xMiwxMDIuMTIsMCwwLDAsMTI4LDI2Wm0wLDE5MmE5MCw5MCwwLDEsMSw5MC05MEE5MC4xLDkwLjEsMCwwLDEsMTI4LDIxOFptNjItOTBhNiw2LDAsMCwxLTYsNkgxMjhhNiw2LDAsMCwxLTYtNlY3MmE2LDYsMCwwLDEsMTIsMHY1MGg1MEE2LDYsMCwwLDEsMTkwLDEyOFoiLz48L3N2Zz4=);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMjgsMjZBMTAyLDEwMiwwLDEsMCwyMzAsMTI4LDEwMi4xMiwxMDIuMTIsMCwwLDAsMTI4LDI2Wm0wLDE5MmE5MCw5MCwwLDEsMSw5MC05MEE5MC4xLDkwLjEsMCwwLDEsMTI4LDIxOFptNjItOTBhNiw2LDAsMCwxLTYsNkgxMjhhNiw2LDAsMCwxLTYtNlY3MmE2LDYsMCwwLDEsMTIsMHY1MGg1MEE2LDYsMCwwLDEsMTkwLDEyOFoiLz48L3N2Zz4=)}.icon-cloud{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xNjAsNDJBODYuMTEsODYuMTEsMCwwLDAsODIuNDMsOTAuODgsNjIsNjIsMCwxLDAsNzIsMjE0aDg4YTg2LDg2LDAsMCwwLDAtMTcyWm0wLDE2MEg3MmE1MCw1MCwwLDAsMSwwLTEwMCw1MC42Nyw1MC42NywwLDAsMSw1LjkxLjM1QTg1LjYxLDg1LjYxLDAsMCwwLDc0LDEyOGE2LDYsMCwwLDAsMTIsMCw3NCw3NCwwLDEsMSw3NCw3NFoiLz48L3N2Zz4=);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xNjAsNDJBODYuMTEsODYuMTEsMCwwLDAsODIuNDMsOTAuODgsNjIsNjIsMCwxLDAsNzIsMjE0aDg4YTg2LDg2LDAsMCwwLDAtMTcyWm0wLDE2MEg3MmE1MCw1MCwwLDAsMSwwLTEwMCw1MC42Nyw1MC42NywwLDAsMSw1LjkxLjM1QTg1LjYxLDg1LjYxLDAsMCwwLDc0LDEyOGE2LDYsMCwwLDAsMTIsMCw3NCw3NCwwLDEsMSw3NCw3NFoiLz48L3N2Zz4=)}.icon-copy{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTYsMzRIODhhNiw2LDAsMCwwLTYsNlY4Mkg0MGE2LDYsMCwwLDAtNiw2VjIxNmE2LDYsMCwwLDAsNiw2SDE2OGE2LDYsMCwwLDAsNi02VjE3NGg0MmE2LDYsMCwwLDAsNi02VjQwQTYsNiwwLDAsMCwyMTYsMzRaTTE2MiwyMTBINDZWOTRIMTYyWm00OC00OEgxNzRWODhhNiw2LDAsMCwwLTYtNkg5NFY0NkgyMTBaIi8+PC9zdmc+);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTYsMzRIODhhNiw2LDAsMCwwLTYsNlY4Mkg0MGE2LDYsMCwwLDAtNiw2VjIxNmE2LDYsMCwwLDAsNiw2SDE2OGE2LDYsMCwwLDAsNi02VjE3NGg0MmE2LDYsMCwwLDAsNi02VjQwQTYsNiwwLDAsMCwyMTYsMzRaTTE2MiwyMTBINDZWOTRIMTYyWm00OC00OEgxNzRWODhhNiw2LDAsMCwwLTYtNkg5NFY0NkgyMTBaIi8+PC9zdmc+)}.icon-download{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzgsMTM2djY0YTE0LDE0LDAsMCwxLTE0LDE0SDMyYTE0LDE0LDAsMCwxLTE0LTE0VjEzNmExNCwxNCwwLDAsMSwxNC0xNEg3MmE2LDYsMCwwLDEsMCwxMkgzMmEyLDIsMCwwLDAtMiwydjY0YTIsMiwwLDAsMCwyLDJIMjI0YTIsMiwwLDAsMCwyLTJWMTM2YTIsMiwwLDAsMC0yLTJIMTg0YTYsNiwwLDAsMSwwLTEyaDQwQTE0LDE0LDAsMCwxLDIzOCwxMzZabS0xMTQuMjQtMy43NmE2LDYsMCwwLDAsOC40OCwwbDQ4LTQ4YTYsNiwwLDAsMC04LjQ4LTguNDhMMTM0LDExMy41MVYyNGE2LDYsMCwwLDAtMTIsMHY4OS41MUw4NC4yNCw3NS43NmE2LDYsMCwwLDAtOC40OCw4LjQ4Wk0xOTgsMTY4YTEwLDEwLDAsMSwwLTEwLDEwQTEwLDEwLDAsMCwwLDE5OCwxNjhaIi8+PC9zdmc+);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzgsMTM2djY0YTE0LDE0LDAsMCwxLTE0LDE0SDMyYTE0LDE0LDAsMCwxLTE0LTE0VjEzNmExNCwxNCwwLDAsMSwxNC0xNEg3MmE2LDYsMCwwLDEsMCwxMkgzMmEyLDIsMCwwLDAtMiwydjY0YTIsMiwwLDAsMCwyLDJIMjI0YTIsMiwwLDAsMCwyLTJWMTM2YTIsMiwwLDAsMC0yLTJIMTg0YTYsNiwwLDAsMSwwLTEyaDQwQTE0LDE0LDAsMCwxLDIzOCwxMzZabS0xMTQuMjQtMy43NmE2LDYsMCwwLDAsOC40OCwwbDQ4LTQ4YTYsNiwwLDAsMC04LjQ4LTguNDhMMTM0LDExMy41MVYyNGE2LDYsMCwwLDAtMTIsMHY4OS41MUw4NC4yNCw3NS43NmE2LDYsMCwwLDAtOC40OCw4LjQ4Wk0xOTgsMTY4YTEwLDEwLDAsMSwwLTEwLDEwQTEwLDEwLDAsMCwwLDE5OCwxNjhaIi8+PC9zdmc+)}.icon-exclamation-mark{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xNDIsMjAwYTE0LDE0LDAsMSwxLTE0LTE0QTE0LDE0LDAsMCwxLDE0MiwyMDBabS0xNC00MmE2LDYsMCwwLDAsNi02VjQ4YTYsNiwwLDAsMC0xMiwwVjE1MkE2LDYsMCwwLDAsMTI4LDE1OFoiLz48L3N2Zz4=);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xNDIsMjAwYTE0LDE0LDAsMSwxLTE0LTE0QTE0LDE0LDAsMCwxLDE0MiwyMDBabS0xNC00MmE2LDYsMCwwLDAsNi02VjQ4YTYsNiwwLDAsMC0xMiwwVjE1MkE2LDYsMCwwLDAsMTI4LDE1OFoiLz48L3N2Zz4=)}.icon-hamburger{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik00OC4wNywxMDJIMjA3LjkzYTE0LDE0LDAsMCwwLDEzLjc2LTE3QzIxNC40NSw1MC44MywxNzUuMDUsMjYsMTI4LDI2UzQxLjU1LDUwLjgzLDM0LjMxLDg1YTE0LDE0LDAsMCwwLDEzLjc2LDE3Wm0tMi0xNC40OEM1Miw1OS4yOSw4Ny4yNSwzOCwxMjgsMzhTMjA0LDU5LjI5LDIxMCw4Ny41MmEyLDIsMCwwLDEtMiwyLjQ4SDQ4LjA3YTIsMiwwLDAsMS0yLTIuNDhaTTIzMCwxNTQuMzYsMTg4LjEsMTY5LjU4bC0zNy44Ny0xNS4xNWE2LDYsMCwwLDAtNC40NiwwTDEwOCwxNjkuNTQsNzAuMjMsMTU0LjQzYTYsNiwwLDAsMC00LjI4LS4wN2wtNDQsMTZBNiw2LDAsMCwwLDI0LDE4MmE2LjExLDYuMTEsMCwwLDAsMi4wNS0uMzZsMTYtNS44VjE4NGEzOCwzOCwwLDAsMCwzOCwzOGg5NmEzOCwzOCwwLDAsMCwzOC0zOFYxNzIuOTNsMjAuMDUtNy4yOWE2LDYsMCwwLDAtNC4xLTExLjI4Wk0yMDIsMTg0YTI2LDI2LDAsMCwxLTI2LDI2SDgwYTI2LDI2LDAsMCwxLTI2LTI2VjE3MS40OGwxMy45LTUuMDYsMzcuODcsMTUuMTVhNiw2LDAsMCwwLDQuNDYsMEwxNDgsMTY2LjQ2bDM3Ljc3LDE1LjExYTYsNiwwLDAsMCw0LjI4LjA3TDIwMiwxNzcuMjlaTTE4LDEyOGE2LDYsMCwwLDEsNi02SDIzMmE2LDYsMCwwLDEsMCwxMkgyNEE2LDYsMCwwLDEsMTgsMTI4WiIvPjwvc3ZnPg==);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik00OC4wNywxMDJIMjA3LjkzYTE0LDE0LDAsMCwwLDEzLjc2LTE3QzIxNC40NSw1MC44MywxNzUuMDUsMjYsMTI4LDI2UzQxLjU1LDUwLjgzLDM0LjMxLDg1YTE0LDE0LDAsMCwwLDEzLjc2LDE3Wm0tMi0xNC40OEM1Miw1OS4yOSw4Ny4yNSwzOCwxMjgsMzhTMjA0LDU5LjI5LDIxMCw4Ny41MmEyLDIsMCwwLDEtMiwyLjQ4SDQ4LjA3YTIsMiwwLDAsMS0yLTIuNDhaTTIzMCwxNTQuMzYsMTg4LjEsMTY5LjU4bC0zNy44Ny0xNS4xNWE2LDYsMCwwLDAtNC40NiwwTDEwOCwxNjkuNTQsNzAuMjMsMTU0LjQzYTYsNiwwLDAsMC00LjI4LS4wN2wtNDQsMTZBNiw2LDAsMCwwLDI0LDE4MmE2LjExLDYuMTEsMCwwLDAsMi4wNS0uMzZsMTYtNS44VjE4NGEzOCwzOCwwLDAsMCwzOCwzOGg5NmEzOCwzOCwwLDAsMCwzOC0zOFYxNzIuOTNsMjAuMDUtNy4yOWE2LDYsMCwwLDAtNC4xLTExLjI4Wk0yMDIsMTg0YTI2LDI2LDAsMCwxLTI2LDI2SDgwYTI2LDI2LDAsMCwxLTI2LTI2VjE3MS40OGwxMy45LTUuMDYsMzcuODcsMTUuMTVhNiw2LDAsMCwwLDQuNDYsMEwxNDgsMTY2LjQ2bDM3Ljc3LDE1LjExYTYsNiwwLDAsMCw0LjI4LjA3TDIwMiwxNzcuMjlaTTE4LDEyOGE2LDYsMCwwLDEsNi02SDIzMmE2LDYsMCwwLDEsMCwxMkgyNEE2LDYsMCwwLDEsMTgsMTI4WiIvPjwvc3ZnPg==)}.icon-heart{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xNzgsNDJjLTIxLDAtMzkuMjYsOS40Ny01MCwyNS4zNEMxMTcuMjYsNTEuNDcsOTksNDIsNzgsNDJhNjAuMDcsNjAuMDcsMCwwLDAtNjAsNjBjMCwyOS4yLDE4LjIsNTkuNTksNTQuMSw5MC4zMWEzMzQuNjgsMzM0LjY4LDAsMCwwLDUzLjA2LDM3LDYsNiwwLDAsMCw1LjY4LDAsMzM0LjY4LDMzNC42OCwwLDAsMCw1My4wNi0zN0MyMTkuOCwxNjEuNTksMjM4LDEzMS4yLDIzOCwxMDJBNjAuMDcsNjAuMDcsMCwwLDAsMTc4LDQyWk0xMjgsMjE3LjExQzExMS41OSwyMDcuNjQsMzAsMTU3LjcyLDMwLDEwMkE0OC4wNSw0OC4wNSwwLDAsMSw3OCw1NGMyMC4yOCwwLDM3LjMxLDEwLjgzLDQ0LjQ1LDI4LjI3YTYsNiwwLDAsMCwxMS4xLDBDMTQwLjY5LDY0LjgzLDE1Ny43Miw1NCwxNzgsNTRhNDguMDUsNDguMDUsMCwwLDEsNDgsNDhDMjI2LDE1Ny43MiwxNDQuNDEsMjA3LjY0LDEyOCwyMTcuMTFaIi8+PC9zdmc+);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xNzgsNDJjLTIxLDAtMzkuMjYsOS40Ny01MCwyNS4zNEMxMTcuMjYsNTEuNDcsOTksNDIsNzgsNDJhNjAuMDcsNjAuMDcsMCwwLDAtNjAsNjBjMCwyOS4yLDE4LjIsNTkuNTksNTQuMSw5MC4zMWEzMzQuNjgsMzM0LjY4LDAsMCwwLDUzLjA2LDM3LDYsNiwwLDAsMCw1LjY4LDAsMzM0LjY4LDMzNC42OCwwLDAsMCw1My4wNi0zN0MyMTkuOCwxNjEuNTksMjM4LDEzMS4yLDIzOCwxMDJBNjAuMDcsNjAuMDcsMCwwLDAsMTc4LDQyWk0xMjgsMjE3LjExQzExMS41OSwyMDcuNjQsMzAsMTU3LjcyLDMwLDEwMkE0OC4wNSw0OC4wNSwwLDAsMSw3OCw1NGMyMC4yOCwwLDM3LjMxLDEwLjgzLDQ0LjQ1LDI4LjI3YTYsNiwwLDAsMCwxMS4xLDBDMTQwLjY5LDY0LjgzLDE1Ny43Miw1NCwxNzgsNTRhNDguMDUsNDguMDUsMCwwLDEsNDgsNDhDMjI2LDE1Ny43MiwxNDQuNDEsMjA3LjY0LDEyOCwyMTcuMTFaIi8+PC9zdmc+)}.icon-image{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTYsNDJINDBBMTQsMTQsMCwwLDAsMjYsNTZWMjAwYTE0LDE0LDAsMCwwLDE0LDE0SDIxNmExNCwxNCwwLDAsMCwxNC0xNFY1NkExNCwxNCwwLDAsMCwyMTYsNDJaTTQwLDU0SDIxNmEyLDIsMCwwLDEsMiwyVjE2My41N0wxODguNTMsMTM0LjFhMTQsMTQsMCwwLDAtMTkuOCwwbC0yMS40MiwyMS40MkwxMDEuOSwxMTAuMWExNCwxNCwwLDAsMC0xOS44LDBMMzgsMTU0LjJWNTZBMiwyLDAsMCwxLDQwLDU0Wk0zOCwyMDBWMTcxLjE3bDUyLjU4LTUyLjU4YTIsMiwwLDAsMSwyLjg0LDBMMTc2LjgzLDIwMkg0MEEyLDIsMCwwLDEsMzgsMjAwWm0xNzgsMkgxOTMuOGwtMzgtMzgsMjEuNDEtMjEuNDJhMiwyLDAsMCwxLDIuODMsMGwzOCwzOFYyMDBBMiwyLDAsMCwxLDIxNiwyMDJaTTE0NiwxMDBhMTAsMTAsMCwxLDEsMTAsMTBBMTAsMTAsMCwwLDEsMTQ2LDEwMFoiLz48L3N2Zz4=);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTYsNDJINDBBMTQsMTQsMCwwLDAsMjYsNTZWMjAwYTE0LDE0LDAsMCwwLDE0LDE0SDIxNmExNCwxNCwwLDAsMCwxNC0xNFY1NkExNCwxNCwwLDAsMCwyMTYsNDJaTTQwLDU0SDIxNmEyLDIsMCwwLDEsMiwyVjE2My41N0wxODguNTMsMTM0LjFhMTQsMTQsMCwwLDAtMTkuOCwwbC0yMS40MiwyMS40MkwxMDEuOSwxMTAuMWExNCwxNCwwLDAsMC0xOS44LDBMMzgsMTU0LjJWNTZBMiwyLDAsMCwxLDQwLDU0Wk0zOCwyMDBWMTcxLjE3bDUyLjU4LTUyLjU4YTIsMiwwLDAsMSwyLjg0LDBMMTc2LjgzLDIwMkg0MEEyLDIsMCwwLDEsMzgsMjAwWm0xNzgsMkgxOTMuOGwtMzgtMzgsMjEuNDEtMjEuNDJhMiwyLDAsMCwxLDIuODMsMGwzOCwzOFYyMDBBMiwyLDAsMCwxLDIxNiwyMDJaTTE0NiwxMDBhMTAsMTAsMCwxLDEsMTAsMTBBMTAsMTAsMCwwLDEsMTQ2LDEwMFoiLz48L3N2Zz4=)}.icon-link{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzgsODguMThhNTIuNDIsNTIuNDIsMCwwLDEtMTUuNCwzNS42NmwtMzQuNzUsMzQuNzVBNTIuMjgsNTIuMjgsMCwwLDEsMTUwLjYyLDE3NGgtLjA1QTUyLjYzLDUyLjYzLDAsMCwxLDk4LDExOS45YTYsNiwwLDAsMSw2LTUuODRoLjE3YTYsNiwwLDAsMSw1LjgzLDYuMTZBNDAuNjIsNDAuNjIsMCwwLDAsMTUwLjU4LDE2MmgwYTQwLjQsNDAuNCwwLDAsMCwyOC43My0xMS45bDM0Ljc1LTM0Ljc0QTQwLjYzLDQwLjYzLDAsMCwwLDE1Ni42Myw1Ny45bC0xMSwxMWE2LDYsMCwwLDEtOC40OS04LjQ5bDExLTExYTUyLjYyLDUyLjYyLDAsMCwxLDc0LjQzLDBBNTIuODMsNTIuODMsMCwwLDEsMjM4LDg4LjE4Wm0tMTI3LjYyLDk4LjktMTEsMTFBNDAuMzYsNDAuMzYsMCwwLDEsNzAuNiwyMTBoMGE0MC42Myw0MC42MywwLDAsMS0yOC43LTY5LjM2TDc2LjYyLDEwNS45QTQwLjYzLDQwLjYzLDAsMCwxLDE0NiwxMzUuNzdhNiw2LDAsMCwwLDUuODMsNi4xNkgxNTJhNiw2LDAsMCwwLDYtNS44NEE1Mi42Myw1Mi42MywwLDAsMCw2OC4xNCw5Ny40MkwzMy4zOCwxMzIuMTZBNTIuNjMsNTIuNjMsMCwwLDAsNzAuNTYsMjIyaDBhNTIuMjYsNTIuMjYsMCwwLDAsMzcuMjItMTUuNDJsMTEtMTFhNiw2LDAsMSwwLTguNDktOC40OFoiLz48L3N2Zz4=);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzgsODguMThhNTIuNDIsNTIuNDIsMCwwLDEtMTUuNCwzNS42NmwtMzQuNzUsMzQuNzVBNTIuMjgsNTIuMjgsMCwwLDEsMTUwLjYyLDE3NGgtLjA1QTUyLjYzLDUyLjYzLDAsMCwxLDk4LDExOS45YTYsNiwwLDAsMSw2LTUuODRoLjE3YTYsNiwwLDAsMSw1LjgzLDYuMTZBNDAuNjIsNDAuNjIsMCwwLDAsMTUwLjU4LDE2MmgwYTQwLjQsNDAuNCwwLDAsMCwyOC43My0xMS45bDM0Ljc1LTM0Ljc0QTQwLjYzLDQwLjYzLDAsMCwwLDE1Ni42Myw1Ny45bC0xMSwxMWE2LDYsMCwwLDEtOC40OS04LjQ5bDExLTExYTUyLjYyLDUyLjYyLDAsMCwxLDc0LjQzLDBBNTIuODMsNTIuODMsMCwwLDEsMjM4LDg4LjE4Wm0tMTI3LjYyLDk4LjktMTEsMTFBNDAuMzYsNDAuMzYsMCwwLDEsNzAuNiwyMTBoMGE0MC42Myw0MC42MywwLDAsMS0yOC43LTY5LjM2TDc2LjYyLDEwNS45QTQwLjYzLDQwLjYzLDAsMCwxLDE0NiwxMzUuNzdhNiw2LDAsMCwwLDUuODMsNi4xNkgxNTJhNiw2LDAsMCwwLDYtNS44NEE1Mi42Myw1Mi42MywwLDAsMCw2OC4xNCw5Ny40MkwzMy4zOCwxMzIuMTZBNTIuNjMsNTIuNjMsMCwwLDAsNzAuNTYsMjIyaDBhNTIuMjYsNTIuMjYsMCwwLDAsMzcuMjItMTUuNDJsMTEtMTFhNiw2LDAsMSwwLTguNDktOC40OFoiLz48L3N2Zz4=)}.icon-list{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjIsMTI4YTYsNiwwLDAsMS02LDZINDBhNiw2LDAsMCwxLDAtMTJIMjE2QTYsNiwwLDAsMSwyMjIsMTI4Wk00MCw3MEgyMTZhNiw2LDAsMCwwLDAtMTJINDBhNiw2LDAsMCwwLDAsMTJaTTIxNiwxODZINDBhNiw2LDAsMCwwLDAsMTJIMjE2YTYsNiwwLDAsMCwwLTEyWiIvPjwvc3ZnPg==);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjIsMTI4YTYsNiwwLDAsMS02LDZINDBhNiw2LDAsMCwxLDAtMTJIMjE2QTYsNiwwLDAsMSwyMjIsMTI4Wk00MCw3MEgyMTZhNiw2LDAsMCwwLDAtMTJINDBhNiw2LDAsMCwwLDAsMTJaTTIxNiwxODZINDBhNiw2LDAsMCwwLDAsMTJIMjE2YTYsNiwwLDAsMCwwLTEyWiIvPjwvc3ZnPg==)}.icon-list-bullets{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik04Miw2NGE2LDYsMCwwLDEsNi02SDIxNmE2LDYsMCwwLDEsMCwxMkg4OEE2LDYsMCwwLDEsODIsNjRabTEzNCw1OEg4OGE2LDYsMCwwLDAsMCwxMkgyMTZhNiw2LDAsMCwwLDAtMTJabTAsNjRIODhhNiw2LDAsMCwwLDAsMTJIMjE2YTYsNiwwLDAsMCwwLTEyWk00NCw1NEExMCwxMCwwLDEsMCw1NCw2NCwxMCwxMCwwLDAsMCw0NCw1NFptMCwxMjhhMTAsMTAsMCwxLDAsMTAsMTBBMTAsMTAsMCwwLDAsNDQsMTgyWm0wLTY0YTEwLDEwLDAsMSwwLDEwLDEwQTEwLDEwLDAsMCwwLDQ0LDExOFoiLz48L3N2Zz4=);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik04Miw2NGE2LDYsMCwwLDEsNi02SDIxNmE2LDYsMCwwLDEsMCwxMkg4OEE2LDYsMCwwLDEsODIsNjRabTEzNCw1OEg4OGE2LDYsMCwwLDAsMCwxMkgyMTZhNiw2LDAsMCwwLDAtMTJabTAsNjRIODhhNiw2LDAsMCwwLDAsMTJIMjE2YTYsNiwwLDAsMCwwLTEyWk00NCw1NEExMCwxMCwwLDEsMCw1NCw2NCwxMCwxMCwwLDAsMCw0NCw1NFptMCwxMjhhMTAsMTAsMCwxLDAsMTAsMTBBMTAsMTAsMCwwLDAsNDQsMTgyWm0wLTY0YTEwLDEwLDAsMSwwLDEwLDEwQTEwLDEwLDAsMCwwLDQ0LDExOFoiLz48L3N2Zz4=)}.icon-list-heart{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0zNCw2NGE2LDYsMCwwLDEsNi02SDIxNmE2LDYsMCwwLDEsMCwxMkg0MEE2LDYsMCwwLDEsMzQsNjRabTYsNzBoNjRhNiw2LDAsMCwwLDAtMTJINDBhNiw2LDAsMCwwLDAsMTJabTgwLDUySDQwYTYsNiwwLDAsMCwwLDEyaDgwYTYsNiwwLDAsMCwwLTEyWm0xMjYtNDJjMCwzNS4zLTQ5LjIyLDYwLjMyLTUxLjMyLDYxLjM3YTYsNiwwLDAsMS01LjM2LDBDMTg3LjIyLDIwNC4zMiwxMzgsMTc5LjMsMTM4LDE0NGEzMCwzMCwwLDAsMSw1NC0xOCwzMCwzMCwwLDAsMSw1NCwxOFptLTEyLDBhMTgsMTgsMCwwLDAtMzYsMCw2LDYsMCwwLDEtMTIsMCwxOCwxOCwwLDAsMC0zNiwwYzAsMTQuMTUsMTEuNzgsMjcsMjEuNjcsMzUuMjVBMTM4LjgyLDEzOC44MiwwLDAsMCwxOTIsMTkzLjE5YTEzOCwxMzgsMCwwLDAsMjAuMzMtMTMuOTRDMjIyLjIyLDE3MSwyMzQsMTU4LjE1LDIzNCwxNDRaIi8+PC9zdmc+);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0zNCw2NGE2LDYsMCwwLDEsNi02SDIxNmE2LDYsMCwwLDEsMCwxMkg0MEE2LDYsMCwwLDEsMzQsNjRabTYsNzBoNjRhNiw2LDAsMCwwLDAtMTJINDBhNiw2LDAsMCwwLDAsMTJabTgwLDUySDQwYTYsNiwwLDAsMCwwLDEyaDgwYTYsNiwwLDAsMCwwLTEyWm0xMjYtNDJjMCwzNS4zLTQ5LjIyLDYwLjMyLTUxLjMyLDYxLjM3YTYsNiwwLDAsMS01LjM2LDBDMTg3LjIyLDIwNC4zMiwxMzgsMTc5LjMsMTM4LDE0NGEzMCwzMCwwLDAsMSw1NC0xOCwzMCwzMCwwLDAsMSw1NCwxOFptLTEyLDBhMTgsMTgsMCwwLDAtMzYsMCw2LDYsMCwwLDEtMTIsMCwxOCwxOCwwLDAsMC0zNiwwYzAsMTQuMTUsMTEuNzgsMjcsMjEuNjcsMzUuMjVBMTM4LjgyLDEzOC44MiwwLDAsMCwxOTIsMTkzLjE5YTEzOCwxMzgsMCwwLDAsMjAuMzMtMTMuOTRDMjIyLjIyLDE3MSwyMzQsMTU4LjE1LDIzNCwxNDRaIi8+PC9zdmc+)}.icon-list-numbers{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjIsMTI4YTYsNiwwLDAsMS02LDZIMTA0YTYsNiwwLDAsMSwwLTEySDIxNkE2LDYsMCwwLDEsMjIyLDEyOFpNMTA0LDcwSDIxNmE2LDYsMCwwLDAsMC0xMkgxMDRhNiw2LDAsMCwwLDAsMTJaTTIxNiwxODZIMTA0YTYsNiwwLDAsMCwwLDEySDIxNmE2LDYsMCwwLDAsMC0xMlpNNDIuNjgsNTMuMzcsNTAsNDkuNzFWMTA0YTYsNiwwLDAsMCwxMiwwVjQwYTYsNiwwLDAsMC04LjY4LTUuMzdsLTE2LDhhNiw2LDAsMCwwLDUuMzYsMTAuNzRaTTcyLDIwMkg1MmwyMS40OC0yOC43NEEyMS41LDIxLjUsMCwwLDAsNzcuNzksMTU3LDIxLjc1LDIxLjc1LDAsMCwwLDY5LDE0Mi4zOGEyMi44NiwyMi44NiwwLDAsMC0zMS4zNSw0LjMxLDIyLjE4LDIyLjE4LDAsMCwwLTMuMjgsNS45Miw2LDYsMCwwLDAsMTEuMjgsNC4xMSw5Ljg3LDkuODcsMCwwLDEsMS40OC0yLjY3LDEwLjc4LDEwLjc4LDAsMCwxLDE0Ljc4LTIsOS44OSw5Ljg5LDAsMCwxLDQsNi42MSw5LjY0LDkuNjQsMCwwLDEtMiw3LjI4bC0uMDYuMDlMMzUuMiwyMDQuNDFBNiw2LDAsMCwwLDQwLDIxNEg3MmE2LDYsMCwwLDAsMC0xMloiLz48L3N2Zz4=);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjIsMTI4YTYsNiwwLDAsMS02LDZIMTA0YTYsNiwwLDAsMSwwLTEySDIxNkE2LDYsMCwwLDEsMjIyLDEyOFpNMTA0LDcwSDIxNmE2LDYsMCwwLDAsMC0xMkgxMDRhNiw2LDAsMCwwLDAsMTJaTTIxNiwxODZIMTA0YTYsNiwwLDAsMCwwLDEySDIxNmE2LDYsMCwwLDAsMC0xMlpNNDIuNjgsNTMuMzcsNTAsNDkuNzFWMTA0YTYsNiwwLDAsMCwxMiwwVjQwYTYsNiwwLDAsMC04LjY4LTUuMzdsLTE2LDhhNiw2LDAsMCwwLDUuMzYsMTAuNzRaTTcyLDIwMkg1MmwyMS40OC0yOC43NEEyMS41LDIxLjUsMCwwLDAsNzcuNzksMTU3LDIxLjc1LDIxLjc1LDAsMCwwLDY5LDE0Mi4zOGEyMi44NiwyMi44NiwwLDAsMC0zMS4zNSw0LjMxLDIyLjE4LDIyLjE4LDAsMCwwLTMuMjgsNS45Miw2LDYsMCwwLDAsMTEuMjgsNC4xMSw5Ljg3LDkuODcsMCwwLDEsMS40OC0yLjY3LDEwLjc4LDEwLjc4LDAsMCwxLDE0Ljc4LTIsOS44OSw5Ljg5LDAsMCwxLDQsNi42MSw5LjY0LDkuNjQsMCwwLDEtMiw3LjI4bC0uMDYuMDlMMzUuMiwyMDQuNDFBNiw2LDAsMCwwLDQwLDIxNEg3MmE2LDYsMCwwLDAsMC0xMloiLz48L3N2Zz4=)}.icon-minus{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjIsMTI4YTYsNiwwLDAsMS02LDZINDBhNiw2LDAsMCwxLDAtMTJIMjE2QTYsNiwwLDAsMSwyMjIsMTI4WiIvPjwvc3ZnPg==);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjIsMTI4YTYsNiwwLDAsMS02LDZINDBhNiw2LDAsMCwxLDAtMTJIMjE2QTYsNiwwLDAsMSwyMjIsMTI4WiIvPjwvc3ZnPg==)}.icon-note{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik05MCw5NmE2LDYsMCwwLDEsNi02aDY0YTYsNiwwLDAsMSwwLDEySDk2QTYsNiwwLDAsMSw5MCw5NlptNiwzOGg2NGE2LDYsMCwwLDAsMC0xMkg5NmE2LDYsMCwwLDAsMCwxMlptMzIsMjBIOTZhNiw2LDAsMCwwLDAsMTJoMzJhNiw2LDAsMCwwLDAtMTJaTTIyMiw0OFYxNTYuNjlhMTMuOTQsMTMuOTQsMCwwLDEtNC4xLDkuOUwxNjYuNTksMjE3LjlhMTMuOTQsMTMuOTQsMCwwLDEtOS45LDQuMUg0OGExNCwxNCwwLDAsMS0xNC0xNFY0OEExNCwxNCwwLDAsMSw0OCwzNEgyMDhBMTQsMTQsMCwwLDEsMjIyLDQ4Wk00OCwyMTBIMTU0VjE2MGE2LDYsMCwwLDEsNi02aDUwVjQ4YTIsMiwwLDAsMC0yLTJINDhhMiwyLDAsMCwwLTIsMlYyMDhBMiwyLDAsMCwwLDQ4LDIxMFptMTUzLjUyLTQ0SDE2NnYzNS41MloiLz48L3N2Zz4=);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik05MCw5NmE2LDYsMCwwLDEsNi02aDY0YTYsNiwwLDAsMSwwLDEySDk2QTYsNiwwLDAsMSw5MCw5NlptNiwzOGg2NGE2LDYsMCwwLDAsMC0xMkg5NmE2LDYsMCwwLDAsMCwxMlptMzIsMjBIOTZhNiw2LDAsMCwwLDAsMTJoMzJhNiw2LDAsMCwwLDAtMTJaTTIyMiw0OFYxNTYuNjlhMTMuOTQsMTMuOTQsMCwwLDEtNC4xLDkuOUwxNjYuNTksMjE3LjlhMTMuOTQsMTMuOTQsMCwwLDEtOS45LDQuMUg0OGExNCwxNCwwLDAsMS0xNC0xNFY0OEExNCwxNCwwLDAsMSw0OCwzNEgyMDhBMTQsMTQsMCwwLDEsMjIyLDQ4Wk00OCwyMTBIMTU0VjE2MGE2LDYsMCwwLDEsNi02aDUwVjQ4YTIsMiwwLDAsMC0yLTJINDhhMiwyLDAsMCwwLTIsMlYyMDhBMiwyLDAsMCwwLDQ4LDIxMFptMTUzLjUyLTQ0SDE2NnYzNS41MloiLz48L3N2Zz4=)}.icon-paragraph{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMDgsNDJIOTZhNjIsNjIsMCwwLDAsMCwxMjRoNDJ2NDJhNiw2LDAsMCwwLDEyLDBWNTRoMjhWMjA4YTYsNiwwLDAsMCwxMiwwVjU0aDE4YTYsNiwwLDAsMCwwLTEyWk0xMzgsMTU0SDk2QTUwLDUwLDAsMCwxLDk2LDU0aDQyWiIvPjwvc3ZnPg==);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMDgsNDJIOTZhNjIsNjIsMCwwLDAsMCwxMjRoNDJ2NDJhNiw2LDAsMCwwLDEyLDBWNTRoMjhWMjA4YTYsNiwwLDAsMCwxMiwwVjU0aDE4YTYsNiwwLDAsMCwwLTEyWk0xMzgsMTU0SDk2QTUwLDUwLDAsMCwxLDk2LDU0aDQyWiIvPjwvc3ZnPg==)}.icon-password{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik00Niw1NlYyMDBhNiw2LDAsMCwxLTEyLDBWNTZhNiw2LDAsMCwxLDEyLDBabTk0LjU4LDU2LjQxTDExOCwxMTkuNzRWOTZhNiw2LDAsMCwwLTEyLDB2MjMuNzRsLTIyLjU4LTcuMzNhNiw2LDAsMSwwLTMuNzEsMTEuNDFsMjIuNTgsNy4zMy0xNCwxOS4yMWE2LDYsMCwxLDAsOS43LDcuMDZsMTQtMTkuMjEsMTQsMTkuMjFhNiw2LDAsMCwwLDkuNy03LjA2bC0xNC0xOS4yMSwyMi41OC03LjMzYTYsNiwwLDEsMC0zLjcxLTExLjQxWm0xMDMuNTYsMy44NWE2LDYsMCwwLDAtNy41Ni0zLjg1TDIxNCwxMTkuNzRWOTZhNiw2LDAsMCwwLTEyLDB2MjMuNzRsLTIyLjU4LTcuMzNhNiw2LDAsMSwwLTMuNzEsMTEuNDFsMjIuNTgsNy4zMy0xMy45NSwxOS4yMWE2LDYsMCwxLDAsOS43LDcuMDZsMTQtMTkuMjEsMTQsMTkuMjFhNiw2LDAsMCwwLDkuNy03LjA2bC0xMy45NS0xOS4yMSwyMi41OC03LjMzQTYsNiwwLDAsMCwyNDQuMTQsMTE2LjI2WiIvPjwvc3ZnPg==);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik00Niw1NlYyMDBhNiw2LDAsMCwxLTEyLDBWNTZhNiw2LDAsMCwxLDEyLDBabTk0LjU4LDU2LjQxTDExOCwxMTkuNzRWOTZhNiw2LDAsMCwwLTEyLDB2MjMuNzRsLTIyLjU4LTcuMzNhNiw2LDAsMSwwLTMuNzEsMTEuNDFsMjIuNTgsNy4zMy0xNCwxOS4yMWE2LDYsMCwxLDAsOS43LDcuMDZsMTQtMTkuMjEsMTQsMTkuMjFhNiw2LDAsMCwwLDkuNy03LjA2bC0xNC0xOS4yMSwyMi41OC03LjMzYTYsNiwwLDEsMC0zLjcxLTExLjQxWm0xMDMuNTYsMy44NWE2LDYsMCwwLDAtNy41Ni0zLjg1TDIxNCwxMTkuNzRWOTZhNiw2LDAsMCwwLTEyLDB2MjMuNzRsLTIyLjU4LTcuMzNhNiw2LDAsMSwwLTMuNzEsMTEuNDFsMjIuNTgsNy4zMy0xMy45NSwxOS4yMWE2LDYsMCwxLDAsOS43LDcuMDZsMTQtMTkuMjEsMTQsMTkuMjFhNiw2LDAsMCwwLDkuNy03LjA2bC0xMy45NS0xOS4yMSwyMi41OC03LjMzQTYsNiwwLDAsMCwyNDQuMTQsMTE2LjI2WiIvPjwvc3ZnPg==)}.icon-share{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjguMjQsMTA4LjI0bC00OCw0OGE2LDYsMCwwLDEtOC40OC04LjQ4TDIwOS41MSwxMTBIMTY1YTg5Ljk0LDg5Ljk0LDAsMCwwLTg3LjE3LDY3LjUsNiw2LDAsMCwxLTExLjYyLTNBMTAxLjk0LDEwMS45NCwwLDAsMSwxNjUsOThoNDQuNTNMMTcxLjc2LDYwLjI0YTYsNiwwLDAsMSw4LjQ4LTguNDhsNDgsNDhBNiw2LDAsMCwxLDIyOC4yNCwxMDguMjRaTTE5MiwyMTBIMzhWODhhNiw2LDAsMCwwLTEyLDBWMjE2YTYsNiwwLDAsMCw2LDZIMTkyYTYsNiwwLDAsMCwwLTEyWiIvPjwvc3ZnPg==);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjguMjQsMTA4LjI0bC00OCw0OGE2LDYsMCwwLDEtOC40OC04LjQ4TDIwOS41MSwxMTBIMTY1YTg5Ljk0LDg5Ljk0LDAsMCwwLTg3LjE3LDY3LjUsNiw2LDAsMCwxLTExLjYyLTNBMTAxLjk0LDEwMS45NCwwLDAsMSwxNjUsOThoNDQuNTNMMTcxLjc2LDYwLjI0YTYsNiwwLDAsMSw4LjQ4LTguNDhsNDgsNDhBNiw2LDAsMCwxLDIyOC4yNCwxMDguMjRaTTE5MiwyMTBIMzhWODhhNiw2LDAsMCwwLTEyLDBWMjE2YTYsNiwwLDAsMCw2LDZIMTkyYTYsNiwwLDAsMCwwLTEyWiIvPjwvc3ZnPg==)}.icon-star{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzcuMjgsOTcuODdBMTQuMTgsMTQuMTgsMCwwLDAsMjI0Ljc2LDg4bC02MC4yNS00Ljg3LTIzLjIyLTU2LjJhMTQuMzcsMTQuMzcsMCwwLDAtMjYuNTgsMEw5MS40OSw4My4xMSwzMS4yNCw4OGExNC4xOCwxNC4xOCwwLDAsMC0xMi41Miw5Ljg5QTE0LjQzLDE0LjQzLDAsMCwwLDIzLDExMy4zMkw2OSwxNTIuOTNsLTE0LDU5LjI1YTE0LjQsMTQuNCwwLDAsMCw1LjU5LDE1LDE0LjEsMTQuMSwwLDAsMCwxNS45MS42TDEyOCwxOTYuMTJsNTEuNTgsMzEuNzFhMTQuMSwxNC4xLDAsMCwwLDE1LjkxLS42LDE0LjQsMTQuNCwwLDAsMCw1LjU5LTE1bC0xNC01OS4yNUwyMzMsMTEzLjMyQTE0LjQzLDE0LjQzLDAsMCwwLDIzNy4yOCw5Ny44N1ptLTEyLjE0LDYuMzctNDguNjksNDJhNiw2LDAsMCwwLTEuOTIsNS45MmwxNC44OCw2Mi43OWEyLjM1LDIuMzUsMCwwLDEtLjk1LDIuNTcsMi4yNCwyLjI0LDAsMCwxLTIuNi4xTDEzMS4xNCwxODRhNiw2LDAsMCwwLTYuMjgsMEw3MC4xNCwyMTcuNjFhMi4yNCwyLjI0LDAsMCwxLTIuNi0uMSwyLjM1LDIuMzUsMCwwLDEtMS0yLjU3bDE0Ljg4LTYyLjc5YTYsNiwwLDAsMC0xLjkyLTUuOTJsLTQ4LjY5LTQyYTIuMzcsMi4zNywwLDAsMS0uNzMtMi42NSwyLjI4LDIuMjgsMCwwLDEsMi4wNy0xLjY1bDYzLjkyLTUuMTZhNiw2LDAsMCwwLDUuMDYtMy42OWwyNC42My01OS42YTIuMzUsMi4zNSwwLDAsMSw0LjM4LDBsMjQuNjMsNTkuNmE2LDYsMCwwLDAsNS4wNiwzLjY5bDYzLjkyLDUuMTZhMi4yOCwyLjI4LDAsMCwxLDIuMDcsMS42NUEyLjM3LDIuMzcsMCwwLDEsMjI1LjE0LDEwNC4yNFoiLz48L3N2Zz4=);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzcuMjgsOTcuODdBMTQuMTgsMTQuMTgsMCwwLDAsMjI0Ljc2LDg4bC02MC4yNS00Ljg3LTIzLjIyLTU2LjJhMTQuMzcsMTQuMzcsMCwwLDAtMjYuNTgsMEw5MS40OSw4My4xMSwzMS4yNCw4OGExNC4xOCwxNC4xOCwwLDAsMC0xMi41Miw5Ljg5QTE0LjQzLDE0LjQzLDAsMCwwLDIzLDExMy4zMkw2OSwxNTIuOTNsLTE0LDU5LjI1YTE0LjQsMTQuNCwwLDAsMCw1LjU5LDE1LDE0LjEsMTQuMSwwLDAsMCwxNS45MS42TDEyOCwxOTYuMTJsNTEuNTgsMzEuNzFhMTQuMSwxNC4xLDAsMCwwLDE1LjkxLS42LDE0LjQsMTQuNCwwLDAsMCw1LjU5LTE1bC0xNC01OS4yNUwyMzMsMTEzLjMyQTE0LjQzLDE0LjQzLDAsMCwwLDIzNy4yOCw5Ny44N1ptLTEyLjE0LDYuMzctNDguNjksNDJhNiw2LDAsMCwwLTEuOTIsNS45MmwxNC44OCw2Mi43OWEyLjM1LDIuMzUsMCwwLDEtLjk1LDIuNTcsMi4yNCwyLjI0LDAsMCwxLTIuNi4xTDEzMS4xNCwxODRhNiw2LDAsMCwwLTYuMjgsMEw3MC4xNCwyMTcuNjFhMi4yNCwyLjI0LDAsMCwxLTIuNi0uMSwyLjM1LDIuMzUsMCwwLDEtMS0yLjU3bDE0Ljg4LTYyLjc5YTYsNiwwLDAsMC0xLjkyLTUuOTJsLTQ4LjY5LTQyYTIuMzcsMi4zNywwLDAsMS0uNzMtMi42NSwyLjI4LDIuMjgsMCwwLDEsMi4wNy0xLjY1bDYzLjkyLTUuMTZhNiw2LDAsMCwwLDUuMDYtMy42OWwyNC42My01OS42YTIuMzUsMi4zNSwwLDAsMSw0LjM4LDBsMjQuNjMsNTkuNmE2LDYsMCwwLDAsNS4wNiwzLjY5bDYzLjkyLDUuMTZhMi4yOCwyLjI4LDAsMCwxLDIuMDcsMS42NUEyLjM3LDIuMzcsMCwwLDEsMjI1LjE0LDEwNC4yNFoiLz48L3N2Zz4=)}.icon-star-half{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzMsMTEzLjMybC01LjA2LDQuMzZhNiw2LDAsMSwxLTcuODMtOS4wOGw1LjA2LTQuMzZhMi4zOCwyLjM4LDAsMCwwLC43Mi0yLjY1LDIuMjgsMi4yOCwwLDAsMC0yLjA3LTEuNjVsLTguMjgtLjY3YTYsNiwwLDEsMSwxLTEybDguMjcuNjdhMTQuMTcsMTQuMTcsMCwwLDEsMTIuNTIsOS44OUExNC40MywxNC40MywwLDAsMSwyMzMsMTEzLjMyWm0tNTguNDUsMzguODMsNC4wOCwxNy4yMmE2LDYsMCwwLDAsNS44Myw0LjYyLDUuNTEsNS41MSwwLDAsMCwxLjM5LS4xNyw2LDYsMCwwLDAsNC40Ni03LjIybC0zLjI0LTEzLjY3LDguODctNy42NWE2LDYsMCwxLDAtNy44NC05LjA5bC0xMS42MywxMEE2LDYsMCwwLDAsMTc0LjUzLDE1Mi4xNVptMjUuMjMsNTQuNDVhNiw2LDAsMSwwLTExLjY3LDIuNzdsMS4zMiw1LjU3YTIuMzUsMi4zNSwwLDAsMS0uOTUsMi41NywyLjI0LDIuMjQsMCwwLDEtMi42LjFsLTYuNzEtNC4xM2E2LDYsMCwxLDAtNi4yOSwxMC4yMmw2LjcyLDQuMTNhMTQuMzksMTQuMzksMCwwLDAsMjEuNS0xNS42NVpNMTc1LjUyLDk2bC0xNS42NC0xLjI2YTYsNiwwLDAsMS01LjA2LTMuNjlMMTM0LDQwLjd2MTQ1bDguMzksNS4xNWE2LDYsMCwwLDEtNi4yOSwxMC4yM2wtOC4xLTVMNzYuNDIsMjI3LjgzYTE0LjM5LDE0LjM5LDAsMCwxLTIxLjUtMTUuNjVsMTQtNTkuMjVMMjMsMTEzLjMyYTE0LjQzLDE0LjQzLDAsMCwxLTQuMy0xNS40NUExNC4xNywxNC4xNywwLDAsMSwzMS4yNCw4OGw2MC4yNS00Ljg3LDIzLjIyLTU2LjJhMTQuMzcsMTQuMzcsMCwwLDEsMjYuNTgsMGwyMy4yMiw1Ni4yLDEyLDFhNiw2LDAsMCwxLTEsMTJaTTEyMiwxODUuNzJWNDAuN0wxMDEuMTgsOTEuMDlhNiw2LDAsMCwxLTUuMDYsMy42OUwzMi4yLDk5Ljk0YTIuMjgsMi4yOCwwLDAsMC0yLjA3LDEuNjUsMi4zOCwyLjM4LDAsMCwwLC43MiwyLjY1bDQ4LjcsNDJhNiw2LDAsMCwxLDEuOTIsNS45Mkw2Ni41OSwyMTQuOTRhMi4zNSwyLjM1LDAsMCwwLDEsMi41NywyLjI0LDIuMjQsMCwwLDAsMi42LjFaIi8+PC9zdmc+);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzMsMTEzLjMybC01LjA2LDQuMzZhNiw2LDAsMSwxLTcuODMtOS4wOGw1LjA2LTQuMzZhMi4zOCwyLjM4LDAsMCwwLC43Mi0yLjY1LDIuMjgsMi4yOCwwLDAsMC0yLjA3LTEuNjVsLTguMjgtLjY3YTYsNiwwLDEsMSwxLTEybDguMjcuNjdhMTQuMTcsMTQuMTcsMCwwLDEsMTIuNTIsOS44OUExNC40MywxNC40MywwLDAsMSwyMzMsMTEzLjMyWm0tNTguNDUsMzguODMsNC4wOCwxNy4yMmE2LDYsMCwwLDAsNS44Myw0LjYyLDUuNTEsNS41MSwwLDAsMCwxLjM5LS4xNyw2LDYsMCwwLDAsNC40Ni03LjIybC0zLjI0LTEzLjY3LDguODctNy42NWE2LDYsMCwxLDAtNy44NC05LjA5bC0xMS42MywxMEE2LDYsMCwwLDAsMTc0LjUzLDE1Mi4xNVptMjUuMjMsNTQuNDVhNiw2LDAsMSwwLTExLjY3LDIuNzdsMS4zMiw1LjU3YTIuMzUsMi4zNSwwLDAsMS0uOTUsMi41NywyLjI0LDIuMjQsMCwwLDEtMi42LjFsLTYuNzEtNC4xM2E2LDYsMCwxLDAtNi4yOSwxMC4yMmw2LjcyLDQuMTNhMTQuMzksMTQuMzksMCwwLDAsMjEuNS0xNS42NVpNMTc1LjUyLDk2bC0xNS42NC0xLjI2YTYsNiwwLDAsMS01LjA2LTMuNjlMMTM0LDQwLjd2MTQ1bDguMzksNS4xNWE2LDYsMCwwLDEtNi4yOSwxMC4yM2wtOC4xLTVMNzYuNDIsMjI3LjgzYTE0LjM5LDE0LjM5LDAsMCwxLTIxLjUtMTUuNjVsMTQtNTkuMjVMMjMsMTEzLjMyYTE0LjQzLDE0LjQzLDAsMCwxLTQuMy0xNS40NUExNC4xNywxNC4xNywwLDAsMSwzMS4yNCw4OGw2MC4yNS00Ljg3LDIzLjIyLTU2LjJhMTQuMzcsMTQuMzcsMCwwLDEsMjYuNTgsMGwyMy4yMiw1Ni4yLDEyLDFhNiw2LDAsMCwxLTEsMTJaTTEyMiwxODUuNzJWNDAuN0wxMDEuMTgsOTEuMDlhNiw2LDAsMCwxLTUuMDYsMy42OUwzMi4yLDk5Ljk0YTIuMjgsMi4yOCwwLDAsMC0yLjA3LDEuNjUsMi4zOCwyLjM4LDAsMCwwLC43MiwyLjY1bDQ4LjcsNDJhNiw2LDAsMCwxLDEuOTIsNS45Mkw2Ni41OSwyMTQuOTRhMi4zNSwyLjM1LDAsMCwwLDEsMi41NywyLjI0LDIuMjQsMCwwLDAsMi42LjFaIi8+PC9zdmc+)}.icon-upload{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzgsMTM2djY0YTE0LDE0LDAsMCwxLTE0LDE0SDMyYTE0LDE0LDAsMCwxLTE0LTE0VjEzNmExNCwxNCwwLDAsMSwxNC0xNEg4MGE2LDYsMCwwLDEsMCwxMkgzMmEyLDIsMCwwLDAtMiwydjY0YTIsMiwwLDAsMCwyLDJIMjI0YTIsMiwwLDAsMCwyLTJWMTM2YTIsMiwwLDAsMC0yLTJIMTc2YTYsNiwwLDAsMSwwLTEyaDQ4QTE0LDE0LDAsMCwxLDIzOCwxMzZaTTg0LjI0LDc2LjI0LDEyMiwzOC40OVYxMjhhNiw2LDAsMCwwLDEyLDBWMzguNDlsMzcuNzYsMzcuNzVhNiw2LDAsMCwwLDguNDgtOC40OGwtNDgtNDhhNiw2LDAsMCwwLTguNDgsMGwtNDgsNDhhNiw2LDAsMCwwLDguNDgsOC40OFpNMTk4LDE2OGExMCwxMCwwLDEsMC0xMCwxMEExMCwxMCwwLDAsMCwxOTgsMTY4WiIvPjwvc3ZnPg==);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzgsMTM2djY0YTE0LDE0LDAsMCwxLTE0LDE0SDMyYTE0LDE0LDAsMCwxLTE0LTE0VjEzNmExNCwxNCwwLDAsMSwxNC0xNEg4MGE2LDYsMCwwLDEsMCwxMkgzMmEyLDIsMCwwLDAtMiwydjY0YTIsMiwwLDAsMCwyLDJIMjI0YTIsMiwwLDAsMCwyLTJWMTM2YTIsMiwwLDAsMC0yLTJIMTc2YTYsNiwwLDAsMSwwLTEyaDQ4QTE0LDE0LDAsMCwxLDIzOCwxMzZaTTg0LjI0LDc2LjI0LDEyMiwzOC40OVYxMjhhNiw2LDAsMCwwLDEyLDBWMzguNDlsMzcuNzYsMzcuNzVhNiw2LDAsMCwwLDguNDgtOC40OGwtNDgtNDhhNiw2LDAsMCwwLTguNDgsMGwtNDgsNDhhNiw2LDAsMCwwLDguNDgsOC40OFpNMTk4LDE2OGExMCwxMCwwLDEsMC0xMCwxMEExMCwxMCwwLDAsMCwxOTgsMTY4WiIvPjwvc3ZnPg==)}.icon-sun{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMjIsNDBWMTZhNiw2LDAsMCwxLDEyLDBWNDBhNiw2LDAsMCwxLTEyLDBabTY4LDg4YTYyLDYyLDAsMSwxLTYyLTYyQTYyLjA3LDYyLjA3LDAsMCwxLDE5MCwxMjhabS0xMiwwYTUwLDUwLDAsMSwwLTUwLDUwQTUwLjA2LDUwLjA2LDAsMCwwLDE3OCwxMjhaTTU5Ljc2LDY4LjI0YTYsNiwwLDEsMCw4LjQ4LTguNDhsLTE2LTE2YTYsNiwwLDAsMC04LjQ4LDguNDhabTAsMTE5LjUyLTE2LDE2YTYsNiwwLDEsMCw4LjQ4LDguNDhsMTYtMTZhNiw2LDAsMSwwLTguNDgtOC40OFpNMTkyLDcwYTYsNiwwLDAsMCw0LjI0LTEuNzZsMTYtMTZhNiw2LDAsMCwwLTguNDgtOC40OGwtMTYsMTZBNiw2LDAsMCwwLDE5Miw3MFptNC4yNCwxMTcuNzZhNiw2LDAsMCwwLTguNDgsOC40OGwxNiwxNmE2LDYsMCwwLDAsOC40OC04LjQ4Wk00NiwxMjhhNiw2LDAsMCwwLTYtNkgxNmE2LDYsMCwwLDAsMCwxMkg0MEE2LDYsMCwwLDAsNDYsMTI4Wm04Miw4MmE2LDYsMCwwLDAtNiw2djI0YTYsNiwwLDAsMCwxMiwwVjIxNkE2LDYsMCwwLDAsMTI4LDIxMFptMTEyLTg4SDIxNmE2LDYsMCwwLDAsMCwxMmgyNGE2LDYsMCwwLDAsMC0xMloiLz48L3N2Zz4=);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMjIsNDBWMTZhNiw2LDAsMCwxLDEyLDBWNDBhNiw2LDAsMCwxLTEyLDBabTY4LDg4YTYyLDYyLDAsMSwxLTYyLTYyQTYyLjA3LDYyLjA3LDAsMCwxLDE5MCwxMjhabS0xMiwwYTUwLDUwLDAsMSwwLTUwLDUwQTUwLjA2LDUwLjA2LDAsMCwwLDE3OCwxMjhaTTU5Ljc2LDY4LjI0YTYsNiwwLDEsMCw4LjQ4LTguNDhsLTE2LTE2YTYsNiwwLDAsMC04LjQ4LDguNDhabTAsMTE5LjUyLTE2LDE2YTYsNiwwLDEsMCw4LjQ4LDguNDhsMTYtMTZhNiw2LDAsMSwwLTguNDgtOC40OFpNMTkyLDcwYTYsNiwwLDAsMCw0LjI0LTEuNzZsMTYtMTZhNiw2LDAsMCwwLTguNDgtOC40OGwtMTYsMTZBNiw2LDAsMCwwLDE5Miw3MFptNC4yNCwxMTcuNzZhNiw2LDAsMCwwLTguNDgsOC40OGwxNiwxNmE2LDYsMCwwLDAsOC40OC04LjQ4Wk00NiwxMjhhNiw2LDAsMCwwLTYtNkgxNmE2LDYsMCwwLDAsMCwxMkg0MEE2LDYsMCwwLDAsNDYsMTI4Wm04Miw4MmE2LDYsMCwwLDAtNiw2djI0YTYsNiwwLDAsMCwxMiwwVjIxNkE2LDYsMCwwLDAsMTI4LDIxMFptMTEyLTg4SDIxNmE2LDYsMCwwLDAsMCwxMmgyNGE2LDYsMCwwLDAsMC0xMloiLz48L3N2Zz4=)}.icon-moon{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzIuMTMsMTQzLjY0YTYsNiwwLDAsMC02LTEuNDlBOTAuMDcsOTAuMDcsMCwwLDEsMTEzLjg2LDI5Ljg1YTYsNiwwLDAsMC03LjQ5LTcuNDhBMTAyLjg4LDEwMi44OCwwLDAsMCw1NC40OCw1OC42OCwxMDIsMTAyLDAsMCwwLDE5Ny4zMiwyMDEuNTJhMTAyLjg4LDEwMi44OCwwLDAsMCwzNi4zMS01MS44OUE2LDYsMCwwLDAsMjMyLjEzLDE0My42NFptLTQyLDQ4LjI5YTkwLDkwLDAsMCwxLTEyNi0xMjZBOTAuOSw5MC45LDAsMCwxLDk5LjY1LDM3LjY2LDEwMi4wNiwxMDIuMDYsMCwwLDAsMjE4LjM0LDE1Ni4zNSw5MC45LDkwLjksMCwwLDEsMTkwLjEsMTkxLjkzWiIvPjwvc3ZnPg==);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzIuMTMsMTQzLjY0YTYsNiwwLDAsMC02LTEuNDlBOTAuMDcsOTAuMDcsMCwwLDEsMTEzLjg2LDI5Ljg1YTYsNiwwLDAsMC03LjQ5LTcuNDhBMTAyLjg4LDEwMi44OCwwLDAsMCw1NC40OCw1OC42OCwxMDIsMTAyLDAsMCwwLDE5Ny4zMiwyMDEuNTJhMTAyLjg4LDEwMi44OCwwLDAsMCwzNi4zMS01MS44OUE2LDYsMCwwLDAsMjMyLjEzLDE0My42NFptLTQyLDQ4LjI5YTkwLDkwLDAsMCwxLTEyNi0xMjZBOTAuOSw5MC45LDAsMCwxLDk5LjY1LDM3LjY2LDEwMi4wNiwxMDIuMDYsMCwwLDAsMjE4LjM0LDE1Ni4zNSw5MC45LDkwLjksMCwwLDEsMTkwLjEsMTkxLjkzWiIvPjwvc3ZnPg==)}.icon-x{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMDQuMjQsMTk1Ljc2YTYsNiwwLDEsMS04LjQ4LDguNDhMMTI4LDEzNi40OSw2MC4yNCwyMDQuMjRhNiw2LDAsMCwxLTguNDgtOC40OEwxMTkuNTEsMTI4LDUxLjc2LDYwLjI0YTYsNiwwLDAsMSw4LjQ4LTguNDhMMTI4LDExOS41MWw2Ny43Ni02Ny43NWE2LDYsMCwwLDEsOC40OCw4LjQ4TDEzNi40OSwxMjhaIi8+PC9zdmc+);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMDQuMjQsMTk1Ljc2YTYsNiwwLDEsMS04LjQ4LDguNDhMMTI4LDEzNi40OSw2MC4yNCwyMDQuMjRhNiw2LDAsMCwxLTguNDgtOC40OEwxMTkuNTEsMTI4LDUxLjc2LDYwLjI0YTYsNiwwLDAsMSw4LjQ4LTguNDhMMTI4LDExOS41MWw2Ny43Ni02Ny43NWE2LDYsMCwwLDEsOC40OCw4LjQ4TDEzNi40OSwxMjhaIi8+PC9zdmc+)}.icon-question{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMzgsMTgwYTEwLDEwLDAsMSwxLTEwLTEwQTEwLDEwLDAsMCwxLDEzOCwxODBaTTEyOCw3NGMtMjEsMC0zOCwxNS4yNS0zOCwzNHY0YTYsNiwwLDAsMCwxMiwwdi00YzAtMTIuMTMsMTEuNjYtMjIsMjYtMjJzMjYsOS44NywyNiwyMi0xMS42NiwyMi0yNiwyMmE2LDYsMCwwLDAtNiw2djhhNiw2LDAsMCwwLDEyLDB2LTIuNDJjMTguMTEtMi41OCwzMi0xNi42NiwzMi0zMy41OEMxNjYsODkuMjUsMTQ5LDc0LDEyOCw3NFptMTAyLDU0QTEwMiwxMDIsMCwxLDEsMTI4LDI2LDEwMi4xMiwxMDIuMTIsMCwwLDEsMjMwLDEyOFptLTEyLDBhOTAsOTAsMCwxLDAtOTAsOTBBOTAuMSw5MC4xLDAsMCwwLDIxOCwxMjhaIi8+PC9zdmc+);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMzgsMTgwYTEwLDEwLDAsMSwxLTEwLTEwQTEwLDEwLDAsMCwxLDEzOCwxODBaTTEyOCw3NGMtMjEsMC0zOCwxNS4yNS0zOCwzNHY0YTYsNiwwLDAsMCwxMiwwdi00YzAtMTIuMTMsMTEuNjYtMjIsMjYtMjJzMjYsOS44NywyNiwyMi0xMS42NiwyMi0yNiwyMmE2LDYsMCwwLDAtNiw2djhhNiw2LDAsMCwwLDEyLDB2LTIuNDJjMTguMTEtMi41OCwzMi0xNi42NiwzMi0zMy41OEMxNjYsODkuMjUsMTQ5LDc0LDEyOCw3NFptMTAyLDU0QTEwMiwxMDIsMCwxLDEsMTI4LDI2LDEwMi4xMiwxMDIuMTIsMCwwLDEsMjMwLDEyOFptLTEyLDBhOTAsOTAsMCwxLDAtOTAsOTBBOTAuMSw5MC4xLDAsMCwwLDIxOCwxMjhaIi8+PC9zdmc+)}.icon-arrows-left-right{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTIuMjQsMTcxLjc2YTYsNiwwLDAsMSwwLDguNDhsLTMyLDMyYTYsNiwwLDAsMS04LjQ4LTguNDhMMTkzLjUxLDE4Mkg0OGE2LDYsMCwwLDEsMC0xMkgxOTMuNTFsLTIxLjc1LTIxLjc2YTYsNiwwLDAsMSw4LjQ4LTguNDhaTTc1Ljc2LDExNi4yNGE2LDYsMCwwLDAsOC40OC04LjQ4TDYyLjQ5LDg2SDIwOGE2LDYsMCwwLDAsMC0xMkg2Mi40OUw4NC4yNCw1Mi4yNGE2LDYsMCwwLDAtOC40OC04LjQ4bC0zMiwzMmE2LDYsMCwwLDAsMCw4LjQ4WiIvPjwvc3ZnPg==);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTIuMjQsMTcxLjc2YTYsNiwwLDAsMSwwLDguNDhsLTMyLDMyYTYsNiwwLDAsMS04LjQ4LTguNDhMMTkzLjUxLDE4Mkg0OGE2LDYsMCwwLDEsMC0xMkgxOTMuNTFsLTIxLjc1LTIxLjc2YTYsNiwwLDAsMSw4LjQ4LTguNDhaTTc1Ljc2LDExNi4yNGE2LDYsMCwwLDAsOC40OC04LjQ4TDYyLjQ5LDg2SDIwOGE2LDYsMCwwLDAsMC0xMkg2Mi40OUw4NC4yNCw1Mi4yNGE2LDYsMCwwLDAtOC40OC04LjQ4bC0zMiwzMmE2LDYsMCwwLDAsMCw4LjQ4WiIvPjwvc3ZnPg==)}.icon-asterisk{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTMuMTQsMTc5LjA5YTYsNiwwLDAsMS04LjIzLDIuMDZMMTM0LDEzOC42VjIxNmE2LDYsMCwwLDEtMTIsMFYxMzguNkw1MS4wOSwxODEuMTVBNi4wNyw2LjA3LDAsMCwxLDQ4LDE4MmE2LDYsMCwwLDEtMy4xLTExLjE1TDExNi4zNCwxMjgsNDQuOTEsODUuMTVhNiw2LDAsMCwxLDYuMTgtMTAuM0wxMjIsMTE3LjRWNDBhNiw2LDAsMCwxLDEyLDB2NzcuNGw3MC45MS00Mi41NWE2LDYsMCwwLDEsNi4xOCwxMC4zTDEzOS42NiwxMjhsNzEuNDMsNDIuODVBNiw2LDAsMCwxLDIxMy4xNCwxNzkuMDlaIi8+PC9zdmc+);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTMuMTQsMTc5LjA5YTYsNiwwLDAsMS04LjIzLDIuMDZMMTM0LDEzOC42VjIxNmE2LDYsMCwwLDEtMTIsMFYxMzguNkw1MS4wOSwxODEuMTVBNi4wNyw2LjA3LDAsMCwxLDQ4LDE4MmE2LDYsMCwwLDEtMy4xLTExLjE1TDExNi4zNCwxMjgsNDQuOTEsODUuMTVhNiw2LDAsMCwxLDYuMTgtMTAuM0wxMjIsMTE3LjRWNDBhNiw2LDAsMCwxLDEyLDB2NzcuNGw3MC45MS00Mi41NWE2LDYsMCwwLDEsNi4xOCwxMC4zTDEzOS42NiwxMjhsNzEuNDMsNDIuODVBNiw2LDAsMCwxLDIxMy4xNCwxNzkuMDlaIi8+PC9zdmc+)}.icon-cake{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzAsMTEyYTIyLDIyLDAsMCwwLTIyLTIySDEzNFY3Ny40QTMwLjA1LDMwLjA1LDAsMCwwLDE1OCw0OGMwLTI2LjktMjUuNTgtNDQuMjctMjYuNjctNDVhNiw2LDAsMCwwLTYuNjYsMEMxMjMuNTgsMy43Myw5OCwyMS4xLDk4LDQ4YTMwLjA1LDMwLjA1LDAsMCwwLDI0LDI5LjRWOTBINDhhMjIsMjIsMCwwLDAtMjIsMjJ2MjMuMzNhMzguODEsMzguODEsMCwwLDAsOCwyMy41N1YyMDBhMjIsMjIsMCwwLDAsMjIsMjJIMjAwYTIyLDIyLDAsMCwwLDIyLTIyVjE1OC45YTM4LjgxLDM4LjgxLDAsMCwwLDgtMjMuNTdaTTExMCw0OGMwLTE1LjQsMTItMjcuNDEsMTgtMzIuNDQsNiw1LDE4LDE3LDE4LDMyLjQ0YTE4LDE4LDAsMCwxLTM2LDBaTTM4LDExMmExMCwxMCwwLDAsMSwxMC0xMEgyMDhhMTAsMTAsMCwwLDEsMTAsMTB2MjMuMzNjMCwxNC4zMi0xMS4zMywyNi4yOC0yNS4yNiwyNi42NkEyNiwyNiwwLDAsMSwxNjYsMTM2YTYsNiwwLDAsMC0xMiwwLDI2LDI2LDAsMCwxLTUyLDAsNiw2LDAsMCwwLTEyLDAsMjYsMjYsMCwwLDEtMjYuNzQsMjZDNDkuMzMsMTYxLjYxLDM4LDE0OS42NSwzOCwxMzUuMzNabTE2Miw5OEg1NmExMCwxMCwwLDAsMS0xMC0xMFYxNjkuMzFBMzYuOCwzNi44LDAsMCwwLDYyLjk0LDE3NCwzNy44MiwzNy44MiwwLDAsMCw5MC41LDE2My4yNCwzOC44MywzOC44MywwLDAsMCw5NiwxNTYuNWEzOCwzOCwwLDAsMCw2NCwwLDM4LjgzLDM4LjgzLDAsMCwwLDUuNTIsNi43NEEzNy43MywzNy43MywwLDAsMCwxOTIsMTc0aDEuMDdBMzYuOCwzNi44LDAsMCwwLDIxMCwxNjkuMzFWMjAwQTEwLDEwLDAsMCwxLDIwMCwyMTBaIi8+PC9zdmc+);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzAsMTEyYTIyLDIyLDAsMCwwLTIyLTIySDEzNFY3Ny40QTMwLjA1LDMwLjA1LDAsMCwwLDE1OCw0OGMwLTI2LjktMjUuNTgtNDQuMjctMjYuNjctNDVhNiw2LDAsMCwwLTYuNjYsMEMxMjMuNTgsMy43Myw5OCwyMS4xLDk4LDQ4YTMwLjA1LDMwLjA1LDAsMCwwLDI0LDI5LjRWOTBINDhhMjIsMjIsMCwwLDAtMjIsMjJ2MjMuMzNhMzguODEsMzguODEsMCwwLDAsOCwyMy41N1YyMDBhMjIsMjIsMCwwLDAsMjIsMjJIMjAwYTIyLDIyLDAsMCwwLDIyLTIyVjE1OC45YTM4LjgxLDM4LjgxLDAsMCwwLDgtMjMuNTdaTTExMCw0OGMwLTE1LjQsMTItMjcuNDEsMTgtMzIuNDQsNiw1LDE4LDE3LDE4LDMyLjQ0YTE4LDE4LDAsMCwxLTM2LDBaTTM4LDExMmExMCwxMCwwLDAsMSwxMC0xMEgyMDhhMTAsMTAsMCwwLDEsMTAsMTB2MjMuMzNjMCwxNC4zMi0xMS4zMywyNi4yOC0yNS4yNiwyNi42NkEyNiwyNiwwLDAsMSwxNjYsMTM2YTYsNiwwLDAsMC0xMiwwLDI2LDI2LDAsMCwxLTUyLDAsNiw2LDAsMCwwLTEyLDAsMjYsMjYsMCwwLDEtMjYuNzQsMjZDNDkuMzMsMTYxLjYxLDM4LDE0OS42NSwzOCwxMzUuMzNabTE2Miw5OEg1NmExMCwxMCwwLDAsMS0xMC0xMFYxNjkuMzFBMzYuOCwzNi44LDAsMCwwLDYyLjk0LDE3NCwzNy44MiwzNy44MiwwLDAsMCw5MC41LDE2My4yNCwzOC44MywzOC44MywwLDAsMCw5NiwxNTYuNWEzOCwzOCwwLDAsMCw2NCwwLDM4LjgzLDM4LjgzLDAsMCwwLDUuNTIsNi43NEEzNy43MywzNy43MywwLDAsMCwxOTIsMTc0aDEuMDdBMzYuOCwzNi44LDAsMCwwLDIxMCwxNjkuMzFWMjAwQTEwLDEwLDAsMCwxLDIwMCwyMTBaIi8+PC9zdmc+)}.icon-person{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMjgsNzBBMzAsMzAsMCwxLDAsOTgsNDAsMzAsMzAsMCwwLDAsMTI4LDcwWm0wLTQ4YTE4LDE4LDAsMSwxLTE4LDE4QTE4LDE4LDAsMCwxLDEyOCwyMlptODguODgsMTEzLjQyTDE3MS42Nyw4NC4xNkEzMCwzMCwwLDAsMCwxNDkuMTcsNzRIMTA2LjgzYTMwLDMwLDAsMCwwLTIyLjUsMTAuMTVMMzkuMTIsMTM1LjQyQTE4LDE4LDAsMCwwLDY0LjQ2LDE2MWwyMS4xMS0xNi45M0w2Ny40NCwyMTIuOTJhMTgsMTgsMCwwLDAsMzIuNzUsMTQuOTRMMTI4LDE4MGwyNy44MSw0Ny45MWExOCwxOCwwLDAsMCwzMi43NS0xNC45NGwtMTguMTMtNjguODdMMTkxLjU0LDE2MWExOCwxOCwwLDAsMCwyNS4zNC0yNS41NlptLTguNjMsMTYuODJhNiw2LDAsMCwxLTguNDksMCw0LjE1LDQuMTUsMCwwLDAtLjQ5LS40NGwtMzUuNTEtMjguNDhhNiw2LDAsMCwwLTkuNTYsNi4ybDIyLjg3LDg2LjkzYTcuNjYsNy42NiwwLDAsMCwuMzcsMSw2LDYsMCwwLDEtMTAuODgsNS4wNyw0LjM3LDQuMzcsMCwwLDAtLjI1LS40OEwxMzMuMTksMTY1YTYsNiwwLDAsMC0xMC4zOCwwTDg5LjY5LDIyMi4wNWE0LjM3LDQuMzcsMCwwLDAtLjI1LjQ4LDYsNiwwLDAsMS0xMC44OC01LjA3LDcuNjYsNy42NiwwLDAsMCwuMzctMWwyMi44Ny04Ni45M0E2LDYsMCwwLDAsOTkuMjcsMTIzLDYuMDcsNi4wNywwLDAsMCw5NiwxMjJhNiw2LDAsMCwwLTMuNzYsMS4zMkw1Ni43MywxNTEuOGE0LjE1LDQuMTUsMCwwLDAtLjQ5LjQ0LDYsNiwwLDAsMS04LjQ5LTguNDlsLjI2LS4yN0w5My4zMyw5Mi4wOUExOCwxOCwwLDAsMSwxMDYuODMsODZoNDIuMzRhMTgsMTgsMCwwLDEsMTMuNSw2LjA5TDIwOCwxNDMuNDhsLjI2LjI3QTYsNiwwLDAsMSwyMDguMjUsMTUyLjI0WiIvPjwvc3ZnPg==);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMjgsNzBBMzAsMzAsMCwxLDAsOTgsNDAsMzAsMzAsMCwwLDAsMTI4LDcwWm0wLTQ4YTE4LDE4LDAsMSwxLTE4LDE4QTE4LDE4LDAsMCwxLDEyOCwyMlptODguODgsMTEzLjQyTDE3MS42Nyw4NC4xNkEzMCwzMCwwLDAsMCwxNDkuMTcsNzRIMTA2LjgzYTMwLDMwLDAsMCwwLTIyLjUsMTAuMTVMMzkuMTIsMTM1LjQyQTE4LDE4LDAsMCwwLDY0LjQ2LDE2MWwyMS4xMS0xNi45M0w2Ny40NCwyMTIuOTJhMTgsMTgsMCwwLDAsMzIuNzUsMTQuOTRMMTI4LDE4MGwyNy44MSw0Ny45MWExOCwxOCwwLDAsMCwzMi43NS0xNC45NGwtMTguMTMtNjguODdMMTkxLjU0LDE2MWExOCwxOCwwLDAsMCwyNS4zNC0yNS41NlptLTguNjMsMTYuODJhNiw2LDAsMCwxLTguNDksMCw0LjE1LDQuMTUsMCwwLDAtLjQ5LS40NGwtMzUuNTEtMjguNDhhNiw2LDAsMCwwLTkuNTYsNi4ybDIyLjg3LDg2LjkzYTcuNjYsNy42NiwwLDAsMCwuMzcsMSw2LDYsMCwwLDEtMTAuODgsNS4wNyw0LjM3LDQuMzcsMCwwLDAtLjI1LS40OEwxMzMuMTksMTY1YTYsNiwwLDAsMC0xMC4zOCwwTDg5LjY5LDIyMi4wNWE0LjM3LDQuMzcsMCwwLDAtLjI1LjQ4LDYsNiwwLDAsMS0xMC44OC01LjA3LDcuNjYsNy42NiwwLDAsMCwuMzctMWwyMi44Ny04Ni45M0E2LDYsMCwwLDAsOTkuMjcsMTIzLDYuMDcsNi4wNywwLDAsMCw5NiwxMjJhNiw2LDAsMCwwLTMuNzYsMS4zMkw1Ni43MywxNTEuOGE0LjE1LDQuMTUsMCwwLDAtLjQ5LjQ0LDYsNiwwLDAsMS04LjQ5LTguNDlsLjI2LS4yN0w5My4zMyw5Mi4wOUExOCwxOCwwLDAsMSwxMDYuODMsODZoNDIuMzRhMTgsMTgsMCwwLDEsMTMuNSw2LjA5TDIwOCwxNDMuNDhsLjI2LjI3QTYsNiwwLDAsMSwyMDguMjUsMTUyLjI0WiIvPjwvc3ZnPg==)}.icon-crosshair{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzIsMTIySDIyMS44QTk0LjEzLDk0LjEzLDAsMCwwLDEzNCwzNC4yVjI0YTYsNiwwLDAsMC0xMiwwVjM0LjJBOTQuMTMsOTQuMTMsMCwwLDAsMzQuMiwxMjJIMjRhNiw2LDAsMCwwLDAsMTJIMzQuMkE5NC4xMyw5NC4xMywwLDAsMCwxMjIsMjIxLjhWMjMyYTYsNiwwLDAsMCwxMiwwVjIyMS44QTk0LjEzLDk0LjEzLDAsMCwwLDIyMS44LDEzNEgyMzJhNiw2LDAsMCwwLDAtMTJabS05OCw4Ny43NlYyMDBhNiw2LDAsMCwwLTEyLDB2OS43NkE4Mi4wOSw4Mi4wOSwwLDAsMSw0Ni4yNCwxMzRINTZhNiw2LDAsMCwwLDAtMTJINDYuMjRBODIuMDksODIuMDksMCwwLDEsMTIyLDQ2LjI0VjU2YTYsNiwwLDAsMCwxMiwwVjQ2LjI0QTgyLjA5LDgyLjA5LDAsMCwxLDIwOS43NiwxMjJIMjAwYTYsNiwwLDAsMCwwLDEyaDkuNzZBODIuMDksODIuMDksMCwwLDEsMTM0LDIwOS43NlpNMTI4LDkwYTM4LDM4LDAsMSwwLDM4LDM4QTM4LDM4LDAsMCwwLDEyOCw5MFptMCw2NGEyNiwyNiwwLDEsMSwyNi0yNkEyNiwyNiwwLDAsMSwxMjgsMTU0WiIvPjwvc3ZnPg==);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzIsMTIySDIyMS44QTk0LjEzLDk0LjEzLDAsMCwwLDEzNCwzNC4yVjI0YTYsNiwwLDAsMC0xMiwwVjM0LjJBOTQuMTMsOTQuMTMsMCwwLDAsMzQuMiwxMjJIMjRhNiw2LDAsMCwwLDAsMTJIMzQuMkE5NC4xMyw5NC4xMywwLDAsMCwxMjIsMjIxLjhWMjMyYTYsNiwwLDAsMCwxMiwwVjIyMS44QTk0LjEzLDk0LjEzLDAsMCwwLDIyMS44LDEzNEgyMzJhNiw2LDAsMCwwLDAtMTJabS05OCw4Ny43NlYyMDBhNiw2LDAsMCwwLTEyLDB2OS43NkE4Mi4wOSw4Mi4wOSwwLDAsMSw0Ni4yNCwxMzRINTZhNiw2LDAsMCwwLDAtMTJINDYuMjRBODIuMDksODIuMDksMCwwLDEsMTIyLDQ2LjI0VjU2YTYsNiwwLDAsMCwxMiwwVjQ2LjI0QTgyLjA5LDgyLjA5LDAsMCwxLDIwOS43NiwxMjJIMjAwYTYsNiwwLDAsMCwwLDEyaDkuNzZBODIuMDksODIuMDksMCwwLDEsMTM0LDIwOS43NlpNMTI4LDkwYTM4LDM4LDAsMSwwLDM4LDM4QTM4LDM4LDAsMCwwLDEyOCw5MFptMCw2NGEyNiwyNiwwLDEsMSwyNi0yNkEyNiwyNiwwLDAsMSwxMjgsMTU0WiIvPjwvc3ZnPg==)}.icon-dots-six{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik03MCw5MkExMCwxMCwwLDEsMSw2MCw4MiwxMCwxMCwwLDAsMSw3MCw5MlptNTgtMTBhMTAsMTAsMCwxLDAsMTAsMTBBMTAsMTAsMCwwLDAsMTI4LDgyWm02OCwyMGExMCwxMCwwLDEsMC0xMC0xMEExMCwxMCwwLDAsMCwxOTYsMTAyWk02MCwxNTRhMTAsMTAsMCwxLDAsMTAsMTBBMTAsMTAsMCwwLDAsNjAsMTU0Wm02OCwwYTEwLDEwLDAsMSwwLDEwLDEwQTEwLDEwLDAsMCwwLDEyOCwxNTRabTY4LDBhMTAsMTAsMCwxLDAsMTAsMTBBMTAsMTAsMCwwLDAsMTk2LDE1NFoiLz48L3N2Zz4=);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik03MCw5MkExMCwxMCwwLDEsMSw2MCw4MiwxMCwxMCwwLDAsMSw3MCw5MlptNTgtMTBhMTAsMTAsMCwxLDAsMTAsMTBBMTAsMTAsMCwwLDAsMTI4LDgyWm02OCwyMGExMCwxMCwwLDEsMC0xMC0xMEExMCwxMCwwLDAsMCwxOTYsMTAyWk02MCwxNTRhMTAsMTAsMCwxLDAsMTAsMTBBMTAsMTAsMCwwLDAsNjAsMTU0Wm02OCwwYTEwLDEwLDAsMSwwLDEwLDEwQTEwLDEwLDAsMCwwLDEyOCwxNTRabTY4LDBhMTAsMTAsMCwxLDAsMTAsMTBBMTAsMTAsMCwwLDAsMTk2LDE1NFoiLz48L3N2Zz4=)}.icon-hourglass{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xOTgsNzUuNjRWNDBhMTQsMTQsMCwwLDAtMTQtMTRINzJBMTQsMTQsMCwwLDAsNTgsNDBWNzZhMTQuMDYsMTQuMDYsMCwwLDAsNS42LDExLjJMMTE4LDEyOCw2My42LDE2OC44QTE0LjA2LDE0LjA2LDAsMCwwLDU4LDE4MHYzNmExNCwxNCwwLDAsMCwxNCwxNEgxODRhMTQsMTQsMCwwLDAsMTQtMTRWMTgwLjM2YTE0LjA4LDE0LjA4LDAsMCwwLTUuNTYtMTEuMTdMMTM4LDEyOGw1NC40OS00MS4xOUExNC4wOCwxNC4wOCwwLDAsMCwxOTgsNzUuNjRaTTE4NiwxODAuMzZWMjE2YTIsMiwwLDAsMS0yLDJINzJhMiwyLDAsMCwxLTItMlYxODBhMiwyLDAsMCwxLC44LTEuNkwxMjgsMTM1LjUxbDU3LjIyLDQzLjI1QTIsMiwwLDAsMSwxODYsMTgwLjM2Wm0wLTEwNC43MmEyLDIsMCwwLDEtLjc5LDEuNkwxMjgsMTIwLjQ5LDcwLjgsNzcuNkEyLDIsMCwwLDEsNzAsNzZWNDBhMiwyLDAsMCwxLDItMkgxODRhMiwyLDAsMCwxLDIsMloiLz48L3N2Zz4=);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xOTgsNzUuNjRWNDBhMTQsMTQsMCwwLDAtMTQtMTRINzJBMTQsMTQsMCwwLDAsNTgsNDBWNzZhMTQuMDYsMTQuMDYsMCwwLDAsNS42LDExLjJMMTE4LDEyOCw2My42LDE2OC44QTE0LjA2LDE0LjA2LDAsMCwwLDU4LDE4MHYzNmExNCwxNCwwLDAsMCwxNCwxNEgxODRhMTQsMTQsMCwwLDAsMTQtMTRWMTgwLjM2YTE0LjA4LDE0LjA4LDAsMCwwLTUuNTYtMTEuMTdMMTM4LDEyOGw1NC40OS00MS4xOUExNC4wOCwxNC4wOCwwLDAsMCwxOTgsNzUuNjRaTTE4NiwxODAuMzZWMjE2YTIsMiwwLDAsMS0yLDJINzJhMiwyLDAsMCwxLTItMlYxODBhMiwyLDAsMCwxLC44LTEuNkwxMjgsMTM1LjUxbDU3LjIyLDQzLjI1QTIsMiwwLDAsMSwxODYsMTgwLjM2Wm0wLTEwNC43MmEyLDIsMCwwLDEtLjc5LDEuNkwxMjgsMTIwLjQ5LDcwLjgsNzcuNkEyLDIsMCwwLDEsNzAsNzZWNDBhMiwyLDAsMCwxLDItMkgxODRhMiwyLDAsMCwxLDIsMloiLz48L3N2Zz4=)}.icon-folder{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTYsNzRIMTMwLjQ5bC0yNy45LTI3LjlhMTMuOTQsMTMuOTQsMCwwLDAtOS45LTQuMUg0MEExNCwxNCwwLDAsMCwyNiw1NlYyMDAuNjJBMTMuMzksMTMuMzksMCwwLDAsMzkuMzgsMjE0SDIxNi44OUExMy4xMiwxMy4xMiwwLDAsMCwyMzAsMjAwLjg5Vjg4QTE0LDE0LDAsMCwwLDIxNiw3NFpNNDAsNTRIOTIuNjlhMiwyLDAsMCwxLDEuNDEuNTlMMTEzLjUxLDc0SDM4VjU2QTIsMiwwLDAsMSw0MCw1NFpNMjE4LDIwMC44OWExLjExLDEuMTEsMCwwLDEtMS4xMSwxLjExSDM5LjM4QTEuNCwxLjQsMCwwLDEsMzgsMjAwLjYyVjg2SDIxNmEyLDIsMCwwLDEsMiwyWiIvPjwvc3ZnPg==);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTYsNzRIMTMwLjQ5bC0yNy45LTI3LjlhMTMuOTQsMTMuOTQsMCwwLDAtOS45LTQuMUg0MEExNCwxNCwwLDAsMCwyNiw1NlYyMDAuNjJBMTMuMzksMTMuMzksMCwwLDAsMzkuMzgsMjE0SDIxNi44OUExMy4xMiwxMy4xMiwwLDAsMCwyMzAsMjAwLjg5Vjg4QTE0LDE0LDAsMCwwLDIxNiw3NFpNNDAsNTRIOTIuNjlhMiwyLDAsMCwxLDEuNDEuNTlMMTEzLjUxLDc0SDM4VjU2QTIsMiwwLDAsMSw0MCw1NFpNMjE4LDIwMC44OWExLjExLDEuMTEsMCwwLDEtMS4xMSwxLjExSDM5LjM4QTEuNCwxLjQsMCwwLDEsMzgsMjAwLjYyVjg2SDIxNmEyLDIsMCwwLDEsMiwyWiIvPjwvc3ZnPg==)}.icon-envelope{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjQsNTBIMzJhNiw2LDAsMCwwLTYsNlYxOTJhMTQsMTQsMCwwLDAsMTQsMTRIMjE2YTE0LDE0LDAsMCwwLDE0LTE0VjU2QTYsNiwwLDAsMCwyMjQsNTBabS05Niw4NS44Nkw0Ny40Miw2MkgyMDguNThaTTEwMS42NywxMjgsMzgsMTg2LjM2VjY5LjY0Wm04Ljg4LDguMTRMMTI0LDE0OC40MmE2LDYsMCwwLDAsOC4xLDBsMTMuNC0xMi4yOEwyMDguNTgsMTk0SDQ3LjQzWk0xNTQuMzMsMTI4LDIxOCw2OS42NFYxODYuMzZaIi8+PC9zdmc+);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjQsNTBIMzJhNiw2LDAsMCwwLTYsNlYxOTJhMTQsMTQsMCwwLDAsMTQsMTRIMjE2YTE0LDE0LDAsMCwwLDE0LTE0VjU2QTYsNiwwLDAsMCwyMjQsNTBabS05Niw4NS44Nkw0Ny40Miw2MkgyMDguNThaTTEwMS42NywxMjgsMzgsMTg2LjM2VjY5LjY0Wm04Ljg4LDguMTRMMTI0LDE0OC40MmE2LDYsMCwwLDAsOC4xLDBsMTMuNC0xMi4yOEwyMDguNTgsMTk0SDQ3LjQzWk0xNTQuMzMsMTI4LDIxOCw2OS42NFYxODYuMzZaIi8+PC9zdmc+)}.icon-hand-heart{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjkuMTIsMTQyLjY1YTIyLjQzLDIyLjQzLDAsMCwwLTE5LjU1LTMuODhsLTQuMzIsMUMyMjcsMTE5LjU1LDIzOCw5OS41MSwyMzgsODBjMC0yNS4zNi0yMC4zOS00Ni00NS40Ni00NkE0NS41MSw0NS41MSwwLDAsMCwxNTYsNTJhNDUuNTEsNDUuNTEsMCwwLDAtMzYuNTQtMThDOTQuMzksMzQsNzQsNTQuNjQsNzQsODBjMCwxMS4zOCwzLjYzLDIyLjQ5LDExLjI5LDM0LjM2YTI5LjczLDI5LjczLDAsMCwwLTE2LjU2LDguNDNMNDUuNTIsMTQ2SDE2QTE0LDE0LDAsMCwwLDIsMTYwdjQwYTE0LDE0LDAsMCwwLDE0LDE0SDEyMGE2LDYsMCwwLDAsMS40Ni0uMThsNjQtMTZhNy4xNiw3LjE2LDAsMCwwLC44OS0uM0wyMjUuMTcsMTgxbC4zMy0uMTVhMjIuNiwyMi42LDAsMCwwLDMuNjItMzguMThaTTExOS40Niw0NmEzMy4xNiwzMy4xNiwwLDAsMSwzMSwyMC4yOCw2LDYsMCwwLDAsMTEuMSwwLDMzLjE2LDMzLjE2LDAsMCwxLDMxLTIwLjI4QzIxMC42OCw0NiwyMjYsNjEuNTcsMjI2LDgwYzAsMjAuMjQtMTYuMTgsNDMtNDYuOCw2NS43NWwtMTQuODcsMy40MkEyNiwyNiwwLDAsMCwxNDAsMTE0SDk5LjY3QzkwLjM2LDEwMS42Nyw4Niw5MC44MSw4Niw4MCw4Niw2MS41NywxMDEuMzIsNDYsMTE5LjQ2LDQ2Wk0xNCwyMDBWMTYwYTIsMiwwLDAsMSwyLTJINDJ2NDRIMTZBMiwyLDAsMCwxLDE0LDIwMFptMjA2LjI4LTMwLTM4LjIsMTYuMjdMMTE5LjI2LDIwMkg1NFYxNTQuNDlsMjMuMjEtMjMuMjJBMTcuODgsMTcuODgsMCwwLDEsODkuOTQsMTI2SDE0MGExNCwxNCwwLDAsMSwwLDI4SDExMmE2LDYsMCwwLDAsMCwxMmgzMmE2LDYsMCwwLDAsMS4zNC0uMTVsNjctMTUuNDEuMjQtLjA2QTEwLjYsMTAuNiwwLDAsMSwyMjAuMjgsMTcwWiIvPjwvc3ZnPg==);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjkuMTIsMTQyLjY1YTIyLjQzLDIyLjQzLDAsMCwwLTE5LjU1LTMuODhsLTQuMzIsMUMyMjcsMTE5LjU1LDIzOCw5OS41MSwyMzgsODBjMC0yNS4zNi0yMC4zOS00Ni00NS40Ni00NkE0NS41MSw0NS41MSwwLDAsMCwxNTYsNTJhNDUuNTEsNDUuNTEsMCwwLDAtMzYuNTQtMThDOTQuMzksMzQsNzQsNTQuNjQsNzQsODBjMCwxMS4zOCwzLjYzLDIyLjQ5LDExLjI5LDM0LjM2YTI5LjczLDI5LjczLDAsMCwwLTE2LjU2LDguNDNMNDUuNTIsMTQ2SDE2QTE0LDE0LDAsMCwwLDIsMTYwdjQwYTE0LDE0LDAsMCwwLDE0LDE0SDEyMGE2LDYsMCwwLDAsMS40Ni0uMThsNjQtMTZhNy4xNiw3LjE2LDAsMCwwLC44OS0uM0wyMjUuMTcsMTgxbC4zMy0uMTVhMjIuNiwyMi42LDAsMCwwLDMuNjItMzguMThaTTExOS40Niw0NmEzMy4xNiwzMy4xNiwwLDAsMSwzMSwyMC4yOCw2LDYsMCwwLDAsMTEuMSwwLDMzLjE2LDMzLjE2LDAsMCwxLDMxLTIwLjI4QzIxMC42OCw0NiwyMjYsNjEuNTcsMjI2LDgwYzAsMjAuMjQtMTYuMTgsNDMtNDYuOCw2NS43NWwtMTQuODcsMy40MkEyNiwyNiwwLDAsMCwxNDAsMTE0SDk5LjY3QzkwLjM2LDEwMS42Nyw4Niw5MC44MSw4Niw4MCw4Niw2MS41NywxMDEuMzIsNDYsMTE5LjQ2LDQ2Wk0xNCwyMDBWMTYwYTIsMiwwLDAsMSwyLTJINDJ2NDRIMTZBMiwyLDAsMCwxLDE0LDIwMFptMjA2LjI4LTMwLTM4LjIsMTYuMjdMMTE5LjI2LDIwMkg1NFYxNTQuNDlsMjMuMjEtMjMuMjJBMTcuODgsMTcuODgsMCwwLDEsODkuOTQsMTI2SDE0MGExNCwxNCwwLDAsMSwwLDI4SDExMmE2LDYsMCwwLDAsMCwxMmgzMmE2LDYsMCwwLDAsMS4zNC0uMTVsNjctMTUuNDEuMjQtLjA2QTEwLjYsMTAuNiwwLDAsMSwyMjAuMjgsMTcwWiIvPjwvc3ZnPg==)}.icon-butterfly{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzEuMSw1MS43MUMyMjYuMDksNDUuMjcsMjE4LjY0LDQyLDIwOSw0MmMtMTYuMzMsMC0zNy40MSwxMS4wNi01Ni40LDI5LjU5QTEzMiwxMzIsMCwwLDAsMTM0LDkzLjk0VjU2YTYsNiwwLDAsMC0xMiwwVjkzLjk0YTEzMiwxMzIsMCwwLDAtMTguNTQtMjIuMzVDODQuNDksNTMuMDYsNjMuNCw0Miw0Ny4wNyw0MmMtOS42OSwwLTE3LjE0LDMuMjctMjIuMTUsOS43MS01LjUzLDcuMTEtNy43MSwxNy42OS02LjY2LDMyLjM0LjkxLDEyLjczLDQuMTIsMjYuNTMsNi44MSwzNy4xMyw2LjI4LDI0Ljc0LDIwLjc3LDMzLDMxLjc4LDM1LjY4QTQyLDQyLDAsMSwwLDEyOCwyMDEuNjJhNDIsNDIsMCwxLDAsNzEuMTYtNDQuNzZjMTEtMi42MywyNS41LTEwLjk0LDMxLjc4LTM1LjY4QzIzNy41NSw5NS4yMSwyNDIuNzIsNjYuNjUsMjMxLjEsNTEuNzFaTTkyLDIxMGEzMC4xMiwzMC4xMiwwLDAsMS0zLjM0LTYwQTYsNiwwLDAsMCw4Ny4zNSwxMzhhNDEuNzEsNDEuNzEsMCwwLDAtMjAuMjgsOGMtOSwuMzEtMjQuMTItMy4xNi0zMC4zNy0yNy43Ni0zLjI1LTEyLjgxLTExLjg5LTQ2LjgzLTIuMzEtNTkuMTVDMzcuMDUsNTUuNjYsNDEuMiw1NCw0Ny4wNyw1NGMxMi44OCwwLDMxLjcyLDEwLjI4LDQ4LDI2LjE4QzExMS42OSw5Ni4zOSwxMjIsMTE0LjU5LDEyMiwxMjcuNjdWMTgwQTMwLDMwLDAsMCwxLDkyLDIxMFptMTI3LjMxLTkxLjc3QzIxMy4wNywxNDIuODMsMTk4LDE0Ni4yOSwxODksMTQ2YTQxLjYyLDQxLjYyLDAsMCwwLTIwLjI4LThBNiw2LDAsMSwwLDE2Ny4zNiwxNTAsMzAuMTEsMzAuMTEsMCwxLDEsMTM0LDE4MFYxMjcuNjdjMC0xMy4wOCwxMC4zMi0zMS4yOCwyNi45My00Ny40OUMxNzcuMjMsNjQuMjgsMTk2LjA3LDU0LDIwOSw1NGM1Ljg3LDAsMTAsMS42NiwxMi42OCw1LjA4QzIzMS4yMSw3MS40LDIyMi41NywxMDUuNDIsMjE5LjMyLDExOC4yM1oiLz48L3N2Zz4=);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzEuMSw1MS43MUMyMjYuMDksNDUuMjcsMjE4LjY0LDQyLDIwOSw0MmMtMTYuMzMsMC0zNy40MSwxMS4wNi01Ni40LDI5LjU5QTEzMiwxMzIsMCwwLDAsMTM0LDkzLjk0VjU2YTYsNiwwLDAsMC0xMiwwVjkzLjk0YTEzMiwxMzIsMCwwLDAtMTguNTQtMjIuMzVDODQuNDksNTMuMDYsNjMuNCw0Miw0Ny4wNyw0MmMtOS42OSwwLTE3LjE0LDMuMjctMjIuMTUsOS43MS01LjUzLDcuMTEtNy43MSwxNy42OS02LjY2LDMyLjM0LjkxLDEyLjczLDQuMTIsMjYuNTMsNi44MSwzNy4xMyw2LjI4LDI0Ljc0LDIwLjc3LDMzLDMxLjc4LDM1LjY4QTQyLDQyLDAsMSwwLDEyOCwyMDEuNjJhNDIsNDIsMCwxLDAsNzEuMTYtNDQuNzZjMTEtMi42MywyNS41LTEwLjk0LDMxLjc4LTM1LjY4QzIzNy41NSw5NS4yMSwyNDIuNzIsNjYuNjUsMjMxLjEsNTEuNzFaTTkyLDIxMGEzMC4xMiwzMC4xMiwwLDAsMS0zLjM0LTYwQTYsNiwwLDAsMCw4Ny4zNSwxMzhhNDEuNzEsNDEuNzEsMCwwLDAtMjAuMjgsOGMtOSwuMzEtMjQuMTItMy4xNi0zMC4zNy0yNy43Ni0zLjI1LTEyLjgxLTExLjg5LTQ2LjgzLTIuMzEtNTkuMTVDMzcuMDUsNTUuNjYsNDEuMiw1NCw0Ny4wNyw1NGMxMi44OCwwLDMxLjcyLDEwLjI4LDQ4LDI2LjE4QzExMS42OSw5Ni4zOSwxMjIsMTE0LjU5LDEyMiwxMjcuNjdWMTgwQTMwLDMwLDAsMCwxLDkyLDIxMFptMTI3LjMxLTkxLjc3QzIxMy4wNywxNDIuODMsMTk4LDE0Ni4yOSwxODksMTQ2YTQxLjYyLDQxLjYyLDAsMCwwLTIwLjI4LThBNiw2LDAsMSwwLDE2Ny4zNiwxNTAsMzAuMTEsMzAuMTEsMCwxLDEsMTM0LDE4MFYxMjcuNjdjMC0xMy4wOCwxMC4zMi0zMS4yOCwyNi45My00Ny40OUMxNzcuMjMsNjQuMjgsMTk2LjA3LDU0LDIwOSw1NGM1Ljg3LDAsMTAsMS42NiwxMi42OCw1LjA4QzIzMS4yMSw3MS40LDIyMi41NywxMDUuNDIsMjE5LjMyLDExOC4yM1oiLz48L3N2Zz4=)}.icon-phone{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjEuNTksMTYwLjNsLTQ3LjI0LTIxLjE3YTE0LDE0LDAsMCwwLTEzLjI4LDEuMjIsNC44MSw0LjgxLDAsMCwwLS41Ni40MmwtMjQuNjksMjFhMS44OCwxLjg4LDAsMCwxLTEuNjguMDZjLTE1Ljg3LTcuNjYtMzIuMzEtMjQtNDAtMzkuNjVhMS45MSwxLjkxLDAsMCwxLDAtMS42OGwyMS4wNy0yNWE2LjEzLDYuMTMsMCwwLDAsLjQyLS41OCwxNCwxNCwwLDAsMCwxLjEyLTEzLjI3TDk1LjczLDM0LjQ5YTE0LDE0LDAsMCwwLTE0LjU2LTguMzhBNTQuMjQsNTQuMjQsMCwwLDAsMzQsODBjMCw3OC4zLDYzLjcsMTQyLDE0MiwxNDJhNTQuMjUsNTQuMjUsMCwwLDAsNTMuODktNDcuMTdBMTQsMTQsMCwwLDAsMjIxLjU5LDE2MC4zWk0xNzYsMjEwQzEwNC4zMiwyMTAsNDYsMTUxLjY4LDQ2LDgwQTQyLjIzLDQyLjIzLDAsMCwxLDgyLjY3LDM4aC4yM2EyLDIsMCwwLDEsMS44NCwxLjMxbDIxLjEsNDcuMTFhMiwyLDAsMCwxLDAsMS42N0w4NC43MywxMTMuMTVhNC43Myw0LjczLDAsMCwwLS40My41NywxNCwxNCwwLDAsMC0uOTEsMTMuNzNjOC44NywxOC4xNiwyNy4xNywzNi4zMiw0NS41Myw0NS4xOWExNCwxNCwwLDAsMCwxMy43Ny0xYy4xOS0uMTMuMzgtLjI3LjU2LS40MmwyNC42OC0yMWExLjkyLDEuOTIsMCwwLDEsMS42LS4xbDQ3LjI1LDIxLjE3YTIsMiwwLDAsMSwxLjIxLDJBNDIuMjQsNDIuMjQsMCwwLDEsMTc2LDIxMFoiLz48L3N2Zz4=);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjEuNTksMTYwLjNsLTQ3LjI0LTIxLjE3YTE0LDE0LDAsMCwwLTEzLjI4LDEuMjIsNC44MSw0LjgxLDAsMCwwLS41Ni40MmwtMjQuNjksMjFhMS44OCwxLjg4LDAsMCwxLTEuNjguMDZjLTE1Ljg3LTcuNjYtMzIuMzEtMjQtNDAtMzkuNjVhMS45MSwxLjkxLDAsMCwxLDAtMS42OGwyMS4wNy0yNWE2LjEzLDYuMTMsMCwwLDAsLjQyLS41OCwxNCwxNCwwLDAsMCwxLjEyLTEzLjI3TDk1LjczLDM0LjQ5YTE0LDE0LDAsMCwwLTE0LjU2LTguMzhBNTQuMjQsNTQuMjQsMCwwLDAsMzQsODBjMCw3OC4zLDYzLjcsMTQyLDE0MiwxNDJhNTQuMjUsNTQuMjUsMCwwLDAsNTMuODktNDcuMTdBMTQsMTQsMCwwLDAsMjIxLjU5LDE2MC4zWk0xNzYsMjEwQzEwNC4zMiwyMTAsNDYsMTUxLjY4LDQ2LDgwQTQyLjIzLDQyLjIzLDAsMCwxLDgyLjY3LDM4aC4yM2EyLDIsMCwwLDEsMS44NCwxLjMxbDIxLjEsNDcuMTFhMiwyLDAsMCwxLDAsMS42N0w4NC43MywxMTMuMTVhNC43Myw0LjczLDAsMCwwLS40My41NywxNCwxNCwwLDAsMC0uOTEsMTMuNzNjOC44NywxOC4xNiwyNy4xNywzNi4zMiw0NS41Myw0NS4xOWExNCwxNCwwLDAsMCwxMy43Ny0xYy4xOS0uMTMuMzgtLjI3LjU2LS40MmwyNC42OC0yMWExLjkyLDEuOTIsMCwwLDEsMS42LS4xbDQ3LjI1LDIxLjE3YTIsMiwwLDAsMSwxLjIxLDJBNDIuMjQsNDIuMjQsMCwwLDEsMTc2LDIxMFoiLz48L3N2Zz4=)}.icon-chat{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTYsNTBINDBBMTQsMTQsMCwwLDAsMjYsNjRWMjI0YTEzLjg4LDEzLjg4LDAsMCwwLDguMDksMTIuNjlBMTQuMTEsMTQuMTEsMCwwLDAsNDAsMjM4YTEzLjg3LDEzLjg3LDAsMCwwLDktMy4zMWwuMDYtLjA1TDgyLjIzLDIwNkgyMTZhMTQsMTQsMCwwLDAsMTQtMTRWNjRBMTQsMTQsMCwwLDAsMjE2LDUwWm0yLDE0MmEyLDIsMCwwLDEtMiwySDgwYTYsNiwwLDAsMC0zLjkyLDEuNDZMNDEuMjYsMjI1LjUzQTIsMiwwLDAsMSwzOCwyMjRWNjRhMiwyLDAsMCwxLDItMkgyMTZhMiwyLDAsMCwxLDIsMloiLz48L3N2Zz4=);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTYsNTBINDBBMTQsMTQsMCwwLDAsMjYsNjRWMjI0YTEzLjg4LDEzLjg4LDAsMCwwLDguMDksMTIuNjlBMTQuMTEsMTQuMTEsMCwwLDAsNDAsMjM4YTEzLjg3LDEzLjg3LDAsMCwwLDktMy4zMWwuMDYtLjA1TDgyLjIzLDIwNkgyMTZhMTQsMTQsMCwwLDAsMTQtMTRWNjRBMTQsMTQsMCwwLDAsMjE2LDUwWm0yLDE0MmEyLDIsMCwwLDEtMiwySDgwYTYsNiwwLDAsMC0zLjkyLDEuNDZMNDEuMjYsMjI1LjUzQTIsMiwwLDAsMSwzOCwyMjRWNjRhMiwyLDAsMCwxLDItMkgyMTZhMiwyLDAsMCwxLDIsMloiLz48L3N2Zz4=)}.icon-arrows-clockwise{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjIsNDhWOTZhNiw2LDAsMCwxLTYsNkgxNjhhNiw2LDAsMCwxLDAtMTJoMzMuNTJMMTgzLjQ3LDcyYTgxLjUxLDgxLjUxLDAsMCwwLTU3LjUzLTI0aC0uNDZBODEuNSw4MS41LDAsMCwwLDY4LjE5LDcxLjI4YTYsNiwwLDEsMS04LjM4LTguNTgsOTMuMzgsOTMuMzgsMCwwLDEsNjUuNjctMjYuNzZIMTI2YTkzLjQ1LDkzLjQ1LDAsMCwxLDY2LDI3LjUzbDE4LDE4VjQ4YTYsNiwwLDAsMSwxMiwwWk0xODcuODEsMTg0LjcyYTgxLjUsODEuNSwwLDAsMS01Ny4yOSwyMy4zNGgtLjQ2YTgxLjUxLDgxLjUxLDAsMCwxLTU3LjUzLTI0TDU0LjQ4LDE2Nkg4OGE2LDYsMCwwLDAsMC0xMkg0MGE2LDYsMCwwLDAtNiw2djQ4YTYsNiwwLDAsMCwxMiwwVjE3NC40OGwxOCwxOC4wNWE5My40NSw5My40NSwwLDAsMCw2NiwyNy41M2guNTJhOTMuMzgsOTMuMzgsMCwwLDAsNjUuNjctMjYuNzYsNiw2LDAsMSwwLTguMzgtOC41OFoiLz48L3N2Zz4=);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjIsNDhWOTZhNiw2LDAsMCwxLTYsNkgxNjhhNiw2LDAsMCwxLDAtMTJoMzMuNTJMMTgzLjQ3LDcyYTgxLjUxLDgxLjUxLDAsMCwwLTU3LjUzLTI0aC0uNDZBODEuNSw4MS41LDAsMCwwLDY4LjE5LDcxLjI4YTYsNiwwLDEsMS04LjM4LTguNTgsOTMuMzgsOTMuMzgsMCwwLDEsNjUuNjctMjYuNzZIMTI2YTkzLjQ1LDkzLjQ1LDAsMCwxLDY2LDI3LjUzbDE4LDE4VjQ4YTYsNiwwLDAsMSwxMiwwWk0xODcuODEsMTg0LjcyYTgxLjUsODEuNSwwLDAsMS01Ny4yOSwyMy4zNGgtLjQ2YTgxLjUxLDgxLjUxLDAsMCwxLTU3LjUzLTI0TDU0LjQ4LDE2Nkg4OGE2LDYsMCwwLDAsMC0xMkg0MGE2LDYsMCwwLDAtNiw2djQ4YTYsNiwwLDAsMCwxMiwwVjE3NC40OGwxOCwxOC4wNWE5My40NSw5My40NSwwLDAsMCw2NiwyNy41M2guNTJhOTMuMzgsOTMuMzgsMCwwLDAsNjUuNjctMjYuNzYsNiw2LDAsMSwwLTguMzgtOC41OFoiLz48L3N2Zz4=)}.icon-check-circle{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xNzIuMjQsOTkuNzZhNiw2LDAsMCwxLDAsOC40OGwtNTYsNTZhNiw2LDAsMCwxLTguNDgsMGwtMjQtMjRhNiw2LDAsMCwxLDguNDgtOC40OEwxMTIsMTUxLjUxbDUxLjc2LTUxLjc1QTYsNiwwLDAsMSwxNzIuMjQsOTkuNzZaTTIzMCwxMjhBMTAyLDEwMiwwLDEsMSwxMjgsMjYsMTAyLjEyLDEwMi4xMiwwLDAsMSwyMzAsMTI4Wm0tMTIsMGE5MCw5MCwwLDEsMC05MCw5MEE5MC4xLDkwLjEsMCwwLDAsMjE4LDEyOFoiLz48L3N2Zz4=);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xNzIuMjQsOTkuNzZhNiw2LDAsMCwxLDAsOC40OGwtNTYsNTZhNiw2LDAsMCwxLTguNDgsMGwtMjQtMjRhNiw2LDAsMCwxLDguNDgtOC40OEwxMTIsMTUxLjUxbDUxLjc2LTUxLjc1QTYsNiwwLDAsMSwxNzIuMjQsOTkuNzZaTTIzMCwxMjhBMTAyLDEwMiwwLDEsMSwxMjgsMjYsMTAyLjEyLDEwMi4xMiwwLDAsMSwyMzAsMTI4Wm0tMTIsMGE5MCw5MCwwLDEsMC05MCw5MEE5MC4xLDkwLjEsMCwwLDAsMjE4LDEyOFoiLz48L3N2Zz4=)}.icon-x-circle{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xNjQuMjQsMTAwLjI0LDEzNi40OCwxMjhsMjcuNzYsMjcuNzZhNiw2LDAsMSwxLTguNDgsOC40OEwxMjgsMTM2LjQ4bC0yNy43NiwyNy43NmE2LDYsMCwwLDEtOC40OC04LjQ4TDExOS41MiwxMjgsOTEuNzYsMTAwLjI0YTYsNiwwLDAsMSw4LjQ4LTguNDhMMTI4LDExOS41MmwyNy43Ni0yNy43NmE2LDYsMCwwLDEsOC40OCw4LjQ4Wk0yMzAsMTI4QTEwMiwxMDIsMCwxLDEsMTI4LDI2LDEwMi4xMiwxMDIuMTIsMCwwLDEsMjMwLDEyOFptLTEyLDBhOTAsOTAsMCwxLDAtOTAsOTBBOTAuMSw5MC4xLDAsMCwwLDIxOCwxMjhaIi8+PC9zdmc+);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xNjQuMjQsMTAwLjI0LDEzNi40OCwxMjhsMjcuNzYsMjcuNzZhNiw2LDAsMSwxLTguNDgsOC40OEwxMjgsMTM2LjQ4bC0yNy43NiwyNy43NmE2LDYsMCwwLDEtOC40OC04LjQ4TDExOS41MiwxMjgsOTEuNzYsMTAwLjI0YTYsNiwwLDAsMSw4LjQ4LTguNDhMMTI4LDExOS41MmwyNy43Ni0yNy43NmE2LDYsMCwwLDEsOC40OCw4LjQ4Wk0yMzAsMTI4QTEwMiwxMDIsMCwxLDEsMTI4LDI2LDEwMi4xMiwxMDIuMTIsMCwwLDEsMjMwLDEyOFptLTEyLDBhOTAsOTAsMCwxLDAtOTAsOTBBOTAuMSw5MC4xLDAsMCwwLDIxOCwxMjhaIi8+PC9zdmc+)}.icon-arrow-fat-down{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjkuNjYsMTQxLjY2bC05Niw5NmE4LDgsMCwwLDEtMTEuMzIsMGwtOTYtOTZBOCw4LDAsMCwxLDMyLDEyOEg3MlY0OEExNiwxNiwwLDAsMSw4OCwzMmg4MGExNiwxNiwwLDAsMSwxNiwxNnY4MGg0MGE4LDgsMCwwLDEsNS42NiwxMy42NloiLz48L3N2Zz4=);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjkuNjYsMTQxLjY2bC05Niw5NmE4LDgsMCwwLDEtMTEuMzIsMGwtOTYtOTZBOCw4LDAsMCwxLDMyLDEyOEg3MlY0OEExNiwxNiwwLDAsMSw4OCwzMmg4MGExNiwxNiwwLDAsMSwxNiwxNnY4MGg0MGE4LDgsMCwwLDEsNS42NiwxMy42NloiLz48L3N2Zz4=)}.icon-arrow-fat-up{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzEuMzksMTIzLjA2QTgsOCwwLDAsMSwyMjQsMTI4SDE4NHY4MGExNiwxNiwwLDAsMS0xNiwxNkg4OGExNiwxNiwwLDAsMS0xNi0xNlYxMjhIMzJhOCw4LDAsMCwxLTUuNjYtMTMuNjZsOTYtOTZhOCw4LDAsMCwxLDExLjMyLDBsOTYsOTZBOCw4LDAsMCwxLDIzMS4zOSwxMjMuMDZaIi8+PC9zdmc+);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzEuMzksMTIzLjA2QTgsOCwwLDAsMSwyMjQsMTI4SDE4NHY4MGExNiwxNiwwLDAsMS0xNiwxNkg4OGExNiwxNiwwLDAsMS0xNi0xNlYxMjhIMzJhOCw4LDAsMCwxLTUuNjYtMTMuNjZsOTYtOTZhOCw4LDAsMCwxLDExLjMyLDBsOTYsOTZBOCw4LDAsMCwxLDIzMS4zOSwxMjMuMDZaIi8+PC9zdmc+)}.icon-heart{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yNDAsMTAyYzAsNzAtMTAzLjc5LDEyNi42Ni0xMDguMjEsMTI5YTgsOCwwLDAsMS03LjU4LDBDMTE5Ljc5LDIyOC42NiwxNiwxNzIsMTYsMTAyQTYyLjA3LDYyLjA3LDAsMCwxLDc4LDQwYzIwLjY1LDAsMzguNzMsOC44OCw1MCwyMy44OUMxMzkuMjcsNDguODgsMTU3LjM1LDQwLDE3OCw0MEE2Mi4wNyw2Mi4wNywwLDAsMSwyNDAsMTAyWiIvPjwvc3ZnPg==);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yNDAsMTAyYzAsNzAtMTAzLjc5LDEyNi42Ni0xMDguMjEsMTI5YTgsOCwwLDAsMS03LjU4LDBDMTE5Ljc5LDIyOC42NiwxNiwxNzIsMTYsMTAyQTYyLjA3LDYyLjA3LDAsMCwxLDc4LDQwYzIwLjY1LDAsMzguNzMsOC44OCw1MCwyMy44OUMxMzkuMjcsNDguODgsMTU3LjM1LDQwLDE3OCw0MEE2Mi4wNyw2Mi4wNywwLDAsMSwyNDAsMTAyWiIvPjwvc3ZnPg==)}.icon-star{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzQuMjksMTE0Ljg1bC00NSwzOC44M0wyMDMsMjExLjc1YTE2LjQsMTYuNCwwLDAsMS0yNC41LDE3LjgyTDEyOCwxOTguNDksNzcuNDcsMjI5LjU3QTE2LjQsMTYuNCwwLDAsMSw1MywyMTEuNzVsMTMuNzYtNTguMDctNDUtMzguODNBMTYuNDYsMTYuNDYsMCwwLDEsMzEuMDgsODZsNTktNC43NiwyMi43Ni01NS4wOGExNi4zNiwxNi4zNiwwLDAsMSwzMC4yNywwbDIyLjc1LDU1LjA4LDU5LDQuNzZhMTYuNDYsMTYuNDYsMCwwLDEsOS4zNywyOC44NloiLz48L3N2Zz4=);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzQuMjksMTE0Ljg1bC00NSwzOC44M0wyMDMsMjExLjc1YTE2LjQsMTYuNCwwLDAsMS0yNC41LDE3LjgyTDEyOCwxOTguNDksNzcuNDcsMjI5LjU3QTE2LjQsMTYuNCwwLDAsMSw1MywyMTEuNzVsMTMuNzYtNTguMDctNDUtMzguODNBMTYuNDYsMTYuNDYsMCwwLDEsMzEuMDgsODZsNTktNC43NiwyMi43Ni01NS4wOGExNi4zNiwxNi4zNiwwLDAsMSwzMC4yNywwbDIyLjc1LDU1LjA4LDU5LDQuNzZhMTYuNDYsMTYuNDYsMCwwLDEsOS4zNywyOC44NloiLz48L3N2Zz4=)}.icon-apple-logo{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjMuMywxNjkuNTlhOC4wNyw4LjA3LDAsMCwwLTIuOC0zLjRDMjAzLjUzLDE1NC41MywyMDAsMTM0LjY0LDIwMCwxMjBjMC0xNy42NywxMy40Ny0zMy4wNiwyMS41LTQwLjY3YTgsOCwwLDAsMCwwLTExLjYyQzIwOC44Miw1NS43NCwxODcuODIsNDgsMTY4LDQ4YTcyLjIsNzIuMiwwLDAsMC00MCwxMi4xMyw3MS41Niw3MS41NiwwLDAsMC05MC43MSw5LjA5QTc0LjYzLDc0LjYzLDAsMCwwLDE2LDEyMy40YTEyNy4wNiwxMjcuMDYsMCwwLDAsNDAuMTQsODkuNzNBMzkuOCwzOS44LDAsMCwwLDgzLjU5LDIyNGg4Ny42OGEzOS44NCwzOS44NCwwLDAsMCwyOS4xMi0xMi41NywxMjUsMTI1LDAsMCwwLDE3LjgyLTI0LjZDMjI1LjIzLDE3NCwyMjQuMzMsMTcyLDIyMy4zLDE2OS41OVptLTM0LjYzLDMwLjk0YTIzLjc2LDIzLjc2LDAsMCwxLTE3LjQsNy40N0g4My41OWEyMy44MiwyMy44MiwwLDAsMS0xNi40NC02LjUxQTExMS4xNCwxMTEuMTQsMCwwLDEsMzIsMTIzLDU4LjUsNTguNSwwLDAsMSw0OC42NSw4MC40Nyw1NC44MSw1NC44MSwwLDAsMSw4OCw2NGguNzhBNTUuNDUsNTUuNDUsMCwwLDEsMTIzLDc2LjI4YTgsOCwwLDAsMCwxMCwwQTU1LjQ0LDU1LjQ0LDAsMCwxLDE2OCw2NGE3MC42NCw3MC42NCwwLDAsMSwzNiwxMC4zNWMtMTMsMTQuNTItMjAsMzAuNDctMjAsNDUuNjUsMCwyMy43Nyw3LjY0LDQyLjczLDIyLjE4LDU1LjNBMTA1LjgyLDEwNS44MiwwLDAsMSwxODguNjcsMjAwLjUzWk0xMjguMjMsMzBBNDAsNDAsMCwwLDEsMTY3LDBoMWE4LDgsMCwwLDEsMCwxNmgtMWEyNCwyNCwwLDAsMC0yMy4yNCwxOCw4LDgsMCwxLDEtMTUuNS00WiIvPjwvc3ZnPg==);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjMuMywxNjkuNTlhOC4wNyw4LjA3LDAsMCwwLTIuOC0zLjRDMjAzLjUzLDE1NC41MywyMDAsMTM0LjY0LDIwMCwxMjBjMC0xNy42NywxMy40Ny0zMy4wNiwyMS41LTQwLjY3YTgsOCwwLDAsMCwwLTExLjYyQzIwOC44Miw1NS43NCwxODcuODIsNDgsMTY4LDQ4YTcyLjIsNzIuMiwwLDAsMC00MCwxMi4xMyw3MS41Niw3MS41NiwwLDAsMC05MC43MSw5LjA5QTc0LjYzLDc0LjYzLDAsMCwwLDE2LDEyMy40YTEyNy4wNiwxMjcuMDYsMCwwLDAsNDAuMTQsODkuNzNBMzkuOCwzOS44LDAsMCwwLDgzLjU5LDIyNGg4Ny42OGEzOS44NCwzOS44NCwwLDAsMCwyOS4xMi0xMi41NywxMjUsMTI1LDAsMCwwLDE3LjgyLTI0LjZDMjI1LjIzLDE3NCwyMjQuMzMsMTcyLDIyMy4zLDE2OS41OVptLTM0LjYzLDMwLjk0YTIzLjc2LDIzLjc2LDAsMCwxLTE3LjQsNy40N0g4My41OWEyMy44MiwyMy44MiwwLDAsMS0xNi40NC02LjUxQTExMS4xNCwxMTEuMTQsMCwwLDEsMzIsMTIzLDU4LjUsNTguNSwwLDAsMSw0OC42NSw4MC40Nyw1NC44MSw1NC44MSwwLDAsMSw4OCw2NGguNzhBNTUuNDUsNTUuNDUsMCwwLDEsMTIzLDc2LjI4YTgsOCwwLDAsMCwxMCwwQTU1LjQ0LDU1LjQ0LDAsMCwxLDE2OCw2NGE3MC42NCw3MC42NCwwLDAsMSwzNiwxMC4zNWMtMTMsMTQuNTItMjAsMzAuNDctMjAsNDUuNjUsMCwyMy43Nyw3LjY0LDQyLjczLDIyLjE4LDU1LjNBMTA1LjgyLDEwNS44MiwwLDAsMSwxODguNjcsMjAwLjUzWk0xMjguMjMsMzBBNDAsNDAsMCwwLDEsMTY3LDBoMWE4LDgsMCwwLDEsMCwxNmgtMWEyNCwyNCwwLDAsMC0yMy4yNCwxOCw4LDgsMCwxLDEtMTUuNS00WiIvPjwvc3ZnPg==)}.icon-arrows-clockwise{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjQsNDhWOTZhOCw4LDAsMCwxLTgsOEgxNjhhOCw4LDAsMCwxLDAtMTZoMjguNjlMMTgyLjA2LDczLjM3YTc5LjU2LDc5LjU2LDAsMCwwLTU2LjEzLTIzLjQzaC0uNDVBNzkuNTIsNzkuNTIsMCwwLDAsNjkuNTksNzIuNzEsOCw4LDAsMCwxLDU4LjQxLDYxLjI3YTk2LDk2LDAsMCwxLDEzNSwuNzlMMjA4LDc2LjY5VjQ4YTgsOCwwLDAsMSwxNiwwWk0xODYuNDEsMTgzLjI5YTgwLDgwLDAsMCwxLTExMi40Ny0uNjZMNTkuMzEsMTY4SDg4YTgsOCwwLDAsMCwwLTE2SDQwYTgsOCwwLDAsMC04LDh2NDhhOCw4LDAsMCwwLDE2LDBWMTc5LjMxbDE0LjYzLDE0LjYzQTk1LjQzLDk1LjQzLDAsMCwwLDEzMCwyMjIuMDZoLjUzYTk1LjM2LDk1LjM2LDAsMCwwLDY3LjA3LTI3LjMzLDgsOCwwLDAsMC0xMS4xOC0xMS40NFoiLz48L3N2Zz4=);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjQsNDhWOTZhOCw4LDAsMCwxLTgsOEgxNjhhOCw4LDAsMCwxLDAtMTZoMjguNjlMMTgyLjA2LDczLjM3YTc5LjU2LDc5LjU2LDAsMCwwLTU2LjEzLTIzLjQzaC0uNDVBNzkuNTIsNzkuNTIsMCwwLDAsNjkuNTksNzIuNzEsOCw4LDAsMCwxLDU4LjQxLDYxLjI3YTk2LDk2LDAsMCwxLDEzNSwuNzlMMjA4LDc2LjY5VjQ4YTgsOCwwLDAsMSwxNiwwWk0xODYuNDEsMTgzLjI5YTgwLDgwLDAsMCwxLTExMi40Ny0uNjZMNTkuMzEsMTY4SDg4YTgsOCwwLDAsMCwwLTE2SDQwYTgsOCwwLDAsMC04LDh2NDhhOCw4LDAsMCwwLDE2LDBWMTc5LjMxbDE0LjYzLDE0LjYzQTk1LjQzLDk1LjQzLDAsMCwwLDEzMCwyMjIuMDZoLjUzYTk1LjM2LDk1LjM2LDAsMCwwLDY3LjA3LTI3LjMzLDgsOCwwLDAsMC0xMS4xOC0xMS40NFoiLz48L3N2Zz4=)}.icon-arrows-left-right{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTMuNjYsMTgxLjY2bC0zMiwzMmE4LDgsMCwwLDEtMTEuMzItMTEuMzJMMTg4LjY5LDE4NEg0OGE4LDgsMCwwLDEsMC0xNkgxODguNjlsLTE4LjM1LTE4LjM0YTgsOCwwLDAsMSwxMS4zMi0xMS4zMmwzMiwzMkE4LDgsMCwwLDEsMjEzLjY2LDE4MS42NlptLTEzOS4zMi02NGE4LDgsMCwwLDAsMTEuMzItMTEuMzJMNjcuMzEsODhIMjA4YTgsOCwwLDAsMCwwLTE2SDY3LjMxTDg1LjY2LDUzLjY2QTgsOCwwLDAsMCw3NC4zNCw0Mi4zNGwtMzIsMzJhOCw4LDAsMCwwLDAsMTEuMzJaIi8+PC9zdmc+);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTMuNjYsMTgxLjY2bC0zMiwzMmE4LDgsMCwwLDEtMTEuMzItMTEuMzJMMTg4LjY5LDE4NEg0OGE4LDgsMCwwLDEsMC0xNkgxODguNjlsLTE4LjM1LTE4LjM0YTgsOCwwLDAsMSwxMS4zMi0xMS4zMmwzMiwzMkE4LDgsMCwwLDEsMjEzLjY2LDE4MS42NlptLTEzOS4zMi02NGE4LDgsMCwwLDAsMTEuMzItMTEuMzJMNjcuMzEsODhIMjA4YTgsOCwwLDAsMCwwLTE2SDY3LjMxTDg1LjY2LDUzLjY2QTgsOCwwLDAsMCw3NC4zNCw0Mi4zNGwtMzIsMzJhOCw4LDAsMCwwLDAsMTEuMzJaIi8+PC9zdmc+)}.icon-asterisk{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTQuODYsMTgwLjEyYTgsOCwwLDAsMS0xMSwyLjc0TDEzNiwxNDIuMTNWMjE2YTgsOCwwLDAsMS0xNiwwVjE0Mi4xM0w1Mi4xMiwxODIuODZhOCw4LDAsMSwxLTguMjMtMTMuNzJMMTEyLjQ1LDEyOCw0My44OSw4Ni44NmE4LDgsMCwxLDEsOC4yMy0xMy43MkwxMjAsMTEzLjg3VjQwYTgsOCwwLDAsMSwxNiwwdjczLjg3bDY3Ljg4LTQwLjczYTgsOCwwLDEsMSw4LjIzLDEzLjcyTDE0My41NSwxMjhsNjguNTYsNDEuMTRBOCw4LDAsMCwxLDIxNC44NiwxODAuMTJaIi8+PC9zdmc+);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTQuODYsMTgwLjEyYTgsOCwwLDAsMS0xMSwyLjc0TDEzNiwxNDIuMTNWMjE2YTgsOCwwLDAsMS0xNiwwVjE0Mi4xM0w1Mi4xMiwxODIuODZhOCw4LDAsMSwxLTguMjMtMTMuNzJMMTEyLjQ1LDEyOCw0My44OSw4Ni44NmE4LDgsMCwxLDEsOC4yMy0xMy43MkwxMjAsMTEzLjg3VjQwYTgsOCwwLDAsMSwxNiwwdjczLjg3bDY3Ljg4LTQwLjczYTgsOCwwLDEsMSw4LjIzLDEzLjcyTDE0My41NSwxMjhsNjguNTYsNDEuMTRBOCw4LDAsMCwxLDIxNC44NiwxODAuMTJaIi8+PC9zdmc+)}.icon-butterfly{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzIuNyw1MC40OEMyMjksNDUuNywyMjEuODQsNDAsMjA5LDQwYy0xNi44NSwwLTM4LjQ2LDExLjI4LTU3LjgxLDMwLjE2QTE0MC4wNywxNDAuMDcsMCwwLDAsMTM2LDg3LjUzVjU2YTgsOCwwLDAsMC0xNiwwVjg3LjUzYTE0MC4wNywxNDAuMDcsMCwwLDAtMTUuMTUtMTcuMzdDODUuNDksNTEuMjgsNjMuODgsNDAsNDcsNDAsMzQuMTYsNDAsMjcsNDUuNywyMy4zLDUwLjQ4Yy02LjgyLDguNzctMTIuMTgsMjQuMDgtLjIxLDcxLjIsNi4wNSwyMy44MywxOS41MSwzMywzMC42MywzNi40MkE0NCw0NCwwLDAsMCwxMjgsMjA1LjI3YTQ0LDQ0LDAsMCwwLDc0LjI4LTQ3LjE3YzExLjEyLTMuNCwyNC41Ny0xMi41OSwzMC42My0zNi40MkMyMzkuNjMsOTUuMjQsMjQ0Ljg1LDY2LjEsMjMyLjcsNTAuNDhaTTkyLDIwOEEyOC4xMiwyOC4xMiwwLDAsMSw4OC44NiwxNTJhOCw4LDAsMSwwLTEuNzYtMTUuOUE0My42NCw0My42NCwwLDAsMCw2Ni4zNiwxNDRjLTguNDMuMDktMjItMy41Ny0yNy43Ni0yNi4yNkMzNS43MiwxMDYuMzksMjcsNzEuODYsMzUuOTQsNjAuMywzNy4zNyw1OC40Niw0MC4wOSw1Niw0Nyw1NmMyNy4yNywwLDczLDQ0Ljg4LDczLDcxLjY3VjE4MEEyOCwyOCwwLDAsMSw5MiwyMDhaTTIxNy40LDExNy43NGMtNS43NywyMi42OS0xOS4zMywyNi4zNC0yNy43NywyNi4yNmE0My42LDQzLjYsMCwwLDAtMjAuNzQtNy45NSw4LDgsMCwxLDAtMS43NiwxNS45QTI4LjExLDI4LjExLDAsMSwxLDEzNiwxODBWMTI3LjY3QzEzNiwxMDAuODgsMTgxLjY5LDU2LDIwOSw1NmM2Ljk1LDAsOS42NiwyLjQ2LDExLjEsNC4zQzIyOS4wNSw3MS44NiwyMjAuMjgsMTA2LjM5LDIxNy40LDExNy43NFoiLz48L3N2Zz4=);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzIuNyw1MC40OEMyMjksNDUuNywyMjEuODQsNDAsMjA5LDQwYy0xNi44NSwwLTM4LjQ2LDExLjI4LTU3LjgxLDMwLjE2QTE0MC4wNywxNDAuMDcsMCwwLDAsMTM2LDg3LjUzVjU2YTgsOCwwLDAsMC0xNiwwVjg3LjUzYTE0MC4wNywxNDAuMDcsMCwwLDAtMTUuMTUtMTcuMzdDODUuNDksNTEuMjgsNjMuODgsNDAsNDcsNDAsMzQuMTYsNDAsMjcsNDUuNywyMy4zLDUwLjQ4Yy02LjgyLDguNzctMTIuMTgsMjQuMDgtLjIxLDcxLjIsNi4wNSwyMy44MywxOS41MSwzMywzMC42MywzNi40MkE0NCw0NCwwLDAsMCwxMjgsMjA1LjI3YTQ0LDQ0LDAsMCwwLDc0LjI4LTQ3LjE3YzExLjEyLTMuNCwyNC41Ny0xMi41OSwzMC42My0zNi40MkMyMzkuNjMsOTUuMjQsMjQ0Ljg1LDY2LjEsMjMyLjcsNTAuNDhaTTkyLDIwOEEyOC4xMiwyOC4xMiwwLDAsMSw4OC44NiwxNTJhOCw4LDAsMSwwLTEuNzYtMTUuOUE0My42NCw0My42NCwwLDAsMCw2Ni4zNiwxNDRjLTguNDMuMDktMjItMy41Ny0yNy43Ni0yNi4yNkMzNS43MiwxMDYuMzksMjcsNzEuODYsMzUuOTQsNjAuMywzNy4zNyw1OC40Niw0MC4wOSw1Niw0Nyw1NmMyNy4yNywwLDczLDQ0Ljg4LDczLDcxLjY3VjE4MEEyOCwyOCwwLDAsMSw5MiwyMDhaTTIxNy40LDExNy43NGMtNS43NywyMi42OS0xOS4zMywyNi4zNC0yNy43NywyNi4yNmE0My42LDQzLjYsMCwwLDAtMjAuNzQtNy45NSw4LDgsMCwxLDAtMS43NiwxNS45QTI4LjExLDI4LjExLDAsMSwxLDEzNiwxODBWMTI3LjY3QzEzNiwxMDAuODgsMTgxLjY5LDU2LDIwOSw1NmM2Ljk1LDAsOS42NiwyLjQ2LDExLjEsNC4zQzIyOS4wNSw3MS44NiwyMjAuMjgsMTA2LjM5LDIxNy40LDExNy43NFoiLz48L3N2Zz4=)}.icon-cake{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzIsMTEyYTI0LDI0LDAsMCwwLTI0LTI0SDEzNlY3OWEzMi4wNiwzMi4wNiwwLDAsMCwyNC0zMWMwLTI4LTI2LjQ0LTQ1LjkxLTI3LjU2LTQ2LjY2YTgsOCwwLDAsMC04Ljg4LDBDMTIyLjQ0LDIuMDksOTYsMjAsOTYsNDhhMzIuMDYsMzIuMDYsMCwwLDAsMjQsMzF2OUg0OGEyNCwyNCwwLDAsMC0yNCwyNHYyMy4zM2E0MC44NCw0MC44NCwwLDAsMCw4LDI0LjI0VjIwMGEyNCwyNCwwLDAsMCwyNCwyNEgyMDBhMjQsMjQsMCwwLDAsMjQtMjRWMTU5LjU3YTQwLjg0LDQwLjg0LDAsMCwwLDgtMjQuMjRaTTExMiw0OGMwLTEzLjU3LDEwLTI0LjQ2LDE2LTI5Ljc5LDYsNS4zMywxNiwxNi4yMiwxNiwyOS43OWExNiwxNiwwLDAsMS0zMiwwWk00MCwxMTJhOCw4LDAsMCwxLDgtOEgyMDhhOCw4LDAsMCwxLDgsOHYyMy4zM2MwLDEzLjI1LTEwLjQ2LDI0LjMxLTIzLjMyLDI0LjY2QTI0LDI0LDAsMCwxLDE2OCwxMzZhOCw4LDAsMCwwLTE2LDAsMjQsMjQsMCwwLDEtNDgsMCw4LDgsMCwwLDAtMTYsMCwyNCwyNCwwLDAsMS0yNC42OCwyNEM1MC40NiwxNTkuNjQsNDAsMTQ4LjU4LDQwLDEzNS4zM1ptMTYwLDk2SDU2YTgsOCwwLDAsMS04LThWMTcyLjU2QTM4Ljc3LDM4Ljc3LDAsMCwwLDYyLjg4LDE3NmEzOS42OSwzOS42OSwwLDAsMCwyOS0xMS4zMUE0MC4zNiw0MC4zNiwwLDAsMCw5NiwxNjBhNDAsNDAsMCwwLDAsNjQsMCw0MC4zNiw0MC4zNiwwLDAsMCw0LjEzLDQuNjdBMzkuNjcsMzkuNjcsMCwwLDAsMTkyLDE3NmMuMzgsMCwuNzYsMCwxLjE0LDBBMzguNzcsMzguNzcsMCwwLDAsMjA4LDE3Mi41NlYyMDBBOCw4LDAsMCwxLDIwMCwyMDhaIi8+PC9zdmc+);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzIsMTEyYTI0LDI0LDAsMCwwLTI0LTI0SDEzNlY3OWEzMi4wNiwzMi4wNiwwLDAsMCwyNC0zMWMwLTI4LTI2LjQ0LTQ1LjkxLTI3LjU2LTQ2LjY2YTgsOCwwLDAsMC04Ljg4LDBDMTIyLjQ0LDIuMDksOTYsMjAsOTYsNDhhMzIuMDYsMzIuMDYsMCwwLDAsMjQsMzF2OUg0OGEyNCwyNCwwLDAsMC0yNCwyNHYyMy4zM2E0MC44NCw0MC44NCwwLDAsMCw4LDI0LjI0VjIwMGEyNCwyNCwwLDAsMCwyNCwyNEgyMDBhMjQsMjQsMCwwLDAsMjQtMjRWMTU5LjU3YTQwLjg0LDQwLjg0LDAsMCwwLDgtMjQuMjRaTTExMiw0OGMwLTEzLjU3LDEwLTI0LjQ2LDE2LTI5Ljc5LDYsNS4zMywxNiwxNi4yMiwxNiwyOS43OWExNiwxNiwwLDAsMS0zMiwwWk00MCwxMTJhOCw4LDAsMCwxLDgtOEgyMDhhOCw4LDAsMCwxLDgsOHYyMy4zM2MwLDEzLjI1LTEwLjQ2LDI0LjMxLTIzLjMyLDI0LjY2QTI0LDI0LDAsMCwxLDE2OCwxMzZhOCw4LDAsMCwwLTE2LDAsMjQsMjQsMCwwLDEtNDgsMCw4LDgsMCwwLDAtMTYsMCwyNCwyNCwwLDAsMS0yNC42OCwyNEM1MC40NiwxNTkuNjQsNDAsMTQ4LjU4LDQwLDEzNS4zM1ptMTYwLDk2SDU2YTgsOCwwLDAsMS04LThWMTcyLjU2QTM4Ljc3LDM4Ljc3LDAsMCwwLDYyLjg4LDE3NmEzOS42OSwzOS42OSwwLDAsMCwyOS0xMS4zMUE0MC4zNiw0MC4zNiwwLDAsMCw5NiwxNjBhNDAsNDAsMCwwLDAsNjQsMCw0MC4zNiw0MC4zNiwwLDAsMCw0LjEzLDQuNjdBMzkuNjcsMzkuNjcsMCwwLDAsMTkyLDE3NmMuMzgsMCwuNzYsMCwxLjE0LDBBMzguNzcsMzguNzcsMCwwLDAsMjA4LDE3Mi41NlYyMDBBOCw4LDAsMCwxLDIwMCwyMDhaIi8+PC9zdmc+)}.icon-chat{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTYsNDhINDBBMTYsMTYsMCwwLDAsMjQsNjRWMjI0YTE1Ljg0LDE1Ljg0LDAsMCwwLDkuMjUsMTQuNUExNi4wNSwxNi4wNSwwLDAsMCw0MCwyNDBhMTUuODksMTUuODksMCwwLDAsMTAuMjUtMy43OGwuMDktLjA3TDgzLDIwOEgyMTZhMTYsMTYsMCwwLDAsMTYtMTZWNjRBMTYsMTYsMCwwLDAsMjE2LDQ4Wk00MCwyMjRoMFpNMjE2LDE5Mkg4MGE4LDgsMCwwLDAtNS4yMywxLjk1TDQwLDIyNFY2NEgyMTZaIi8+PC9zdmc+);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTYsNDhINDBBMTYsMTYsMCwwLDAsMjQsNjRWMjI0YTE1Ljg0LDE1Ljg0LDAsMCwwLDkuMjUsMTQuNUExNi4wNSwxNi4wNSwwLDAsMCw0MCwyNDBhMTUuODksMTUuODksMCwwLDAsMTAuMjUtMy43OGwuMDktLjA3TDgzLDIwOEgyMTZhMTYsMTYsMCwwLDAsMTYtMTZWNjRBMTYsMTYsMCwwLDAsMjE2LDQ4Wk00MCwyMjRoMFpNMjE2LDE5Mkg4MGE4LDgsMCwwLDAtNS4yMywxLjk1TDQwLDIyNFY2NEgyMTZaIi8+PC9zdmc+)}.icon-check-circle{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xNzMuNjYsOTguMzRhOCw4LDAsMCwxLDAsMTEuMzJsLTU2LDU2YTgsOCwwLDAsMS0xMS4zMiwwbC0yNC0yNGE4LDgsMCwwLDEsMTEuMzItMTEuMzJMMTEyLDE0OC42OWw1MC4zNC01MC4zNUE4LDgsMCwwLDEsMTczLjY2LDk4LjM0Wk0yMzIsMTI4QTEwNCwxMDQsMCwxLDEsMTI4LDI0LDEwNC4xMSwxMDQuMTEsMCwwLDEsMjMyLDEyOFptLTE2LDBhODgsODgsMCwxLDAtODgsODhBODguMSw4OC4xLDAsMCwwLDIxNiwxMjhaIi8+PC9zdmc+);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xNzMuNjYsOTguMzRhOCw4LDAsMCwxLDAsMTEuMzJsLTU2LDU2YTgsOCwwLDAsMS0xMS4zMiwwbC0yNC0yNGE4LDgsMCwwLDEsMTEuMzItMTEuMzJMMTEyLDE0OC42OWw1MC4zNC01MC4zNUE4LDgsMCwwLDEsMTczLjY2LDk4LjM0Wk0yMzIsMTI4QTEwNCwxMDQsMCwxLDEsMTI4LDI0LDEwNC4xMSwxMDQuMTEsMCwwLDEsMjMyLDEyOFptLTE2LDBhODgsODgsMCwxLDAtODgsODhBODguMSw4OC4xLDAsMCwwLDIxNiwxMjhaIi8+PC9zdmc+)}.icon-clock{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMjgsMjRBMTA0LDEwNCwwLDEsMCwyMzIsMTI4LDEwNC4xMSwxMDQuMTEsMCwwLDAsMTI4LDI0Wm0wLDE5MmE4OCw4OCwwLDEsMSw4OC04OEE4OC4xLDg4LjEsMCwwLDEsMTI4LDIxNlptNjQtODhhOCw4LDAsMCwxLTgsOEgxMjhhOCw4LDAsMCwxLTgtOFY3MmE4LDgsMCwwLDEsMTYsMHY0OGg0OEE4LDgsMCwwLDEsMTkyLDEyOFoiLz48L3N2Zz4=);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMjgsMjRBMTA0LDEwNCwwLDEsMCwyMzIsMTI4LDEwNC4xMSwxMDQuMTEsMCwwLDAsMTI4LDI0Wm0wLDE5MmE4OCw4OCwwLDEsMSw4OC04OEE4OC4xLDg4LjEsMCwwLDEsMTI4LDIxNlptNjQtODhhOCw4LDAsMCwxLTgsOEgxMjhhOCw4LDAsMCwxLTgtOFY3MmE4LDgsMCwwLDEsMTYsMHY0OGg0OEE4LDgsMCwwLDEsMTkyLDEyOFoiLz48L3N2Zz4=)}.icon-crosshair{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzIsMTIwaC04LjM0QTk2LjE0LDk2LjE0LDAsMCwwLDEzNiwzMi4zNFYyNGE4LDgsMCwwLDAtMTYsMHY4LjM0QTk2LjE0LDk2LjE0LDAsMCwwLDMyLjM0LDEyMEgyNGE4LDgsMCwwLDAsMCwxNmg4LjM0QTk2LjE0LDk2LjE0LDAsMCwwLDEyMCwyMjMuNjZWMjMyYTgsOCwwLDAsMCwxNiwwdi04LjM0QTk2LjE0LDk2LjE0LDAsMCwwLDIyMy42NiwxMzZIMjMyYTgsOCwwLDAsMCwwLTE2Wm0tOTYsODcuNlYyMDBhOCw4LDAsMCwwLTE2LDB2Ny42QTgwLjE1LDgwLjE1LDAsMCwxLDQ4LjQsMTM2SDU2YTgsOCwwLDAsMCwwLTE2SDQ4LjRBODAuMTUsODAuMTUsMCwwLDEsMTIwLDQ4LjRWNTZhOCw4LDAsMCwwLDE2LDBWNDguNEE4MC4xNSw4MC4xNSwwLDAsMSwyMDcuNiwxMjBIMjAwYTgsOCwwLDAsMCwwLDE2aDcuNkE4MC4xNSw4MC4xNSwwLDAsMSwxMzYsMjA3LjZaTTEyOCw4OGE0MCw0MCwwLDEsMCw0MCw0MEE0MCw0MCwwLDAsMCwxMjgsODhabTAsNjRhMjQsMjQsMCwxLDEsMjQtMjRBMjQsMjQsMCwwLDEsMTI4LDE1MloiLz48L3N2Zz4=);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzIsMTIwaC04LjM0QTk2LjE0LDk2LjE0LDAsMCwwLDEzNiwzMi4zNFYyNGE4LDgsMCwwLDAtMTYsMHY4LjM0QTk2LjE0LDk2LjE0LDAsMCwwLDMyLjM0LDEyMEgyNGE4LDgsMCwwLDAsMCwxNmg4LjM0QTk2LjE0LDk2LjE0LDAsMCwwLDEyMCwyMjMuNjZWMjMyYTgsOCwwLDAsMCwxNiwwdi04LjM0QTk2LjE0LDk2LjE0LDAsMCwwLDIyMy42NiwxMzZIMjMyYTgsOCwwLDAsMCwwLTE2Wm0tOTYsODcuNlYyMDBhOCw4LDAsMCwwLTE2LDB2Ny42QTgwLjE1LDgwLjE1LDAsMCwxLDQ4LjQsMTM2SDU2YTgsOCwwLDAsMCwwLTE2SDQ4LjRBODAuMTUsODAuMTUsMCwwLDEsMTIwLDQ4LjRWNTZhOCw4LDAsMCwwLDE2LDBWNDguNEE4MC4xNSw4MC4xNSwwLDAsMSwyMDcuNiwxMjBIMjAwYTgsOCwwLDAsMCwwLDE2aDcuNkE4MC4xNSw4MC4xNSwwLDAsMSwxMzYsMjA3LjZaTTEyOCw4OGE0MCw0MCwwLDEsMCw0MCw0MEE0MCw0MCwwLDAsMCwxMjgsODhabTAsNjRhMjQsMjQsMCwxLDEsMjQtMjRBMjQsMjQsMCwwLDEsMTI4LDE1MloiLz48L3N2Zz4=)}.icon-dots-six{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik03Miw5MkExMiwxMiwwLDEsMSw2MCw4MCwxMiwxMiwwLDAsMSw3Miw5MlptNTYtMTJhMTIsMTIsMCwxLDAsMTIsMTJBMTIsMTIsMCwwLDAsMTI4LDgwWm02OCwyNGExMiwxMiwwLDEsMC0xMi0xMkExMiwxMiwwLDAsMCwxOTYsMTA0Wk02MCwxNTJhMTIsMTIsMCwxLDAsMTIsMTJBMTIsMTIsMCwwLDAsNjAsMTUyWm02OCwwYTEyLDEyLDAsMSwwLDEyLDEyQTEyLDEyLDAsMCwwLDEyOCwxNTJabTY4LDBhMTIsMTIsMCwxLDAsMTIsMTJBMTIsMTIsMCwwLDAsMTk2LDE1MloiLz48L3N2Zz4=);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik03Miw5MkExMiwxMiwwLDEsMSw2MCw4MCwxMiwxMiwwLDAsMSw3Miw5MlptNTYtMTJhMTIsMTIsMCwxLDAsMTIsMTJBMTIsMTIsMCwwLDAsMTI4LDgwWm02OCwyNGExMiwxMiwwLDEsMC0xMi0xMkExMiwxMiwwLDAsMCwxOTYsMTA0Wk02MCwxNTJhMTIsMTIsMCwxLDAsMTIsMTJBMTIsMTIsMCwwLDAsNjAsMTUyWm02OCwwYTEyLDEyLDAsMSwwLDEyLDEyQTEyLDEyLDAsMCwwLDEyOCwxNTJabTY4LDBhMTIsMTIsMCwxLDAsMTIsMTJBMTIsMTIsMCwwLDAsMTk2LDE1MloiLz48L3N2Zz4=)}.icon-envelope{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjQsNDhIMzJhOCw4LDAsMCwwLTgsOFYxOTJhMTYsMTYsMCwwLDAsMTYsMTZIMjE2YTE2LDE2LDAsMCwwLDE2LTE2VjU2QTgsOCwwLDAsMCwyMjQsNDhabS05Niw4NS4xNUw1Mi41Nyw2NEgyMDMuNDNaTTk4LjcxLDEyOCw0MCwxODEuODFWNzQuMTlabTExLjg0LDEwLjg1LDEyLDExLjA1YTgsOCwwLDAsMCwxMC44MiwwbDEyLTExLjA1LDU4LDUzLjE1SDUyLjU3Wk0xNTcuMjksMTI4LDIxNiw3NC4xOFYxODEuODJaIi8+PC9zdmc+);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjQsNDhIMzJhOCw4LDAsMCwwLTgsOFYxOTJhMTYsMTYsMCwwLDAsMTYsMTZIMjE2YTE2LDE2LDAsMCwwLDE2LTE2VjU2QTgsOCwwLDAsMCwyMjQsNDhabS05Niw4NS4xNUw1Mi41Nyw2NEgyMDMuNDNaTTk4LjcxLDEyOCw0MCwxODEuODFWNzQuMTlabTExLjg0LDEwLjg1LDEyLDExLjA1YTgsOCwwLDAsMCwxMC44MiwwbDEyLTExLjA1LDU4LDUzLjE1SDUyLjU3Wk0xNTcuMjksMTI4LDIxNiw3NC4xOFYxODEuODJaIi8+PC9zdmc+)}.icon-facebook-logo{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMjgsMjRBMTA0LDEwNCwwLDEsMCwyMzIsMTI4LDEwNC4xMSwxMDQuMTEsMCwwLDAsMTI4LDI0Wm04LDE5MS42M1YxNTJoMjRhOCw4LDAsMCwwLDAtMTZIMTM2VjExMmExNiwxNiwwLDAsMSwxNi0xNmgxNmE4LDgsMCwwLDAsMC0xNkgxNTJhMzIsMzIsMCwwLDAtMzIsMzJ2MjRIOTZhOCw4LDAsMCwwLDAsMTZoMjR2NjMuNjNhODgsODgsMCwxLDEsMTYsMFoiLz48L3N2Zz4=);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMjgsMjRBMTA0LDEwNCwwLDEsMCwyMzIsMTI4LDEwNC4xMSwxMDQuMTEsMCwwLDAsMTI4LDI0Wm04LDE5MS42M1YxNTJoMjRhOCw4LDAsMCwwLDAtMTZIMTM2VjExMmExNiwxNiwwLDAsMSwxNi0xNmgxNmE4LDgsMCwwLDAsMC0xNkgxNTJhMzIsMzIsMCwwLDAtMzIsMzJ2MjRIOTZhOCw4LDAsMCwwLDAsMTZoMjR2NjMuNjNhODgsODgsMCwxLDEsMTYsMFoiLz48L3N2Zz4=)}.icon-folder{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTYsNzJIMTMxLjMxTDEwNCw0NC42OUExNS44NiwxNS44NiwwLDAsMCw5Mi42OSw0MEg0MEExNiwxNiwwLDAsMCwyNCw1NlYyMDAuNjJBMTUuNCwxNS40LDAsMCwwLDM5LjM4LDIxNkgyMTYuODlBMTUuMTMsMTUuMTMsMCwwLDAsMjMyLDIwMC44OVY4OEExNiwxNiwwLDAsMCwyMTYsNzJaTTQwLDU2SDkyLjY5bDE2LDE2SDQwWk0yMTYsMjAwSDQwVjg4SDIxNloiLz48L3N2Zz4=);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMTYsNzJIMTMxLjMxTDEwNCw0NC42OUExNS44NiwxNS44NiwwLDAsMCw5Mi42OSw0MEg0MEExNiwxNiwwLDAsMCwyNCw1NlYyMDAuNjJBMTUuNCwxNS40LDAsMCwwLDM5LjM4LDIxNkgyMTYuODlBMTUuMTMsMTUuMTMsMCwwLDAsMjMyLDIwMC44OVY4OEExNiwxNiwwLDAsMCwyMTYsNzJaTTQwLDU2SDkyLjY5bDE2LDE2SDQwWk0yMTYsMjAwSDQwVjg4SDIxNloiLz48L3N2Zz4=)}.icon-google-logo{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjQsMTI4YTk2LDk2LDAsMSwxLTIxLjk1LTYxLjA5LDgsOCwwLDEsMS0xMi4zMywxMC4xOEE4MCw4MCwwLDEsMCwyMDcuNiwxMzZIMTI4YTgsOCwwLDAsMSwwLTE2aDg4QTgsOCwwLDAsMSwyMjQsMTI4WiIvPjwvc3ZnPg==);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjQsMTI4YTk2LDk2LDAsMSwxLTIxLjk1LTYxLjA5LDgsOCwwLDEsMS0xMi4zMywxMC4xOEE4MCw4MCwwLDEsMCwyMDcuNiwxMzZIMTI4YTgsOCwwLDAsMSwwLTE2aDg4QTgsOCwwLDAsMSwyMjQsMTI4WiIvPjwvc3ZnPg==)}.icon-hand-heart{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzAuMzMsMTQxLjA2YTI0LjM0LDI0LjM0LDAsMCwwLTE4LjYxLTQuNzdDMjMwLjUsMTE3LjMzLDI0MCw5OC40OCwyNDAsODBjMC0yNi40Ny0yMS4yOS00OC00Ny40Ni00OEE0Ny41OCw0Ny41OCwwLDAsMCwxNTYsNDguNzUsNDcuNTgsNDcuNTgsMCwwLDAsMTE5LjQ2LDMyQzkzLjI5LDMyLDcyLDUzLjUzLDcyLDgwYzAsMTEsMy4yNCwyMS42OSwxMC4wNiwzM2EzMS44NywzMS44NywwLDAsMC0xNC43NSw4LjRMNDQuNjksMTQ0SDE2QTE2LDE2LDAsMCwwLDAsMTYwdjQwYTE2LDE2LDAsMCwwLDE2LDE2SDEyMGE3LjkzLDcuOTMsMCwwLDAsMS45NC0uMjRsNjQtMTZhNi45NCw2Ljk0LDAsMCwwLDEuMTktLjRMMjI2LDE4Mi44MmwuNDQtLjJhMjQuNiwyNC42LDAsMCwwLDMuOTMtNDEuNTZaTTExOS40Niw0OEEzMS4xNSwzMS4xNSwwLDAsMSwxNDguNiw2N2E4LDgsMCwwLDAsMTQuOCwwLDMxLjE1LDMxLjE1LDAsMCwxLDI5LjE0LTE5QzIwOS41OSw0OCwyMjQsNjIuNjUsMjI0LDgwYzAsMTkuNTEtMTUuNzksNDEuNTgtNDUuNjYsNjMuOWwtMTEuMDksMi41NUEyOCwyOCwwLDAsMCwxNDAsMTEySDEwMC42OEM5Mi4wNSwxMDAuMzYsODgsOTAuMTIsODgsODAsODgsNjIuNjUsMTAyLjQxLDQ4LDExOS40Niw0OFpNMTYsMTYwSDQwdjQwSDE2Wm0yMDMuNDMsOC4yMS0zOCwxNi4xOEwxMTksMjAwSDU2VjE1NS4zMWwyMi42My0yMi42MkExNS44NiwxNS44NiwwLDAsMSw4OS45NCwxMjhIMTQwYTEyLDEyLDAsMCwxLDAsMjRIMTEyYTgsOCwwLDAsMCwwLDE2aDMyYTguMzIsOC4zMiwwLDAsMCwxLjc5LS4ybDY3LTE1LjQxLjMxLS4wOGE4LjYsOC42LDAsMCwxLDYuMywxNS45WiIvPjwvc3ZnPg==);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzAuMzMsMTQxLjA2YTI0LjM0LDI0LjM0LDAsMCwwLTE4LjYxLTQuNzdDMjMwLjUsMTE3LjMzLDI0MCw5OC40OCwyNDAsODBjMC0yNi40Ny0yMS4yOS00OC00Ny40Ni00OEE0Ny41OCw0Ny41OCwwLDAsMCwxNTYsNDguNzUsNDcuNTgsNDcuNTgsMCwwLDAsMTE5LjQ2LDMyQzkzLjI5LDMyLDcyLDUzLjUzLDcyLDgwYzAsMTEsMy4yNCwyMS42OSwxMC4wNiwzM2EzMS44NywzMS44NywwLDAsMC0xNC43NSw4LjRMNDQuNjksMTQ0SDE2QTE2LDE2LDAsMCwwLDAsMTYwdjQwYTE2LDE2LDAsMCwwLDE2LDE2SDEyMGE3LjkzLDcuOTMsMCwwLDAsMS45NC0uMjRsNjQtMTZhNi45NCw2Ljk0LDAsMCwwLDEuMTktLjRMMjI2LDE4Mi44MmwuNDQtLjJhMjQuNiwyNC42LDAsMCwwLDMuOTMtNDEuNTZaTTExOS40Niw0OEEzMS4xNSwzMS4xNSwwLDAsMSwxNDguNiw2N2E4LDgsMCwwLDAsMTQuOCwwLDMxLjE1LDMxLjE1LDAsMCwxLDI5LjE0LTE5QzIwOS41OSw0OCwyMjQsNjIuNjUsMjI0LDgwYzAsMTkuNTEtMTUuNzksNDEuNTgtNDUuNjYsNjMuOWwtMTEuMDksMi41NUEyOCwyOCwwLDAsMCwxNDAsMTEySDEwMC42OEM5Mi4wNSwxMDAuMzYsODgsOTAuMTIsODgsODAsODgsNjIuNjUsMTAyLjQxLDQ4LDExOS40Niw0OFpNMTYsMTYwSDQwdjQwSDE2Wm0yMDMuNDMsOC4yMS0zOCwxNi4xOEwxMTksMjAwSDU2VjE1NS4zMWwyMi42My0yMi42MkExNS44NiwxNS44NiwwLDAsMSw4OS45NCwxMjhIMTQwYTEyLDEyLDAsMCwxLDAsMjRIMTEyYTgsOCwwLDAsMCwwLDE2aDMyYTguMzIsOC4zMiwwLDAsMCwxLjc5LS4ybDY3LTE1LjQxLjMxLS4wOGE4LjYsOC42LDAsMCwxLDYuMywxNS45WiIvPjwvc3ZnPg==)}.icon-hourglass{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMDAsNzUuNjRWNDBhMTYsMTYsMCwwLDAtMTYtMTZINzJBMTYsMTYsMCwwLDAsNTYsNDBWNzZhMTYuMDcsMTYuMDcsMCwwLDAsNi40LDEyLjhMMTE0LjY3LDEyOCw2Mi40LDE2Ny4yQTE2LjA3LDE2LjA3LDAsMCwwLDU2LDE4MHYzNmExNiwxNiwwLDAsMCwxNiwxNkgxODRhMTYsMTYsMCwwLDAsMTYtMTZWMTgwLjM2YTE2LjA5LDE2LjA5LDAsMCwwLTYuMzUtMTIuNzdMMTQxLjI3LDEyOGw1Mi4zOC0zOS42QTE2LjA1LDE2LjA1LDAsMCwwLDIwMCw3NS42NFpNMTg0LDIxNkg3MlYxODBsNTYtNDIsNTYsNDIuMzVabTAtMTQwLjM2TDEyOCwxMTgsNzIsNzZWNDBIMTg0WiIvPjwvc3ZnPg==);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMDAsNzUuNjRWNDBhMTYsMTYsMCwwLDAtMTYtMTZINzJBMTYsMTYsMCwwLDAsNTYsNDBWNzZhMTYuMDcsMTYuMDcsMCwwLDAsNi40LDEyLjhMMTE0LjY3LDEyOCw2Mi40LDE2Ny4yQTE2LjA3LDE2LjA3LDAsMCwwLDU2LDE4MHYzNmExNiwxNiwwLDAsMCwxNiwxNkgxODRhMTYsMTYsMCwwLDAsMTYtMTZWMTgwLjM2YTE2LjA5LDE2LjA5LDAsMCwwLTYuMzUtMTIuNzdMMTQxLjI3LDEyOGw1Mi4zOC0zOS42QTE2LjA1LDE2LjA1LDAsMCwwLDIwMCw3NS42NFpNMTg0LDIxNkg3MlYxODBsNTYtNDIsNTYsNDIuMzVabTAtMTQwLjM2TDEyOCwxMTgsNzIsNzZWNDBIMTg0WiIvPjwvc3ZnPg==)}.icon-instagram-logo{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMjgsODBhNDgsNDgsMCwxLDAsNDgsNDhBNDguMDUsNDguMDUsMCwwLDAsMTI4LDgwWm0wLDgwYTMyLDMyLDAsMSwxLDMyLTMyQTMyLDMyLDAsMCwxLDEyOCwxNjBaTTE3NiwyNEg4MEE1Ni4wNiw1Ni4wNiwwLDAsMCwyNCw4MHY5NmE1Ni4wNiw1Ni4wNiwwLDAsMCw1Niw1Nmg5NmE1Ni4wNiw1Ni4wNiwwLDAsMCw1Ni01NlY4MEE1Ni4wNiw1Ni4wNiwwLDAsMCwxNzYsMjRabTQwLDE1MmE0MCw0MCwwLDAsMS00MCw0MEg4MGE0MCw0MCwwLDAsMS00MC00MFY4MEE0MCw0MCwwLDAsMSw4MCw0MGg5NmE0MCw0MCwwLDAsMSw0MCw0MFpNMTkyLDc2YTEyLDEyLDAsMSwxLTEyLTEyQTEyLDEyLDAsMCwxLDE5Miw3NloiLz48L3N2Zz4=);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xMjgsODBhNDgsNDgsMCwxLDAsNDgsNDhBNDguMDUsNDguMDUsMCwwLDAsMTI4LDgwWm0wLDgwYTMyLDMyLDAsMSwxLDMyLTMyQTMyLDMyLDAsMCwxLDEyOCwxNjBaTTE3NiwyNEg4MEE1Ni4wNiw1Ni4wNiwwLDAsMCwyNCw4MHY5NmE1Ni4wNiw1Ni4wNiwwLDAsMCw1Niw1Nmg5NmE1Ni4wNiw1Ni4wNiwwLDAsMCw1Ni01NlY4MEE1Ni4wNiw1Ni4wNiwwLDAsMCwxNzYsMjRabTQwLDE1MmE0MCw0MCwwLDAsMS00MCw0MEg4MGE0MCw0MCwwLDAsMS00MC00MFY4MEE0MCw0MCwwLDAsMSw4MCw0MGg5NmE0MCw0MCwwLDAsMSw0MCw0MFpNMTkyLDc2YTEyLDEyLDAsMSwxLTEyLTEyQTEyLDEyLDAsMCwxLDE5Miw3NloiLz48L3N2Zz4=)}.icon-link{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yNDAsODguMjNhNTQuNDMsNTQuNDMsMCwwLDEtMTYsMzdMMTg5LjI1LDE2MGE1NC4yNyw1NC4yNywwLDAsMS0zOC42MywxNmgtLjA1QTU0LjYzLDU0LjYzLDAsMCwxLDk2LDExOS44NGE4LDgsMCwwLDEsMTYsLjQ1QTM4LjYyLDM4LjYyLDAsMCwwLDE1MC41OCwxNjBoMGEzOC4zOSwzOC4zOSwwLDAsMCwyNy4zMS0xMS4zMWwzNC43NS0zNC43NWEzOC42MywzOC42MywwLDAsMC01NC42My01NC42M2wtMTEsMTFBOCw4LDAsMCwxLDEzNS43LDU5bDExLTExQTU0LjY1LDU0LjY1LDAsMCwxLDIyNCw0OCw1NC44Niw1NC44NiwwLDAsMSwyNDAsODguMjNaTTEwOSwxODUuNjZsLTExLDExQTM4LjQxLDM4LjQxLDAsMCwxLDcwLjYsMjA4aDBhMzguNjMsMzguNjMsMCwwLDEtMjcuMjktNjUuOTRMNzgsMTA3LjMxQTM4LjYzLDM4LjYzLDAsMCwxLDE0NCwxMzUuNzFhOCw4LDAsMCwwLDE2LC40NUE1NC44Niw1NC44NiwwLDAsMCwxNDQsOTZhNTQuNjUsNTQuNjUsMCwwLDAtNzcuMjcsMEwzMiwxMzAuNzVBNTQuNjIsNTQuNjIsMCwwLDAsNzAuNTYsMjI0aDBhNTQuMjgsNTQuMjgsMCwwLDAsMzguNjQtMTZsMTEtMTFBOCw4LDAsMCwwLDEwOSwxODUuNjZaIi8+PC9zdmc+);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yNDAsODguMjNhNTQuNDMsNTQuNDMsMCwwLDEtMTYsMzdMMTg5LjI1LDE2MGE1NC4yNyw1NC4yNywwLDAsMS0zOC42MywxNmgtLjA1QTU0LjYzLDU0LjYzLDAsMCwxLDk2LDExOS44NGE4LDgsMCwwLDEsMTYsLjQ1QTM4LjYyLDM4LjYyLDAsMCwwLDE1MC41OCwxNjBoMGEzOC4zOSwzOC4zOSwwLDAsMCwyNy4zMS0xMS4zMWwzNC43NS0zNC43NWEzOC42MywzOC42MywwLDAsMC01NC42My01NC42M2wtMTEsMTFBOCw4LDAsMCwxLDEzNS43LDU5bDExLTExQTU0LjY1LDU0LjY1LDAsMCwxLDIyNCw0OCw1NC44Niw1NC44NiwwLDAsMSwyNDAsODguMjNaTTEwOSwxODUuNjZsLTExLDExQTM4LjQxLDM4LjQxLDAsMCwxLDcwLjYsMjA4aDBhMzguNjMsMzguNjMsMCwwLDEtMjcuMjktNjUuOTRMNzgsMTA3LjMxQTM4LjYzLDM4LjYzLDAsMCwxLDE0NCwxMzUuNzFhOCw4LDAsMCwwLDE2LC40NUE1NC44Niw1NC44NiwwLDAsMCwxNDQsOTZhNTQuNjUsNTQuNjUsMCwwLDAtNzcuMjcsMEwzMiwxMzAuNzVBNTQuNjIsNTQuNjIsMCwwLDAsNzAuNTYsMjI0aDBhNTQuMjgsNTQuMjgsMCwwLDAsMzguNjQtMTZsMTEtMTFBOCw4LDAsMCwwLDEwOSwxODUuNjZaIi8+PC9zdmc+)}.icon-magic-wand{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik00OCw2NGE4LDgsMCwwLDEsOC04SDcyVjQwYTgsOCwwLDAsMSwxNiwwVjU2aDE2YTgsOCwwLDAsMSwwLDE2SDg4Vjg4YTgsOCwwLDAsMS0xNiwwVjcySDU2QTgsOCwwLDAsMSw0OCw2NFpNMTg0LDE5MmgtOHYtOGE4LDgsMCwwLDAtMTYsMHY4aC04YTgsOCwwLDAsMCwwLDE2aDh2OGE4LDgsMCwwLDAsMTYsMHYtOGg4YTgsOCwwLDAsMCwwLTE2Wm01Ni00OEgyMjRWMTI4YTgsOCwwLDAsMC0xNiwwdjE2SDE5MmE4LDgsMCwwLDAsMCwxNmgxNnYxNmE4LDgsMCwwLDAsMTYsMFYxNjBoMTZhOCw4LDAsMCwwLDAtMTZaTTIxOS4zMSw4MCw4MCwyMTkuMzFhMTYsMTYsMCwwLDEtMjIuNjIsMEwzNi42OCwxOTguNjNhMTYsMTYsMCwwLDEsMC0yMi42M0wxNzYsMzYuNjlhMTYsMTYsMCwwLDEsMjIuNjMsMGwyMC42OCwyMC42OEExNiwxNiwwLDAsMSwyMTkuMzEsODBabS01NC42MywzMkwxNDQsOTEuMzFsLTk2LDk2TDY4LjY4LDIwOFpNMjA4LDY4LjY5LDE4Ny4zMSw0OGwtMzIsMzJMMTc2LDEwMC42OVoiLz48L3N2Zz4=);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik00OCw2NGE4LDgsMCwwLDEsOC04SDcyVjQwYTgsOCwwLDAsMSwxNiwwVjU2aDE2YTgsOCwwLDAsMSwwLDE2SDg4Vjg4YTgsOCwwLDAsMS0xNiwwVjcySDU2QTgsOCwwLDAsMSw0OCw2NFpNMTg0LDE5MmgtOHYtOGE4LDgsMCwwLDAtMTYsMHY4aC04YTgsOCwwLDAsMCwwLDE2aDh2OGE4LDgsMCwwLDAsMTYsMHYtOGg4YTgsOCwwLDAsMCwwLTE2Wm01Ni00OEgyMjRWMTI4YTgsOCwwLDAsMC0xNiwwdjE2SDE5MmE4LDgsMCwwLDAsMCwxNmgxNnYxNmE4LDgsMCwwLDAsMTYsMFYxNjBoMTZhOCw4LDAsMCwwLDAtMTZaTTIxOS4zMSw4MCw4MCwyMTkuMzFhMTYsMTYsMCwwLDEtMjIuNjIsMEwzNi42OCwxOTguNjNhMTYsMTYsMCwwLDEsMC0yMi42M0wxNzYsMzYuNjlhMTYsMTYsMCwwLDEsMjIuNjMsMGwyMC42OCwyMC42OEExNiwxNiwwLDAsMSwyMTkuMzEsODBabS01NC42MywzMkwxNDQsOTEuMzFsLTk2LDk2TDY4LjY4LDIwOFpNMjA4LDY4LjY5LDE4Ny4zMSw0OGwtMzIsMzJMMTc2LDEwMC42OVoiLz48L3N2Zz4=)}.icon-person{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xNjAsNDBhMzIsMzIsMCwxLDAtMzIsMzJBMzIsMzIsMCwwLDAsMTYwLDQwWk0xMjgsNTZhMTYsMTYsMCwxLDEsMTYtMTZBMTYsMTYsMCwwLDEsMTI4LDU2Wm05MC4zNCw3OC4wNUwxNzMuMTcsODIuODNhMzIsMzIsMCwwLDAtMjQtMTAuODNIMTA2LjgzYTMyLDMyLDAsMCwwLTI0LDEwLjgzTDM3LjY2LDEzNC4wNWEyMCwyMCwwLDAsMCwyOC4xMywyOC40M2wxNi4zLTEzLjA4TDY1LjU1LDIxMi4yOEEyMCwyMCwwLDAsMCwxMDIsMjI4LjhsMjYtNDQuODcsMjYsNDQuODdhMjAsMjAsMCwwLDAsMzYuNDEtMTYuNTJMMTczLjkxLDE0OS40bDE2LjMsMTMuMDhhMjAsMjAsMCwwLDAsMjguMTMtMjguNDNabS0xMS41MSwxNi43N2E0LDQsMCwwLDEtNS42NiwwYy0uMjEtLjItLjQyLS40LS42NS0uNThMMTY1LDEyMS43NkE4LDgsMCwwLDAsMTUyLjI2LDEzMEwxNzUuMTQsMjE3YTcuNzIsNy43MiwwLDAsMCwuNDgsMS4zNSw0LDQsMCwxLDEtNy4yNSwzLjM4LDYuMjUsNi4yNSwwLDAsMC0uMzMtLjYzTDEzNC45MiwxNjRhOCw4LDAsMCwwLTEzLjg0LDBMODgsMjIxLjA1YTYuMjUsNi4yNSwwLDAsMC0uMzMuNjMsNCw0LDAsMCwxLTIuMjYsMi4wNyw0LDQsMCwwLDEtNS01LjQ1LDcuNzIsNy43MiwwLDAsMCwuNDgtMS4zNUwxMDMuNzQsMTMwQTgsOCwwLDAsMCw5MSwxMjEuNzZMNTUuNDgsMTUwLjI0Yy0uMjMuMTgtLjQ0LjM4LS42NS41OGE0LDQsMCwxLDEtNS42Ni01LjY1Yy4xMi0uMTIuMjMtLjI0LjM0LS4zN0w5NC44Myw5My40MWExNiwxNiwwLDAsMSwxMi01LjQxaDQyLjM0YTE2LDE2LDAsMCwxLDEyLDUuNDFsNDUuMzIsNTEuMzljLjExLjEzLjIyLjI1LjM0LjM3QTQsNCwwLDAsMSwyMDYuODMsMTUwLjgyWiIvPjwvc3ZnPg==);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xNjAsNDBhMzIsMzIsMCwxLDAtMzIsMzJBMzIsMzIsMCwwLDAsMTYwLDQwWk0xMjgsNTZhMTYsMTYsMCwxLDEsMTYtMTZBMTYsMTYsMCwwLDEsMTI4LDU2Wm05MC4zNCw3OC4wNUwxNzMuMTcsODIuODNhMzIsMzIsMCwwLDAtMjQtMTAuODNIMTA2LjgzYTMyLDMyLDAsMCwwLTI0LDEwLjgzTDM3LjY2LDEzNC4wNWEyMCwyMCwwLDAsMCwyOC4xMywyOC40M2wxNi4zLTEzLjA4TDY1LjU1LDIxMi4yOEEyMCwyMCwwLDAsMCwxMDIsMjI4LjhsMjYtNDQuODcsMjYsNDQuODdhMjAsMjAsMCwwLDAsMzYuNDEtMTYuNTJMMTczLjkxLDE0OS40bDE2LjMsMTMuMDhhMjAsMjAsMCwwLDAsMjguMTMtMjguNDNabS0xMS41MSwxNi43N2E0LDQsMCwwLDEtNS42NiwwYy0uMjEtLjItLjQyLS40LS42NS0uNThMMTY1LDEyMS43NkE4LDgsMCwwLDAsMTUyLjI2LDEzMEwxNzUuMTQsMjE3YTcuNzIsNy43MiwwLDAsMCwuNDgsMS4zNSw0LDQsMCwxLDEtNy4yNSwzLjM4LDYuMjUsNi4yNSwwLDAsMC0uMzMtLjYzTDEzNC45MiwxNjRhOCw4LDAsMCwwLTEzLjg0LDBMODgsMjIxLjA1YTYuMjUsNi4yNSwwLDAsMC0uMzMuNjMsNCw0LDAsMCwxLTIuMjYsMi4wNyw0LDQsMCwwLDEtNS01LjQ1LDcuNzIsNy43MiwwLDAsMCwuNDgtMS4zNUwxMDMuNzQsMTMwQTgsOCwwLDAsMCw5MSwxMjEuNzZMNTUuNDgsMTUwLjI0Yy0uMjMuMTgtLjQ0LjM4LS42NS41OGE0LDQsMCwxLDEtNS42Ni01LjY1Yy4xMi0uMTIuMjMtLjI0LjM0LS4zN0w5NC44Myw5My40MWExNiwxNiwwLDAsMSwxMi01LjQxaDQyLjM0YTE2LDE2LDAsMCwxLDEyLDUuNDFsNDUuMzIsNTEuMzljLjExLjEzLjIyLjI1LjM0LjM3QTQsNCwwLDAsMSwyMDYuODMsMTUwLjgyWiIvPjwvc3ZnPg==)}.icon-phone{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjIuMzcsMTU4LjQ2bC00Ny4xMS0yMS4xMS0uMTMtLjA2YTE2LDE2LDAsMCwwLTE1LjE3LDEuNCw4LjEyLDguMTIsMCwwLDAtLjc1LjU2TDEzNC44NywxNjBjLTE1LjQyLTcuNDktMzEuMzQtMjMuMjktMzguODMtMzguNTFsMjAuNzgtMjQuNzFjLjItLjI1LjM5LS41LjU3LS43N2ExNiwxNiwwLDAsMCwxLjMyLTE1LjA2bDAtLjEyTDk3LjU0LDMzLjY0YTE2LDE2LDAsMCwwLTE2LjYyLTkuNTJBNTYuMjYsNTYuMjYsMCwwLDAsMzIsODBjMCw3OS40LDY0LjYsMTQ0LDE0NCwxNDRhNTYuMjYsNTYuMjYsMCwwLDAsNTUuODgtNDguOTJBMTYsMTYsMCwwLDAsMjIyLjM3LDE1OC40NlpNMTc2LDIwOEExMjguMTQsMTI4LjE0LDAsMCwxLDQ4LDgwLDQwLjIsNDAuMiwwLDAsMSw4Mi44Nyw0MGEuNjEuNjEsMCwwLDAsMCwuMTJsMjEsNDdMODMuMiwxMTEuODZhNi4xMyw2LjEzLDAsMCwwLS41Ny43NywxNiwxNiwwLDAsMC0xLDE1LjdjOS4wNiwxOC41MywyNy43MywzNy4wNiw0Ni40Niw0Ni4xMWExNiwxNiwwLDAsMCwxNS43NS0xLjE0LDguNDQsOC40NCwwLDAsMCwuNzQtLjU2TDE2OC44OSwxNTJsNDcsMjEuMDVoMHMuMDgsMCwuMTEsMEE0MC4yMSw0MC4yMSwwLDAsMSwxNzYsMjA4WiIvPjwvc3ZnPg==);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjIuMzcsMTU4LjQ2bC00Ny4xMS0yMS4xMS0uMTMtLjA2YTE2LDE2LDAsMCwwLTE1LjE3LDEuNCw4LjEyLDguMTIsMCwwLDAtLjc1LjU2TDEzNC44NywxNjBjLTE1LjQyLTcuNDktMzEuMzQtMjMuMjktMzguODMtMzguNTFsMjAuNzgtMjQuNzFjLjItLjI1LjM5LS41LjU3LS43N2ExNiwxNiwwLDAsMCwxLjMyLTE1LjA2bDAtLjEyTDk3LjU0LDMzLjY0YTE2LDE2LDAsMCwwLTE2LjYyLTkuNTJBNTYuMjYsNTYuMjYsMCwwLDAsMzIsODBjMCw3OS40LDY0LjYsMTQ0LDE0NCwxNDRhNTYuMjYsNTYuMjYsMCwwLDAsNTUuODgtNDguOTJBMTYsMTYsMCwwLDAsMjIyLjM3LDE1OC40NlpNMTc2LDIwOEExMjguMTQsMTI4LjE0LDAsMCwxLDQ4LDgwLDQwLjIsNDAuMiwwLDAsMSw4Mi44Nyw0MGEuNjEuNjEsMCwwLDAsMCwuMTJsMjEsNDdMODMuMiwxMTEuODZhNi4xMyw2LjEzLDAsMCwwLS41Ny43NywxNiwxNiwwLDAsMC0xLDE1LjdjOS4wNiwxOC41MywyNy43MywzNy4wNiw0Ni40Niw0Ni4xMWExNiwxNiwwLDAsMCwxNS43NS0xLjE0LDguNDQsOC40NCwwLDAsMCwuNzQtLjU2TDE2OC44OSwxNTJsNDcsMjEuMDVoMHMuMDgsMCwuMTEsMEE0MC4yMSw0MC4yMSwwLDAsMSwxNzYsMjA4WiIvPjwvc3ZnPg==)}.icon-question{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xNDAsMTgwYTEyLDEyLDAsMSwxLTEyLTEyQTEyLDEyLDAsMCwxLDE0MCwxODBaTTEyOCw3MmMtMjIuMDYsMC00MCwxNi4xNS00MCwzNnY0YTgsOCwwLDAsMCwxNiwwdi00YzAtMTEsMTAuNzctMjAsMjQtMjBzMjQsOSwyNCwyMC0xMC43NywyMC0yNCwyMGE4LDgsMCwwLDAtOCw4djhhOCw4LDAsMCwwLDE2LDB2LS43MmMxOC4yNC0zLjM1LDMyLTE3LjksMzItMzUuMjhDMTY4LDg4LjE1LDE1MC4wNiw3MiwxMjgsNzJabTEwNCw1NkExMDQsMTA0LDAsMSwxLDEyOCwyNCwxMDQuMTEsMTA0LjExLDAsMCwxLDIzMiwxMjhabS0xNiwwYTg4LDg4LDAsMSwwLTg4LDg4QTg4LjEsODguMSwwLDAsMCwyMTYsMTI4WiIvPjwvc3ZnPg==);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xNDAsMTgwYTEyLDEyLDAsMSwxLTEyLTEyQTEyLDEyLDAsMCwxLDE0MCwxODBaTTEyOCw3MmMtMjIuMDYsMC00MCwxNi4xNS00MCwzNnY0YTgsOCwwLDAsMCwxNiwwdi00YzAtMTEsMTAuNzctMjAsMjQtMjBzMjQsOSwyNCwyMC0xMC43NywyMC0yNCwyMGE4LDgsMCwwLDAtOCw4djhhOCw4LDAsMCwwLDE2LDB2LS43MmMxOC4yNC0zLjM1LDMyLTE3LjksMzItMzUuMjhDMTY4LDg4LjE1LDE1MC4wNiw3MiwxMjgsNzJabTEwNCw1NkExMDQsMTA0LDAsMSwxLDEyOCwyNCwxMDQuMTEsMTA0LjExLDAsMCwxLDIzMiwxMjhabS0xNiwwYTg4LDg4LDAsMSwwLTg4LDg4QTg4LjEsODguMSwwLDAsMCwyMTYsMTI4WiIvPjwvc3ZnPg==)}.icon-star{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzkuMTgsOTcuMjZBMTYuMzgsMTYuMzgsMCwwLDAsMjI0LjkyLDg2bC01OS00Ljc2TDE0My4xNCwyNi4xNWExNi4zNiwxNi4zNiwwLDAsMC0zMC4yNywwTDkwLjExLDgxLjIzLDMxLjA4LDg2YTE2LjQ2LDE2LjQ2LDAsMCwwLTkuMzcsMjguODZsNDUsMzguODNMNTMsMjExLjc1YTE2LjM4LDE2LjM4LDAsMCwwLDI0LjUsMTcuODJMMTI4LDE5OC40OWw1MC41MywzMS4wOEExNi40LDE2LjQsMCwwLDAsMjAzLDIxMS43NWwtMTMuNzYtNTguMDcsNDUtMzguODNBMTYuNDMsMTYuNDMsMCwwLDAsMjM5LjE4LDk3LjI2Wm0tMTUuMzQsNS40Ny00OC43LDQyYTgsOCwwLDAsMC0yLjU2LDcuOTFsMTQuODgsNjIuOGEuMzcuMzcsMCwwLDEtLjE3LjQ4Yy0uMTguMTQtLjIzLjExLS4zOCwwbC01NC43Mi0zMy42NWE4LDgsMCwwLDAtOC4zOCwwTDY5LjA5LDIxNS45NGMtLjE1LjA5LS4xOS4xMi0uMzgsMGEuMzcuMzcsMCwwLDEtLjE3LS40OGwxNC44OC02Mi44YTgsOCwwLDAsMC0yLjU2LTcuOTFsLTQ4LjctNDJjLS4xMi0uMS0uMjMtLjE5LS4xMy0uNXMuMTgtLjI3LjMzLS4yOWw2My45Mi01LjE2QTgsOCwwLDAsMCwxMDMsOTEuODZsMjQuNjItNTkuNjFjLjA4LS4xNy4xMS0uMjUuMzUtLjI1cy4yNy4wOC4zNS4yNUwxNTMsOTEuODZhOCw4LDAsMCwwLDYuNzUsNC45Mmw2My45Miw1LjE2Yy4xNSwwLC4yNCwwLC4zMy4yOVMyMjQsMTAyLjYzLDIyMy44NCwxMDIuNzNaIi8+PC9zdmc+);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzkuMTgsOTcuMjZBMTYuMzgsMTYuMzgsMCwwLDAsMjI0LjkyLDg2bC01OS00Ljc2TDE0My4xNCwyNi4xNWExNi4zNiwxNi4zNiwwLDAsMC0zMC4yNywwTDkwLjExLDgxLjIzLDMxLjA4LDg2YTE2LjQ2LDE2LjQ2LDAsMCwwLTkuMzcsMjguODZsNDUsMzguODNMNTMsMjExLjc1YTE2LjM4LDE2LjM4LDAsMCwwLDI0LjUsMTcuODJMMTI4LDE5OC40OWw1MC41MywzMS4wOEExNi40LDE2LjQsMCwwLDAsMjAzLDIxMS43NWwtMTMuNzYtNTguMDcsNDUtMzguODNBMTYuNDMsMTYuNDMsMCwwLDAsMjM5LjE4LDk3LjI2Wm0tMTUuMzQsNS40Ny00OC43LDQyYTgsOCwwLDAsMC0yLjU2LDcuOTFsMTQuODgsNjIuOGEuMzcuMzcsMCwwLDEtLjE3LjQ4Yy0uMTguMTQtLjIzLjExLS4zOCwwbC01NC43Mi0zMy42NWE4LDgsMCwwLDAtOC4zOCwwTDY5LjA5LDIxNS45NGMtLjE1LjA5LS4xOS4xMi0uMzgsMGEuMzcuMzcsMCwwLDEtLjE3LS40OGwxNC44OC02Mi44YTgsOCwwLDAsMC0yLjU2LTcuOTFsLTQ4LjctNDJjLS4xMi0uMS0uMjMtLjE5LS4xMy0uNXMuMTgtLjI3LjMzLS4yOWw2My45Mi01LjE2QTgsOCwwLDAsMCwxMDMsOTEuODZsMjQuNjItNTkuNjFjLjA4LS4xNy4xMS0uMjUuMzUtLjI1cy4yNy4wOC4zNS4yNUwxNTMsOTEuODZhOCw4LDAsMCwwLDYuNzUsNC45Mmw2My45Miw1LjE2Yy4xNSwwLC4yNCwwLC4zMy4yOVMyMjQsMTAyLjYzLDIyMy44NCwxMDIuNzNaIi8+PC9zdmc+)}.icon-tiktok-logo{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjQsNzJhNDguMDUsNDguMDUsMCwwLDEtNDgtNDgsOCw4LDAsMCwwLTgtOEgxMjhhOCw4LDAsMCwwLTgsOFYxNTZhMjAsMjAsMCwxLDEtMjguNTctMTguMDhBOCw4LDAsMCwwLDk2LDEzMC42OVY4OGE4LDgsMCwwLDAtOS40LTcuODhDNTAuOTEsODYuNDgsMjQsMTE5LjEsMjQsMTU2YTc2LDc2LDAsMCwwLDE1MiwwVjExNi4yOUExMDMuMjUsMTAzLjI1LDAsMCwwLDIyNCwxMjhhOCw4LDAsMCwwLDgtOFY4MEE4LDgsMCwwLDAsMjI0LDcyWm0tOCwzOS42NGE4Ny4xOSw4Ny4xOSwwLDAsMS00My4zMy0xNi4xNUE4LDgsMCwwLDAsMTYwLDEwMnY1NGE2MCw2MCwwLDAsMS0xMjAsMGMwLTI1LjksMTYuNjQtNDkuMTMsNDAtNTcuNnYyNy42N0EzNiwzNiwwLDEsMCwxMzYsMTU2VjMyaDI0LjVBNjQuMTQsNjQuMTQsMCwwLDAsMjE2LDg3LjVaIi8+PC9zdmc+);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMjQsNzJhNDguMDUsNDguMDUsMCwwLDEtNDgtNDgsOCw4LDAsMCwwLTgtOEgxMjhhOCw4LDAsMCwwLTgsOFYxNTZhMjAsMjAsMCwxLDEtMjguNTctMTguMDhBOCw4LDAsMCwwLDk2LDEzMC42OVY4OGE4LDgsMCwwLDAtOS40LTcuODhDNTAuOTEsODYuNDgsMjQsMTE5LjEsMjQsMTU2YTc2LDc2LDAsMCwwLDE1MiwwVjExNi4yOUExMDMuMjUsMTAzLjI1LDAsMCwwLDIyNCwxMjhhOCw4LDAsMCwwLDgtOFY4MEE4LDgsMCwwLDAsMjI0LDcyWm0tOCwzOS42NGE4Ny4xOSw4Ny4xOSwwLDAsMS00My4zMy0xNi4xNUE4LDgsMCwwLDAsMTYwLDEwMnY1NGE2MCw2MCwwLDAsMS0xMjAsMGMwLTI1LjksMTYuNjQtNDkuMTMsNDAtNTcuNnYyNy42N0EzNiwzNiwwLDEsMCwxMzYsMTU2VjMyaDI0LjVBNjQuMTQsNjQuMTQsMCwwLDAsMjE2LDg3LjVaIi8+PC9zdmc+)}.icon-user{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzAuOTIsMjEyYy0xNS4yMy0yNi4zMy0zOC43LTQ1LjIxLTY2LjA5LTU0LjE2YTcyLDcyLDAsMSwwLTczLjY2LDBDNjMuNzgsMTY2Ljc4LDQwLjMxLDE4NS42NiwyNS4wOCwyMTJhOCw4LDAsMSwwLDEzLjg1LDhjMTguODQtMzIuNTYsNTIuMTQtNTIsODkuMDctNTJzNzAuMjMsMTkuNDQsODkuMDcsNTJhOCw4LDAsMSwwLDEzLjg1LThaTTcyLDk2YTU2LDU2LDAsMSwxLDU2LDU2QTU2LjA2LDU2LjA2LDAsMCwxLDcyLDk2WiIvPjwvc3ZnPg==);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMzAuOTIsMjEyYy0xNS4yMy0yNi4zMy0zOC43LTQ1LjIxLTY2LjA5LTU0LjE2YTcyLDcyLDAsMSwwLTczLjY2LDBDNjMuNzgsMTY2Ljc4LDQwLjMxLDE4NS42NiwyNS4wOCwyMTJhOCw4LDAsMSwwLDEzLjg1LDhjMTguODQtMzIuNTYsNTIuMTQtNTIsODkuMDctNTJzNzAuMjMsMTkuNDQsODkuMDcsNTJhOCw4LDAsMSwwLDEzLjg1LThaTTcyLDk2YTU2LDU2LDAsMSwxLDU2LDU2QTU2LjA2LDU2LjA2LDAsMCwxLDcyLDk2WiIvPjwvc3ZnPg==)}.icon-x{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMDUuNjYsMTk0LjM0YTgsOCwwLDAsMS0xMS4zMiwxMS4zMkwxMjgsMTM5LjMxLDYxLjY2LDIwNS42NmE4LDgsMCwwLDEtMTEuMzItMTEuMzJMMTE2LjY5LDEyOCw1MC4zNCw2MS42NkE4LDgsMCwwLDEsNjEuNjYsNTAuMzRMMTI4LDExNi42OWw2Ni4zNC02Ni4zNWE4LDgsMCwwLDEsMTEuMzIsMTEuMzJMMTM5LjMxLDEyOFoiLz48L3N2Zz4=);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0yMDUuNjYsMTk0LjM0YTgsOCwwLDAsMS0xMS4zMiwxMS4zMkwxMjgsMTM5LjMxLDYxLjY2LDIwNS42NmE4LDgsMCwwLDEtMTEuMzItMTEuMzJMMTE2LjY5LDEyOCw1MC4zNCw2MS42NkE4LDgsMCwwLDEsNjEuNjYsNTAuMzRMMTI4LDExNi42OWw2Ni4zNC02Ni4zNWE4LDgsMCwwLDEsMTEuMzIsMTEuMzJMMTM5LjMxLDEyOFoiLz48L3N2Zz4=)}.icon-x-circle{mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xNjUuNjYsMTAxLjY2LDEzOS4zMSwxMjhsMjYuMzUsMjYuMzRhOCw4LDAsMCwxLTExLjMyLDExLjMyTDEyOCwxMzkuMzFsLTI2LjM0LDI2LjM1YTgsOCwwLDAsMS0xMS4zMi0xMS4zMkwxMTYuNjksMTI4LDkwLjM0LDEwMS42NmE4LDgsMCwwLDEsMTEuMzItMTEuMzJMMTI4LDExNi42OWwyNi4zNC0yNi4zNWE4LDgsMCwwLDEsMTEuMzIsMTEuMzJaTTIzMiwxMjhBMTA0LDEwNCwwLDEsMSwxMjgsMjQsMTA0LjExLDEwNC4xMSwwLDAsMSwyMzIsMTI4Wm0tMTYsMGE4OCw4OCwwLDEsMC04OCw4OEE4OC4xLDg4LjEsMCwwLDAsMjE2LDEyOFoiLz48L3N2Zz4=);-webkit-mask-image:url(data:image/svg+xml;base64,PHN2ZyB4bWxucz0iaHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmciIHZpZXdCb3g9IjAgMCAyNTYgMjU2IiBmaWxsPSJjdXJyZW50Q29sb3IiPjxwYXRoIGQ9Ik0xNjUuNjYsMTAxLjY2LDEzOS4zMSwxMjhsMjYuMzUsMjYuMzRhOCw4LDAsMCwxLTExLjMyLDExLjMyTDEyOCwxMzkuMzFsLTI2LjM0LDI2LjM1YTgsOCwwLDAsMS0xMS4zMi0xMS4zMkwxMTYuNjksMTI4LDkwLjM0LDEwMS42NmE4LDgsMCwwLDEsMTEuMzItMTEuMzJMMTI4LDExNi42OWwyNi4zNC0yNi4zNWE4LDgsMCwwLDEsMTEuMzIsMTEuMzJaTTIzMiwxMjhBMTA0LDEwNCwwLDEsMSwxMjgsMjQsMTA0LjExLDEwNC4xMSwwLDAsMSwyMzIsMTI4Wm0tMTYsMGE4OCw4OCwwLDEsMC04OCw4OEE4OC4xLDg4LjEsMCwwLDAsMjE2LDEyOFoiLz48L3N2Zz4=)}
\ No newline at end of file
diff --git a/assets/css/nav.min.css b/assets/css/nav.min.css
index e44a3cd..7dd5b31 100644
--- a/assets/css/nav.min.css
+++ b/assets/css/nav.min.css
@@ -1 +1 @@
-nav{--py:.25rem;--px:1rem;max-width:100%;font-family:var(--heading)}nav,nav a,nav li,nav ol,nav ul{height:var(--height);display:flex;justify-content:var(--justify);align-items:var(--align);gap:var(--gap);flex-wrap:var(--wrap);flex-direction:var(--dir)}ul.socials{--w:1.2em;--height:fit-content;gap:.5rem;display:flex;justify-content:center;align-items:center;flex-wrap:nowrap;flex-direction:row;overflow:auto hidden;touch-action:pan-x;list-style:none}nav:not(:has(>ul)),nav>ul{--justify:flex-start;--align:center;--wrap:nowrap;--w:1em;--dir:row;position:relative;overflow:auto hidden;touch-action:pan-x}nav a{padding:0 var(--px);white-space:nowrap;text-transform:uppercase}nav .current a,nav a.current,nav a:focus,nav a:focus:visited,nav a:hover,nav a:hover:visited{background-color:var(--action-0);color:var(--action-contrast);transition:background-color var(--transition-base),color var(--transition-base)}nav ol,nav ul{list-style:none;margin:0;padding:0}.has-submenu button:hover,nav a:hover{background-color:var(--action-0);color:var(--action-contrast)}.has-submenu button{height:var(--height);width:var(--height);padding:0;border:2px solid var(--base);color:var(--contrast);border-radius:0}.toggle svg{transform:rotate(0);transition:transform var(--timing) var(--function);transition-property:transform,background-color,color}.has-submenu.open>button:not(.notifications,.quick-help) svg,.has-submenu:hover>button:not(.notifications,.quick-help) svg{transform:rotate(900deg)}ul.submenu{--dir:column;--wrap:nowrap;--gap:0;position:absolute;top:100%;left:0;max-height:0;transform:scaleY(0);transform-origin:top;width:max-content;min-width:100%;background-color:var(--overlay-light);border:2px solid var(--overlay-light);transition:all var(--timing) var(--function);box-shadow:var(--shadow-none)}.always ul.submenu{position:relative;top:0;left:0}.submenu li{background-color:var(--overlay-heavy);border:1px solid var(--base-50)}.submenu li:hover{--c:var(--action-rgb);background-color:var(--overlay-heavy)}.submenu a:hover{background-color:transparent}.wp-site-blocks>header ul.submenu{right:0;left:auto}.has-submenu.open>ul.submenu,.has-submenu:hover>ul.submenu{transform:scaleY(1);max-height:1000%;box-shadow:var(--shadow)}nav.fixed.bottom,nav.on-this-page{--dir:row;--gap:0;width:calc(100% - var(--height));left:0;bottom:0;position:fixed;box-shadow:var(--shadow);z-index:var(--zz-top)}nav.fixed.bottom ul{width:100%;--justify:space-between;background-color:var(--base);padding:0 .25rem}nav.fixed a,nav.fixed li{--justify:center;width:100%}nav.fixed.bottom a,nav.fixed.bottom a:visited{color:var(--contrast);font-size:var(--small);padding:0}@media (min-width:768px){nav.fixed.bottom a,nav.fixed.bottom a:visited{font-size:var(--medium)}}nav.fixed.bottom a:focus,nav.fixed.bottom a:focus:visited,nav.fixed.bottom a:hover,nav.fixed.bottom a:hover:visited{color:var(--action-contrast)}.fixed.bottom li{flex:1}nav.always a{padding:0;--justify:center}nav.always .socials{width:100%}nav.always .socials li{width:100%}nav.always li{gap:0;--justify:flex-start}nav.always>ul>li>a{width:80%}nav.always .submenu{width:80%;min-width:80%;box-shadow:none!important;border:2px solid var(--action-0);background-color:rgba(var(--contrast-rgb),var(--rgb-subtle))}nav.always .submenu li{background-color:var(--overlay-light)}nav.always .has-submenu>a,nav.fixed .has-submenu>a{width:80%}.has-submenu>button{width:20%}nav#breadcrumbs{--height:1.5em;--w:20px;width:fit-content;max-width:var(--full);position:absolute;background-color:var(--overlay-medium);font-size:var(--small);padding:.125em;overflow:visible;--gap:0}nav#breadcrumbs li+li::before{content:'/';color:var(--contrast-200)}nav#breadcrumbs li:last-of-type{margin-right:.5em}nav#breadcrumbs a,nav#breadcrumbs span{padding:0 .125rem;white-space:nowrap;height:2em;color:var(--contrast);text-transform:none;width:max-content}nav#breadcrumbs span{display:flex;align-items:center;padding-left:.5em}nav#breadcrumbs a:focus,nav#breadcrumbs a:focus:visited,nav#breadcrumbs a:hover,nav#breadcrumbs a:hover:visited{background-color:transparent;color:var(--action-0)}nav#breadcrumbs a:has(.icon){width:2rem}nav.always{z-index:var(--z-above);position:fixed;width:var(--height);bottom:0;right:0}nav.always.open{width:100vw;height:100vh;padding-bottom:var(--offHeight);background-color:var(--overlay-heavy);backdrop-filter:blur(5px);justify-content:flex-end;flex-direction:column;z-index:var(--z-above)}nav.always>ul{--dir:column;--wrap:nowrap;--justify:flex-start;--align:center;--gap:0;position:relative;right:-300vw;padding:1rem 0 0;width:100vw;height:fit-content;max-height:100%;overflow:hidden auto;transition:right var(--transition-base)}nav.always.open>ul{right:0;transition:right var(--transition-base)}nav.always li{max-inline-size:none;width:100%;height:fit-content;--dir:row;--wrap:wrap}nav.always a{--py:1rem;width:100%;min-height:var(--height)}nav.always>button{position:fixed;bottom:0;right:0;width:var(--height);height:var(--height);border-radius:0;background-color:var(--base);color:var(--contrast);transition:width var(--timing) var(--function);transition-property:width,background-color;box-shadow:var(--shadow)}nav.always>button:hover{background-color:var(--action-0);color:var(--action-contrast)}nav.always.open>button{--c:var(--action-rgb);z-index:1000000;width:100%;background-color:var(--overlay-heavy);color:var(--contrast);backdrop-filter:blur(5px)}nav.always.open>button:focus,nav.always.open>button:hover{background-color:var(--action-0);color:var(--action-contrast)}nav.always.open>button .list,nav.always>button .x{transform:scale(0);height:0;width:0;position:absolute}nav.always.open>button .x,nav.always>button .list{transform:scale(1);height:32px;width:32px}nav.always .has-submenu.open>.submenu,nav.always .has-submenu:hover>.submenu{height:max-content}nav.always .has-submenu:hover>a,nav.always .submenu>li>a:focus,nav.always .submenu>li>a:hover{background-color:var(--action-0);color:var(--action-contrast)}@media (min-width:768px){nav.always>ul{padding:var(--height) 0 0}}nav.on-this-page{--justify:space-between;max-width:none;z-index:var(--z-6);margin:0;padding:0 .5rem;background-color:var(--overlay-medium);color:var(--base-200)}body:has(nav.fixed) nav.on-this-page{bottom:var(--offHeight)}.on-this-page ul{--justify:flex-start;gap:0;width:100%}.on-this-page li:not(.has){padding:0}nav.letters li{width:100%;max-width:calc(7.69% - 2px)}.on-this-page .active a{--c:var(--action-rgb);background-color:var(--overlay-heavy);color:var(--action-contrast)}@media (min-width:768px){nav.letters li{max-width:none;width:fit-content}nav.letters a,nav.letters li:not(.has){padding:.25rem .66rem}}nav.index{--justify:flex-start;--px:0;background-color:var(--overlay-heavy)}.index ul{--justify:flex-start;width:fit-content}.index li{flex-shrink:0;transform:scaleX(0);transform-origin:right;max-width:0;overflow:hidden;transition:transform var(--timing) var(--function)}.index li.active{transform:scaleX(1);transform-origin:left;width:100%;flex-shrink:1;max-width:fit-content}@media (min-width:768px){.index li.adj{transform:scaleX(1);transform-origin:left;width:100%;flex-shrink:1;max-width:fit-content}}.index a{border-bottom:4px solid transparent}.index .active a{border-color:var(--action-0);color:var(--contrast)}.index .active a:hover,.index a:hover{background-color:var(--action-0);color:var(--action-contrast)}.index label{display:flex;color:var(--contrast);align-items:center;margin:0}.index label button{margin-left:1em}.index.open{--dir:column-reverse;height:calc(100% - 8rem);z-index:var(--z-above);width:100%;background-color:var(--overlay-heavy);backdrop-filter:blur(5px);align-items:flex-end}.index.open label{max-width:90%;margin-top:1rem;margin-right:2rem}.index.open .toggle svg{transform:rotate(45deg)}.index.open ul{--dir:column;--justify:flex-end;height:100%;max-width:100%;width:100%}.index.open li{background-color:transparent;max-width:100%!important;width:100%;height:var(--height);transform:scaleX(1);flex-shrink:1;overflow:visible}.index.open a{--justify:flex-end;background-color:transparent;padding:0 2rem 0 0}.condensed{--dir:row;--wrap:wrap;--height:1.2em;--py:.25rem;--px:.25rem;height:fit-content}.condensed>ul{--wrap:wrap;height:fit-content}.condensed ul{--justify:center;--gap:0}.condensed li{width:fit-content}.condensed li+li::before{content:'·';display:block;padding:0 .25em}nav.condensed a{text-transform:none;white-space:nowrap;border-bottom:2px solid transparent}.condensed a:focus,.condensed a:focus:visited,.condensed a:hover,.condensed a:hover:visited{border-color:var(--action-0)}.dashboard-nav{width:100%;--dir:row;--justify:flex-start;--wrap:nowrap}.wp-site-blocks>header,body>header{--dir:row;position:sticky;top:0;left:0;right:0;height:var(--height);width:100vw;display:flex;justify-content:space-between;align-items:center;padding:0 .5rem;background-color:var(--base);z-index:var(--zz-top);box-shadow:var(--shadow);border-bottom:1px solid var(--action-0)}body>header{justify-content:space-between}header a[rel=home],header>img{position:absolute;width:var(--offHeight);left:calc(50% - (var(--offHeight)/ 2))}header .title{--w:5em;margin:0;position:absolute;width:100%;height:100%;display:flex;justify-content:center;align-items:flex-start;max-inline-size:none}.current-hours{position:sticky;top:var(--height);bottom:unset;width:unset;z-index:100;background-color:var(--action-0);color:var(--action-contrast);box-shadow:var(--shadow);padding:.25rem 1rem;display:flex;justify-content:space-between}.current-hours p{margin:0;display:flex;flex-wrap:wrap;flex:1}.current-hours p+p{justify-content:flex-end}.current-hours a{color:var(--action-contrast)}.current-hours a:hover{color:var(--action-200)}.current-hours b{margin-right:.25rem}.find-us{display:flex;align-items:center;gap:0 .5rem}.find-us a{display:flex;padding:.25rem 1rem;border:1px solid var(--action-contrast);border-radius:var(--innerRadius)}.find-us a:hover{background-color:var(--base);color:var(--contrast);border-color:var(--contrast)}nav.menu{--justify:flex-start}nav.menu a{padding:.5rem .66rem}nav.tabs{--gap:0;--wrap:nowrap;padding-bottom:2px;z-index:var(--z-6);position:fixed;bottom:var(--height);left:var(--doubleHeight);right:var(--doubleHeight)}nav.term-navigation:has([hidden]){display:none}ul.socials a{padding:.5rem}ul.socials a .icon{margin:0}
\ No newline at end of file
+nav{--py:.25rem;--px:1rem;max-width:100%;font-family:var(--heading)}nav,nav a,nav li,nav ol,nav ul{height:var(--height);display:flex;justify-content:var(--justify);align-items:var(--align);gap:var(--gap);flex-wrap:var(--wrap);flex-direction:var(--dir)}ul.socials{--w:1.2em;--height:fit-content;gap:.5rem;display:flex;justify-content:center;align-items:center;flex-wrap:nowrap;flex-direction:row;overflow:auto hidden;touch-action:pan-x;list-style:none}nav:not(:has(>ul)),nav>ul{--justify:flex-start;--align:center;--wrap:nowrap;--w:1em;--dir:row;position:relative;overflow:auto hidden;touch-action:pan-x}nav a{padding:0 var(--px);white-space:nowrap;text-transform:uppercase}nav .current a,nav a.current,nav a:focus,nav a:focus:visited,nav a:hover,nav a:hover:visited{background-color:var(--action-0);color:var(--action-contrast);transition:background-color var(--transition-base),color var(--transition-base)}nav ol,nav ul{list-style:none;margin:0;padding:0}.has-submenu button:hover,nav a:hover{background-color:var(--action-0);color:var(--action-contrast)}.has-submenu button{height:var(--height);width:var(--height);padding:0;border:2px solid var(--base);color:var(--contrast);border-radius:0}.toggle .icon{transform:rotate(0);transition:transform var(--timing) var(--function);transition-property:transform,background-color,color}.has-submenu.open>button:not(.notifications,.quick-help) .icon,.has-submenu:hover>button:not(.notifications,.quick-help) .icon{transform:rotate(900deg)}ul.submenu{--dir:column;--wrap:nowrap;--gap:0;position:absolute;top:100%;left:0;max-height:0;transform:scaleY(0);transform-origin:top;width:max-content;min-width:100%;background-color:var(--overlay-light);border:2px solid var(--overlay-light);transition:all var(--timing) var(--function);box-shadow:var(--shadow-none)}.always ul.submenu{position:relative;top:0;left:0}.submenu li{background-color:var(--overlay-heavy);border:1px solid var(--base-50)}.submenu li:hover{--c:var(--action-rgb);background-color:var(--overlay-heavy)}.submenu a:hover{background-color:transparent}.wp-site-blocks>header ul.submenu{right:0;left:auto}.has-submenu.open>ul.submenu,.has-submenu:hover>ul.submenu{transform:scaleY(1);max-height:1000%;box-shadow:var(--shadow)}nav.fixed.bottom,nav.on-this-page{--dir:row;--gap:0;width:calc(100% - var(--height));left:0;bottom:0;position:fixed;box-shadow:var(--shadow);z-index:var(--zz-top)}nav.fixed.bottom ul{width:100%;--justify:space-between;background-color:var(--base);padding:0 .25rem}nav.fixed a,nav.fixed li{--justify:center;width:100%}nav.fixed.bottom a,nav.fixed.bottom a:visited{color:var(--contrast);font-size:var(--small);padding:0}@media (min-width:768px){nav.fixed.bottom a,nav.fixed.bottom a:visited{font-size:var(--medium)}}nav.fixed.bottom a:focus,nav.fixed.bottom a:focus:visited,nav.fixed.bottom a:hover,nav.fixed.bottom a:hover:visited{color:var(--action-contrast)}.fixed.bottom li{flex:1}nav.always a{padding:0;--justify:center}nav.always .socials{width:100%}nav.always .socials li{width:100%}nav.always li{gap:0;--justify:flex-start}nav.always>ul>li>a{width:80%}nav.always .submenu{width:80%;min-width:80%;box-shadow:none!important;border:2px solid var(--action-0);background-color:rgba(var(--contrast-rgb),var(--rgb-subtle))}nav.always .submenu li{background-color:var(--overlay-light)}nav.always .has-submenu>a,nav.fixed .has-submenu>a{width:80%}.has-submenu>button{width:20%}nav#breadcrumbs{--height:1.5em;--w:20px;width:fit-content;max-width:var(--full);position:absolute;background-color:var(--overlay-medium);font-size:var(--small);padding:.125em;overflow:visible;--gap:0}nav#breadcrumbs li+li::before{content:'/';color:var(--contrast-200)}nav#breadcrumbs li:last-of-type{margin-right:.5em}nav#breadcrumbs a,nav#breadcrumbs span{padding:0 .125rem;white-space:nowrap;height:2em;color:var(--contrast);text-transform:none;width:max-content}nav#breadcrumbs span{display:flex;align-items:center;padding-left:.5em}nav#breadcrumbs a:focus,nav#breadcrumbs a:focus:visited,nav#breadcrumbs a:hover,nav#breadcrumbs a:hover:visited{background-color:transparent;color:var(--action-0)}nav#breadcrumbs a:has(.icon){width:2rem}nav.always{z-index:var(--z-above);position:fixed;width:var(--height);bottom:0;right:0}nav.always.open{width:100vw;height:100vh;padding-bottom:var(--offHeight);background-color:var(--overlay-heavy);backdrop-filter:blur(5px);justify-content:flex-end;flex-direction:column;z-index:var(--z-above)}nav.always>ul{--dir:column;--wrap:nowrap;--justify:flex-start;--align:center;--gap:0;position:relative;right:-300vw;padding:1rem 0 0;width:100vw;height:fit-content;max-height:100%;overflow:hidden auto;transition:right var(--transition-base)}nav.always.open>ul{right:0;transition:right var(--transition-base)}nav.always li{max-inline-size:none;width:100%;height:fit-content;--dir:row;--wrap:wrap}nav.always a{--py:1rem;width:100%;min-height:var(--height)}nav.always>button{position:fixed;bottom:0;right:0;width:var(--height);height:var(--height);border-radius:0;background-color:var(--base);color:var(--contrast);transition:width var(--timing) var(--function);transition-property:width,background-color;box-shadow:var(--shadow)}nav.always>button:hover{background-color:var(--action-0);color:var(--action-contrast)}nav.always.open>button{--c:var(--action-rgb);z-index:1000000;width:100%;background-color:var(--overlay-heavy);color:var(--contrast);backdrop-filter:blur(5px)}nav.always.open>button:focus,nav.always.open>button:hover{background-color:var(--action-0);color:var(--action-contrast)}nav.always.open>button .icon-list,nav.always>button .icon-x{transform:scale(0);height:0;width:0;position:absolute}nav.always.open>button .icon-x,nav.always>button .icon-list{transform:scale(1);height:32px;width:32px}nav.always .has-submenu.open>.submenu,nav.always .has-submenu:hover>.submenu{height:max-content}nav.always .has-submenu:hover>a,nav.always .submenu>li>a:focus,nav.always .submenu>li>a:hover{background-color:var(--action-0);color:var(--action-contrast)}@media (min-width:768px){nav.always>ul{padding:var(--height) 0 0}}nav.on-this-page{--justify:space-between;max-width:none;z-index:var(--z-6);margin:0;padding:0 .5rem;background-color:var(--overlay-medium);color:var(--base-200)}body:has(nav.fixed) nav.on-this-page{bottom:var(--offHeight)}.on-this-page ul{--justify:flex-start;gap:0;width:100%}.on-this-page li:not(.has){padding:0}nav.letters li{width:100%;max-width:calc(7.69% - 2px)}.on-this-page .active a{--c:var(--action-rgb);background-color:var(--overlay-heavy);color:var(--action-contrast)}@media (min-width:768px){nav.letters li{max-width:none;width:fit-content}nav.letters a,nav.letters li:not(.has){padding:.25rem .66rem}}nav.index{--justify:flex-start;--px:0;background-color:var(--overlay-heavy)}.index ul{--justify:flex-start;width:fit-content}.index li{flex-shrink:0;transform:scaleX(0);transform-origin:right;max-width:0;overflow:hidden;transition:transform var(--timing) var(--function)}.index li.active{transform:scaleX(1);transform-origin:left;width:100%;flex-shrink:1;max-width:fit-content}@media (min-width:768px){.index li.adj{transform:scaleX(1);transform-origin:left;width:100%;flex-shrink:1;max-width:fit-content}}.index a{border-bottom:4px solid transparent}.index .active a{border-color:var(--action-0);color:var(--contrast)}.index .active a:hover,.index a:hover{background-color:var(--action-0);color:var(--action-contrast)}.index label{display:flex;color:var(--contrast);align-items:center;margin:0}.index label button{margin-left:1em}.index.open{--dir:column-reverse;height:calc(100% - 8rem);z-index:var(--z-above);width:100%;background-color:var(--overlay-heavy);backdrop-filter:blur(5px);align-items:flex-end}.index.open label{max-width:90%;margin-top:1rem;margin-right:2rem}.index.open .toggle .icon{transform:rotate(45deg)}.index.open ul{--dir:column;--justify:flex-end;height:100%;max-width:100%;width:100%}.index.open li{background-color:transparent;max-width:100%!important;width:100%;height:var(--height);transform:scaleX(1);flex-shrink:1;overflow:visible}.index.open a{--justify:flex-end;background-color:transparent;padding:0 2rem 0 0}.condensed{--dir:row;--wrap:wrap;--height:1.2em;--py:.25rem;--px:.25rem;height:fit-content}.condensed>ul{--wrap:wrap;height:fit-content}.condensed ul{--justify:center;--gap:0}.condensed li{width:fit-content}.condensed li+li::before{content:'·';display:block;padding:0 .25em}nav.condensed a{text-transform:none;white-space:nowrap;border-bottom:2px solid transparent}.condensed a:focus,.condensed a:focus:visited,.condensed a:hover,.condensed a:hover:visited{border-color:var(--action-0)}.dashboard-nav{width:100%;--dir:row;--justify:flex-start;--wrap:nowrap}.wp-site-blocks>header,body>header{--dir:row;position:sticky;top:0;left:0;right:0;height:var(--height);width:100vw;display:flex;justify-content:space-between;align-items:center;padding:0 .5rem;background-color:var(--base);z-index:var(--zz-top);box-shadow:var(--shadow);border-bottom:1px solid var(--action-0)}.wp-site-blocks>header img{width:var(--height)}body>header{justify-content:space-between}header .title{--w:5em;margin:0;position:absolute;width:100%;height:100%;display:flex;justify-content:center;align-items:flex-start;max-inline-size:none}.current-hours{position:sticky;top:var(--height);bottom:unset;width:unset;z-index:100;background-color:var(--action-0);color:var(--action-contrast);box-shadow:var(--shadow);padding:.25rem 1rem;display:flex;justify-content:space-between}.current-hours p{margin:0;display:flex;flex-wrap:wrap;flex:1}.current-hours p+p{justify-content:flex-end}.current-hours a{color:var(--action-contrast)}.current-hours a:hover{color:var(--action-200)}.current-hours b{margin-right:.25rem}.find-us{display:flex;align-items:center;gap:0 .5rem}.find-us a{display:flex;padding:.25rem 1rem;border:1px solid var(--action-contrast);border-radius:var(--innerRadius)}.find-us a:hover{background-color:var(--base);color:var(--contrast);border-color:var(--contrast)}nav.menu{--justify:flex-start}nav.menu a{padding:.5rem .66rem}nav.tabs{--gap:0;--wrap:nowrap;padding-bottom:2px;z-index:var(--z-6);position:fixed;bottom:var(--height);left:var(--doubleHeight);right:var(--doubleHeight)}nav.term-navigation:has([hidden]){display:none}ul.socials a{padding:.5rem}ul.socials a .icon{margin:0}
\ No newline at end of file
diff --git a/assets/js/Gallery.js b/assets/js/Gallery.js
index e952b26..dfc8069 100644
--- a/assets/js/Gallery.js
+++ b/assets/js/Gallery.js
@@ -1,272 +1,226 @@
 class Gallery {
-	constructor(modal, config) {
-		this.imageWrapper = config.imageWrapper??null;
-		this.container = config.container ? document.querySelector(config.container) : document.querySelector('main');
-		this.modal = new window.jvbModal(
-			modal,
-			{
-				onOpen: false,
-				onSave: false,
-				onClose: () => this.closeGallery(false)
-			}
-		);
-		this.items = this.getGalleryItems() || [];
-		this.loadMore = config.loadMore??false;
+	constructor(modal, config = {}) {
+		this.container = config.container
+			? document.querySelector(config.container)
+			: document.querySelector('main');
 
-		this.modalElement = (typeof modal === 'string') ? document.querySelector(modal)??false : modal;
-		if (!this.modal) {
-			return;
-		}
+		this.gallerySelector = config.gallerySelector || 'img[data-small]';
+
+		this.modal = new window.jvbModal(modal, {
+			onOpen: false,
+			onSave: false,
+			onClose: () => this.closeGallery(false)
+		});
+
+		this.modalElement = (typeof modal === 'string')
+			? document.querySelector(modal)
+			: modal;
+
+		if (!this.modalElement) return;
 
 		this.a11y = window.jvbA11y;
-		this.openWhenReady = false;
-
-
-		this.initElements();
-		this.imageWrapper = (typeof this.imageWrapper === 'string') ? this.imageWrapper : '.'+this.imageWrapper.classList.join('.');
-
 		this.index = 0;
+		this.items = [];
+
 		this.swipe = {
 			touchStart: null,
 			touchEnd: null,
 			minSwipe: 50,
 		};
-		this.isLoading = false;
 
+		this.initElements();
+		this.initListeners();
 	}
 
 	initElements() {
 		this.prevBtn = this.modalElement.querySelector('.prev');
 		this.nextBtn = this.modalElement.querySelector('.next');
-		this.favourite = this.modalElement.querySelector('.favourite');
 		this.image = this.modalElement.querySelector('.image');
 		this.counter = this.modalElement.querySelector('.counter');
-		this.extra = this.modalElement.querySelector('.item-info');
-
-		//If we don't have the wrapper set up, we can remove these elements
-		if (!this.imageWrapper) {
-			this.favourite.remove();
-			this.extra.remove();
-		}
-	}
-
-	getGalleryItems() {
-		let search = (this.imageWrapper) ? this.imageWrapper : 'img';
-
-		return Array.from(this.container.querySelectorAll(search))
-			.map(item => {
-				const img = (this.imageWrapper) ? item.querySelector('img') : item;
-				if (!img) return null;
-
-				return {
-					id: (this.imageWrapper) ? item.querySelector('button.favourite').dataset.id : '',
-					small: img.dataset.small || img.src,
-					large: img.dataset.large || img.src,
-					full: img.dataset.full || img.src,
-					alt: img.alt || '',
-					fav: (this.imageWrapper) ? item.querySelector('button.favourite')?.cloneNode(true) : '',
-					info: (this.imageWrapper) ? item.querySelector('.item-info')?.cloneNode(true) : ''
-				};
-			}).filter(Boolean);
 	}
 
 	initListeners() {
+		// Delegate click handling to container
 		this.container.addEventListener('click', this.handleClick.bind(this));
-		document.addEventListener('keydown', this.handleKeys.bind(this));
-		document.addEventListener('touchstart', this.handleTouchStart.bind(this));
-		document.addEventListener('touchend', this.handleTouchEnd.bind(this));
-		document.addEventListener('touchmove', this.handleTouchMove.bind(this));
+
+		// Modal-specific listeners added only when open
+		this.boundKeyHandler = this.handleKeys.bind(this);
+		this.boundTouchStart = this.handleTouchStart.bind(this);
+		this.boundTouchMove = this.handleTouchMove.bind(this);
+		this.boundTouchEnd = this.handleTouchEnd.bind(this);
 	}
-	destroyListeners() {
-		this.container.removeEventListener('click', this.handleClick.bind(this));
-		document.removeEventListener('keydown', this.handleKeys.bind(this));
-		document.removeEventListener('touchstart', this.handleTouchStart.bind(this));
-		document.removeEventListener('touchend', this.handleTouchEnd.bind(this));
-		document.removeEventListener('touchmove', this.handleTouchMove.bind(this));
+
+	handleClick(e) {
+		// Open gallery when clicking images
+		const img = e.target.closest(this.gallerySelector);
+		if (img && !this.modal.isOpen) {
+			e.preventDefault();
+			this.openGallery(img);
+			return;
+		}
+
+		// Navigation within gallery
+		if (this.modal.isOpen) {
+			if (e.target.closest('.next')) {
+				this.navigate(1);
+			} else if (e.target.closest('.prev')) {
+				this.navigate(-1);
+			}
+		}
 	}
 
 	handleKeys(e) {
+		if (!this.modal.isOpen) return;
+
 		switch (e.key) {
 			case 'ArrowLeft':
+				e.preventDefault();
 				this.navigate(-1);
 				break;
 			case 'ArrowRight':
+				e.preventDefault();
 				this.navigate(1);
 				break;
 		}
 	}
 
 	handleTouchStart(e) {
+		if (!this.modal.isOpen) return;
 		this.swipe.touchStart = e.touches[0].clientX;
 	}
 
 	handleTouchMove(e) {
+		if (!this.modal.isOpen) return;
 		this.swipe.touchEnd = e.touches[0].clientX;
 	}
 
 	handleTouchEnd(e) {
-		if (!this.swipe.touchStart || !this.swipe.touchEnd) return;
-		const distance = this.swipe.touchStart - this.swipe.touchEnd;
-		const isLeftSwipe = distance > this.swipe.minSwipe;
-		const isRightSwipe = distance < -this.swipe.minSwipe;
+		if (!this.modal.isOpen || !this.swipe.touchStart || !this.swipe.touchEnd) return;
 
-		if (isLeftSwipe) {
-			this.navigate(1);
-		} else if (isRightSwipe) {
-			this.navigate(-1);
+		const distance = this.swipe.touchStart - this.swipe.touchEnd;
+
+		if (Math.abs(distance) > this.swipe.minSwipe) {
+			this.navigate(distance > 0 ? 1 : -1);
 		}
+
 		this.swipe.touchStart = null;
 		this.swipe.touchEnd = null;
 	}
 
-	handleClick(e) {
-		//test if it's a link click, and the click has an image
-		if(window.targetCheck(e, '.feed-images')){
-			this.handleGalleryOpen(e);
-		}
+	buildGalleryItems() {
+		return Array.from(this.container.querySelectorAll(this.gallerySelector))
+			.map((img, index) => ({
+				id: img.dataset.id || index,
+				small: img.dataset.small || img.src,
+				medium: img.dataset.medium || img.src,
+				full: img.dataset.full || img.src,
+				alt: img.alt || '',
+				element: img
+			}));
+	}
 
-		if(window.targetCheck(e, '.nav')) {
-			if (window.targetCheck(e, '.next')) {
-				this.navigate(1);
-			} else {
-				this.navigate(-1);
-			}
-		}
-		if (window.targetCheck(e, 'button.cancel')) {
-			this.closeGallery();
+	openGallery(clickedImg) {
+		// Build fresh gallery items
+		this.items = this.buildGalleryItems();
+
+		// Find clicked image index
+		this.index = this.items.findIndex(item =>
+			item.element === clickedImg
+		);
+
+		if (this.index === -1) this.index = 0;
+
+		// Attach modal-specific listeners
+		document.addEventListener('keydown', this.boundKeyHandler);
+		document.addEventListener('touchstart', this.boundTouchStart, { passive: true });
+		document.addEventListener('touchmove', this.boundTouchMove, { passive: true });
+		document.addEventListener('touchend', this.boundTouchEnd, { passive: true });
+
+		this.modal.handleOpen();
+		this.updateDisplay();
+		this.preloadAdjacent();
+
+		this.a11y.announce(
+			`Gallery opened. Image ${this.index + 1} of ${this.items.length}. Use arrow keys to navigate.`
+		);
+	}
+
+	closeGallery(useModal = true) {
+		// Remove modal-specific listeners
+		document.removeEventListener('keydown', this.boundKeyHandler);
+		document.removeEventListener('touchstart', this.boundTouchStart);
+		document.removeEventListener('touchmove', this.boundTouchMove);
+		document.removeEventListener('touchend', this.boundTouchEnd);
+
+		if (useModal) {
+			this.modal.handleClose();
 		}
 	}
 
-	async navigate(direction) {
+	navigate(direction) {
 		let newIndex = this.index + direction;
 
-		//Check if out of bounds
-		if (newIndex <0 || newIndex >= this.items.length) {
-			this.a11y.announceNavigation(newIndex, this.items.length, direction < 0, direction > 0);
-			if (newIndex <0) {
-				newIndex = this.items.length - 1;
-			} else {
-				newIndex = 0;
-			}
+		// Wrap around
+		if (newIndex < 0) {
+			newIndex = this.items.length - 1;
+		} else if (newIndex >= this.items.length) {
+			newIndex = 0;
 		}
 
-		//update index
 		this.index = newIndex;
-
 		this.updateDisplay();
-		this.preloadImages();
+		this.preloadAdjacent();
 
-		this.a11y.announceNavigation(this.index, this.items.length);
-
-		if (typeof this.loadMore === 'function' &&
-			direction > 0 &&
-			newIndex >= (this.items.length - 3)) {
-			if (window.feedBlock.hasMore) {
-				await this.loadMore();
-				this.updateGalleryItems(this.getGalleryItems());
-			}
-		}
-	}
-
-	updateGalleryItems(newItems) {
-		const currentItem = this.items[this.index];
-
-		this.items = newItems;
-		if (currentItem) {
-			const newIndex = this.items.findIndex(item =>
-				item.full === currentItem.full ||
-				item.large === currentItem.large
-			);
-
-			if (newIndex !== -1) {
-				this.index = newIndex;
-			}
-		}
-
-		this.updateNavigationButtons();
-		if(this.items.length > 0 && this.openWhenReady) {
-			let index = this.findIndex('id', this.openWhenReady);
-			this.openGallery(index);
-		}
-	}
-	preloadImages() {
-		[-1,0,1].forEach(offset => {
-			const index = this.index + offset;
-			if (index >=0 && index < this.items.length) {
-				const img = new Image();
-				const item = this.items[index];
-				if (window.innerWidth < 1000) {
-					img.src = item.large || item.src;
-				} else {
-					img.src = item.full || item.src;
-				}
-			}
-		});
+		this.a11y.announce(`Image ${this.index + 1} of ${this.items.length}`);
 	}
 
 	updateDisplay() {
 		const item = this.items[this.index];
 		if (!item) return;
 
-		[
-			this.image.src,
-			this.image.alt,
-			this.counter.textContent
-		] = [
-			(window.innerWidth < 1000) ?
-				(item.large || item.src) :
-				(item.full || item.src),
-			item.alt || '',
-			`${this.index + 1} / ${this.items.length}`
-		];
-		if (this.imageWrapper) {
-			if (item.fav) {
-				window.removeChildren(this.favourite);
-				this.favourite.appendChild(item.fav.cloneNode(true));
+		// Use medium/full based on viewport
+		const src = window.innerWidth < 1000
+			? (item.medium || item.src)
+			: (item.full || item.src);
+
+		this.image.src = src;
+		this.image.alt = item.alt;
+		this.counter.textContent = `${this.index + 1} / ${this.items.length}`;
+
+		// Update button states
+		this.prevBtn.classList.toggle('disabled', this.items.length <= 1);
+		this.nextBtn.classList.toggle('disabled', this.items.length <= 1);
+	}
+
+	preloadAdjacent() {
+		[-1, 1].forEach(offset => {
+			const index = this.index + offset;
+			if (index >= 0 && index < this.items.length) {
+				const item = this.items[index];
+				const img = new Image();
+				img.src = window.innerWidth < 1000
+					? (item.medium || item.src)
+					: (item.full || item.src);
 			}
-			if (item.info) {
-				window.removeChildren(this.extra);
-				this.extra.appendChild(item.info.cloneNode(true));
-			}
-		}
-
-		this.updateNavigationButtons();
+		});
 	}
 
-	updateNavigationButtons() {
-		this.prevBtn.classList.toggle('end', this.index > 0 ? '' : 'none');
-		this.nextBtn.classList.toggle('end', this.index < this.items.length -1 ? '' : 'none');
-	}
-
-	handleGalleryOpen(e) {
-		let item = (this.imageWrapper) ? e.target.closest(this.imageWrapper) : e.target.closest('img');
-		let key = (this.imageWrapper) ? 'id' : 'small';
-		let value = item.dataset[key];
-
-		let index = this.findIndex(key, value);
-		this.openGallery(index);
-	}
-
-	findIndex(property, value) {
-		return this.items.findIndex(obj => obj[property] === value);
-	}
-
-	openGallery(index) {
-		this.initListeners();
-		this.index = index;
-		this.modal.handleOpen();
-		this.updateDisplay();
-		this.preloadImages();
-
-		this.a11y.announce(`Image ${this.index + 1} of ${this.items.length}. Use arrow keys to navigate.`);
-	}
-
-	closeGallery(useModal = true) {
-		this.destroyListeners();
-		if (useModal) {
-			this.modal.handleClose();
-		}
+	cleanup() {
+		this.container.removeEventListener('click', this.handleClick);
+		this.closeGallery(false);
 	}
 }
 
-window.jvbGallery = Gallery;
+
+document.addEventListener('DOMContentLoaded', function() {
+	let galleries = document.querySelectorAll('dialog.gallery');
+	if (galleries.length > 0) {
+		window.galleries = new Map();
+
+		galleries.forEach(gallery => {
+			let id = gallery.id;
+			window.galleries.set(id, new Gallery(gallery));
+		});
+	}
+});
+
diff --git a/assets/js/concise/FormController.js b/assets/js/concise/FormController.js
index 9a3b075..73a2756 100644
--- a/assets/js/concise/FormController.js
+++ b/assets/js/concise/FormController.js
@@ -188,6 +188,7 @@
 	 * Register a standalone form (for front-end forms)
 	 */
 	registerForm(formElement, options = {}) {
+		if (!formElement) return;
 		const formId = formElement.dataset.formId || `form_${Date.now()}`;
 		formElement.dataset.formId = formId;
 
@@ -196,16 +197,17 @@
 		const formConfig = {
 			element: formElement,
 			id: formId,
+			status: '',
 			options: {
-				autoSave: 'autosave' in formElement.dataset,
+				autosave: 'autosave' in formElement.dataset,
 				saveDelay: this.autoSaveDefaults.delay,
 				endpoint: formElement.dataset.save??'',
+				formStatus: true,
 				cache: true,
 				...options
 			},
 			dependencies: new Map(),
 			data: this.collectFormData(formElement),
-			isDirty: false
 		};
 
 		// Initialize special fields
@@ -444,8 +446,7 @@
 
 		container.appendChild(row);
 
-		// Schedule save if auto-save enabled
-		if (formConfig && formConfig.options.autoSave) {
+		if (formConfig) {
 			this.scheduleSave(formConfig, {
 				type: 'repeater',
 				action: 'add',
@@ -472,7 +473,7 @@
 		this.updateRepeaterOrder(repeater, formConfig);
 
 		// Schedule save
-		if (formConfig && formConfig.options.autoSave) {
+		if (formConfig) {
 			this.scheduleSave(formConfig, {
 				type: 'repeater',
 				action: 'remove',
@@ -515,7 +516,7 @@
 		});
 
 		// Schedule save
-		if (formConfig && formConfig.options.autoSave) {
+		if (formConfig) {
 			this.scheduleSave(formConfig, {
 				type: 'repeater',
 				action: 'reorder',
@@ -650,16 +651,15 @@
 
 	/* ========== Event Handlers ========== */
 
-	handleSubmit(event) {
-		//TODO: submit data, if successful, delete from store
-		if (this.subscribers.size > 0 ){
-			const form = event.target;
-			if (!form.dataset.formId) return;
+	async handleSubmit(event) {
+		const form = event.target;
+		if (!form.dataset.formId) return;
+
+		const formConfig = this.forms.get(form.dataset.formId);
+
+		// Handle subscriber-based forms
+		if (this.subscribers.size > 0) {
 			event.preventDefault();
-
-			const formConfig = this.forms.get(form.dataset.formId);
-			if (!formConfig) return;
-
 			const formData = this.collectFormData(form);
 			this.notify('form-submit', {
 				formId: formConfig.id,
@@ -669,6 +669,133 @@
 		}
 	}
 
+	handleFormSuccess(form, data) {
+		// Clear previous errors
+		form.querySelectorAll('.error-message').forEach(el => el.remove());
+		form.querySelectorAll('.field-error').forEach(el =>
+			el.classList.remove('field-error')
+		);
+
+		// Add success class to form
+		form.classList.add('form-success');
+
+		// Show success message if provided
+		if (data.message) {
+			const success = document.createElement('div');
+			success.className = 'form-success-message success-message';
+			success.textContent = data.message;
+			form.insertBefore(success, form.firstChild);
+
+			// Optionally add icon
+			const icon = window.getIcon?.('check-circle');
+			if (icon) {
+				icon.classList.add('success-icon');
+				success.prepend(icon);
+			}
+		}
+
+		// If there's a title/description (for registration success)
+		if (data.title || data.description) {
+			const successBox = document.createElement('div');
+			successBox.className = 'success-box';
+
+			if (data.title) {
+				const title = document.createElement('h3');
+				title.textContent = data.title;
+				successBox.appendChild(title);
+			}
+
+			if (data.description) {
+				// Handle both string and array descriptions
+				const descriptions = Array.isArray(data.description)
+					? data.description
+					: [data.description];
+
+				descriptions.forEach(desc => {
+					const p = document.createElement('p');
+					p.textContent = desc;
+					successBox.appendChild(p);
+				});
+			}
+
+			form.insertBefore(successBox, form.firstChild);
+		}
+
+		// Announce success for accessibility
+		if (window.jvbA11y) {
+			window.jvbA11y.announce(data.message || 'Form submitted successfully');
+		}
+
+		// Trigger custom event
+		form.dispatchEvent(new CustomEvent('jvb-form-success', {
+			detail: data
+		}));
+	}
+
+	handleFormError(form, data) {
+		// Clear all previous errors
+		form.querySelectorAll('.error-message').forEach(el => el.remove());
+		form.querySelectorAll('.field-error, .has-error').forEach(el => {
+			el.classList.remove('field-error', 'has-error');
+		});
+
+		// Clear validation states using existing method
+		form.querySelectorAll('.field').forEach(fieldWrapper => {
+			this.clearValidation(fieldWrapper);
+		});
+
+		// Handle field-specific errors
+		if (data.field) {
+			const fieldWrapper = form.querySelector(`[data-field="${data.field}"]`);
+			if (fieldWrapper) {
+				// Use existing showError method for consistency
+				this.showError(fieldWrapper, data.message);
+
+				// Mark as touched so validation persists
+				this.touchedFields.add(data.field);
+
+				// Scroll to error
+				fieldWrapper.scrollIntoView({ behavior: 'smooth', block: 'center' });
+
+				// Focus the input for better UX
+				const input = fieldWrapper.querySelector('input, textarea, select');
+				if (input) {
+					input.focus();
+				}
+			}
+		} else {
+			// General form error (not field-specific)
+			const error = document.createElement('div');
+			error.className = 'form-error error-message';
+			error.textContent = data.message;
+
+			// Add icon for consistency
+			const icon = window.getIcon?.('close-circle');
+			if (icon) {
+				icon.classList.add('error-icon');
+				error.prepend(icon);
+			}
+
+			form.insertBefore(error, form.firstChild);
+
+			// Scroll to top to show the error
+			form.scrollIntoView({ behavior: 'smooth', block: 'start' });
+		}
+
+		// Announce error for accessibility
+		if (window.jvbA11y) {
+			const announcement = data.field
+				? `Error in ${data.field}: ${data.message}`
+				: `Form error: ${data.message}`;
+			window.jvbA11y.announce(announcement);
+		}
+
+		// Trigger custom event
+		form.dispatchEvent(new CustomEvent('jvb-form-error', {
+			detail: data
+		}));
+	}
+
 	handleClick(e) {
 		if (window.targetCheck(e, 'div.quantity')) {
 			let container = window.targetCheck(e, 'div.quantity');
@@ -749,15 +876,13 @@
 		if (event.target.closest('[data-ignore]')) {
 			return;
 		}
-		if (this.subscribers.size > 0) {
-			const target = event.target;
-			const form = target.form || target.closest('form');
+		const target = event.target;
+		const form = target.form || target.closest('form');if (!form) return;
 
-			if (!form) return;
-
-			const formConfig = this.forms?.get(form.dataset.formId);
-			if (!formConfig) return;
-
+		const formConfig = this.forms?.get(form.dataset.formId);
+		if (!formConfig) return;
+		console.log(formConfig.options);
+		if (formConfig.options.autosave || this.subscribers.size > 0) {
 			// Check conditional fields
 			const dependencies = formConfig.dependencies.get(target.name);
 			if (dependencies) {
@@ -767,10 +892,8 @@
 			}
 
 			// Schedule auto-save if enabled
-			if (formConfig.options.autoSave && !form.dataset.noautosave) {
-				const delay = this.getDelayForField(target);
-				this.scheduleSave(formConfig, delay);
-			}
+			const delay = this.getDelayForField(target);
+			this.scheduleSave(formConfig, delay);
 		}
 	}
 
@@ -807,7 +930,7 @@
 				this.validateField(input, fieldWrapper);
 			}
 			const formConfig = this.forms?.get(form.dataset.formId);
-			if (formConfig && formConfig.options.autoSave && !form.dataset.noautosave) {
+			if (formConfig) {
 				// Shorter delay on blur
 				this.scheduleSave(formConfig, {
 					type: 'blur',
@@ -983,6 +1106,7 @@
 
 		// All validations passed
 		this.showSuccess(fieldWrapper);
+		this.notify('field-validated', input);
 		return true;
 	}
 
@@ -1007,7 +1131,7 @@
 	/**
 	 * Show success state (green checkmark)
 	 */
-	showSuccess(fieldWrapper) {
+	showSuccess(fieldWrapper, textMessage = '') {
 		if (!fieldWrapper) return;
 
 		// Find validation elements (they might be in field-input-wrapper or field-content)
@@ -1033,8 +1157,13 @@
 
 		// Hide error message
 		if (message) {
-			message.hidden = true;
-			message.textContent = '';
+			if (textMessage === '') {
+				message.hidden = true;
+				message.textContent = '';
+			} else {
+				message.hidden = false;
+				message.textContent = textMessage;
+			}
 		}
 	}
 
@@ -1253,6 +1382,9 @@
 		return this.autoSaveDefaults.delay;
 	}
 	scheduleSave(formConfig, delay = this.autoSaveDefaults.delay) {
+		if (!formConfig.options.autosave) {
+			return;
+		}
 		document.addEventListener('input', this.saveCheck, {passive: true});
 		const saveKey = `autosave_${formConfig.id}`;
 
@@ -1288,7 +1420,9 @@
 
 		// Get only changed fields
 		const changes = this.getChangedFields(formConfig.data, formData);
+		console.log('Changes:', changes);
 		if (Object.keys(changes).length === 0) return;
+		console.log('Continuing on:');
 
 		// Update stored data
 		formConfig.data = formData;
@@ -1322,14 +1456,23 @@
 
 		// Check if current data differs from snapshot
 		const currentData = this.collectFormData(formConfig.element);
-		const changes = this.getChangedFields(formConfig.lastSnapshot, currentData);
+		const changes = this.getChangedFields(formConfig.data, currentData);
 
 		return Object.keys(changes).length > 0;
 	}
 
-	showFormStatus(formID, status) {
+	showFormStatus(formID, status, message='') {
 		// Remove existing status
 		let form = this.forms.get(formID);
+		if (!form.options.formStatus) {
+			return;
+		}
+
+		if (form.status === status){
+			return;
+		}
+
+		form.status = status;
 
 		console.log('Setting status: ', status);
 
@@ -1350,9 +1493,9 @@
 			'offline': 'Changes will be saved when online'
 		};
 		const icons = {
-			'autosaved': 'check',
-			'submitted': 'check',
-			'error': 'close',
+			'autosaved': 'check-circle',
+			'submitted': 'check-circle',
+			'error': 'close-circle',
 			'offline': 'cloud-slash',
 			'pending': 'exclamation-mark'
 		}
@@ -1361,9 +1504,10 @@
 		if (icon) {
 			statusWrap.prepend(icon);
 		}
-		console.log(status, messages[status]);
-		console.log(status, icons[status]);
-		statusElement.textContent = messages[status] || status;
+		if (message === '') {
+			message = messages[status] || status;
+		}
+		statusElement.textContent = message;
 		statusWrap.classList.toggle('loading', ['uploading', 'saving'].includes(status));
 
 		// Auto-hide success messages
@@ -1391,6 +1535,9 @@
 	/* ========== Form Data Methods ========== */
 
 	collectFormData(form) {
+		if (Object.hasOwn(form.dataset, 'timeline')) {
+			return this.collectTimeline(form);
+		}
 		const formData = new FormData(form);
 		let data = {};
 		const repeaterData = {};
@@ -1409,6 +1556,46 @@
 		return this.mergeRepeaterData(data, repeaterData);
 	}
 
+	collectTimeline(form) {
+		console.log('Collecting Timeline data:');
+		let data = {};
+		let posts = {}; // Temporary object keyed by post ID
+		let postOrder = []; // Track order as encountered (preserves DOM/drag order)
+		let formData = new FormData(form);
+
+		for (const [key, value] of formData.entries()) {
+			if (this.ignore.includes(key) || key.endsWith('_temp')) {
+				continue;
+			}
+			const match = key.match(/^\[(\d+)\](.+)$/);
+			if (match) {
+				// Timeline-specific field: [postId]fieldName
+				const [, postId, fieldName] = match;
+				if (!posts[postId]) {
+					posts[postId] = { id: parseInt(postId) };
+					postOrder.push(postId); // Track first occurrence
+				}
+				const processor = this.getFieldProcessor(fieldName);
+				processor(fieldName, value, posts[postId], {}, {}, form);
+			} else {
+				// Shared field (post_title, taxonomies, etc.)
+				const processor = this.getFieldProcessor(key);
+				processor(key, value, data, {}, {}, form);
+			}
+		}
+
+		// Convert to array in DOM order (matches menu_order)
+		data.timeline = postOrder.map(id => posts[id]);
+
+		delete data['form-id'];
+		delete data['sendAll'];
+		delete data['timeline_temp'];
+		delete data['']; // Empty key
+
+		console.log('Data: ', data);
+		return data;
+	}
+
 	getFieldProcessor(key) {
 		if (key.includes('|')) return this.processTableField;
 		if (key.includes('::')) return this.processGroupField;
@@ -2118,5 +2305,4 @@
 
 document.addEventListener('DOMContentLoaded', () => {
 	window.jvbForm = FormController;
-	console.log('FormController in window');
 });
diff --git a/assets/js/concise/JVBase.js b/assets/js/concise/JVBase.js
index d9bac2b..50c452a 100644
--- a/assets/js/concise/JVBase.js
+++ b/assets/js/concise/JVBase.js
@@ -181,6 +181,7 @@
 	 * ICONS
 	********************************************/
 	getIcon(icon) {
+		console.log('Getting Icon: '+icon);
 		if (typeof icon === 'undefined') {
 			return '';
 		}
diff --git a/assets/js/concise/Media.js b/assets/js/concise/Media.js
index 3f733a0..ab20c7f 100644
--- a/assets/js/concise/Media.js
+++ b/assets/js/concise/Media.js
@@ -1,207 +1,98 @@
-/*
-SITE GALLERY MANAGER
-Handles two main functions:
-
-1. Responsive Images
-- Automatically loads appropriate image sizes based on screen width
-- Listens for resize events and updates images accordingly
-- Uses data-small, data-medium, and data-full attributes on images
-
-2. Gallery Management
-- HTML Structure:
-  <a class="open-gallery" data-opens="gallery-id" data-focus="image-to-show">
-    <figure><img...></figure>
-  </a>
-
-  <dialog class="gallery" id="gallery-id">
-    - Main image display (.images with multiple <figure>s)
-    - Navigation bar on bottom (nav.thumbnails) with:
-      - Previous/Next buttons
-      - Thumbnail list (toggles with .open class)
-      - Expand button (toggles .expanded class)
-    - Visibility toggle (toggles .hide-info class)
-    - Close button
-  </dialog>
-
-Key Features:
-- Opens in modal dialog with keyboard navigation
-- Manages thumbnail navigation state
-- Handles responsive image loading
-- Uses class-based state management (open, expanded, hide-info)
-- All event handling through delegation
-- Built on UIHandler base class for state management
-
-State Management:
-- Dialog: open/closed
-- Thumbnails: collapsed/open/expanded
-- Info panel: visible/hidden
-- Active image: tracked with .focused class
-*/
 class Media {
 	constructor() {
 		this.currentWidth = window.innerWidth;
-		this.initElements();
-		if (this.images.length === 0) {
-			return;
-		}
-		this.store = new window.jvbStore({
-			name: 'images',
-			TTL: 604800
-		});
-		this.touch = {
-			x: null,
-			y: null
-		}
-		this.a11y = window.jvbA11y;
-		this.debouncer = window.debouncer;
-		this.isTouching = false;
+		this.images = document.querySelectorAll('.wp-site-blocks img[data-small]');
+
+		if (this.images.length === 0) return;
+
+		// Immediately load visible images
+		this.loadVisibleImages();
+
 		this.initListeners();
-		document.addEventListener('beforeunload', this.cleanup);
 	}
 
-	initElements(){
-		this.images = document.querySelectorAll('.wp-site-blocks img');
-		this.gallery = document.querySelector('dialog.gallery');
-		this.modal = new window.jvbModal(this.gallery, {
-			openMessage: 'Opened Gallery',
-			closeMessage: 'Closed Gallery',
-			open: '.open-gallery',
-			close: '.close'
-		});
+	loadVisibleImages() {
+		// Load first image immediately, plus any in viewport
+		this.images.forEach((img, index) => {
+			const rect = img.getBoundingClientRect();
+			const isVisible = rect.top < window.innerHeight && rect.bottom > 0;
 
-		this.modal.subscribe((event, data) => {
-			if (event === 'modal-open') {
-				this.openGallery();
-			} else if (event === 'modal-close') {
-				this.closeGallery();
+			// Always load first image, or if currently visible
+			if (index === 0 || isVisible) {
+				this.loadAppropriateImage(img);
+				img.dataset.loaded = 'true'; // Mark so we don't observe it
 			}
 		});
 	}
 
 	initListeners() {
 		this.resizeHandler = this.handleResize.bind(this);
-		this.clickHandler = this.handleClick.bind(this);
-		this.keysHandler = this.handleKeys.bind(this);
-		this.touchStartHandler = this.handleTouchStart.bind(this);
-		this.touchEndHandler = this.handleTouchEnd.bind(this);
-
-		document.addEventListener('click', this.clickHandler);
 		window.addEventListener('resize', this.resizeHandler);
-		console.log('window hash: ',window.location.hash);
-		let target = document.querySelector(window.location.hash);
-		if (target && target.tagName === 'LI') {
-			let trigger = target.querySelector('.open-gallery');
-			if (trigger) {
-				this.openGallery(trigger);
-			}
-		}
 
-		this.observer = new IntersectionObserver((entries) =>{
+		// Only observe images that weren't immediately loaded
+		this.observer = new IntersectionObserver((entries) => {
 			entries.forEach(entry => {
 				if (entry.isIntersecting) {
-					let img = entry.target;
-					if (!img.closest('dialog')) {
-						this.loadAppropriateImage(img);
-						this.observer.unobserve(img);
-					}
+					this.loadAppropriateImage(entry.target);
+					this.observer.unobserve(entry.target);
 				}
-			})
+			});
 		}, {
-			root: null,
 			rootMargin: '50px',
-			threshold: .1
+			threshold: 0.1
 		});
+
 		this.images.forEach(img => {
-			if (!img.closest('dialog')) {
+			if (!img.dataset.loaded) {
 				this.observer.observe(img);
 			}
-		})
+		});
 	}
 
-	initTouchHandling() {
-		this.isTouching = true;
-		this.touch.x = 0;
-		this.touch.y = 0;
-
-		this.gallery.addEventListener('touchstart', this.touchStartHandler);
-		this.gallery.addEventListener('touchend', this.touchEndHandler);
-	}
-	cancelTouchHandling() {
-		this.isTouching = false;
-		this.gallery.removeEventListener('touchstart', this.touchStartHandler);
-		this.gallery.removeEventListener('touchend', this.touchEndHandler);
-	}
-
-	handleTouchStart(e) {
-		this.touch.x = e.touches[0].clientX;
-		this.touch.y = e.touches[0].clientY;
-	}
-	handleTouchEnd(e) {
-		const diffX = e.changedTouches[0].clientX - this.touch.x;
-		const diffY = e.changedTouches[0].clientY - this.touch.y;
-
-		if (Math.abs(diffX) > Math.abs(diffY) && Math.abs(diffX) > 50) {
-			this.navigateImages({target: this.gallery}, diffX < 0 ? 'next' : 'previous');
-		}
-	}
-	handleResize(e) {
-		window.debouncer.schedule(
-			'resize',
-			()=> {
-				const currentWidth = window.innerWidth;
-				if (Math.abs(currentWidth - this.currentWidth) > 100) {
-					this.currentWidth = currentWidth;
-					this.handleImageResize();
-				}
-			},
-			150
-		);
-	}
-	handleClick(e) {
-
-	}
-	handleKeys(e) {
-		//Escape handled by Modal.js
-		if (e.key === 'Tab') {
-			if (e.shiftKey) {
-
-			} else {
-
+	handleResize() {
+		window.debouncer.schedule('image-resize', () => {
+			const newWidth = window.innerWidth;
+			if (Math.abs(newWidth - this.currentWidth) > 100) {
+				this.currentWidth = newWidth;
+				this.updateVisibleImages();
 			}
+		}, 150);
+	}
+
+	updateVisibleImages() {
+		this.images.forEach(img => {
+			const rect = img.getBoundingClientRect();
+			if (rect.top < window.innerHeight && rect.bottom > 0) {
+				this.loadAppropriateImage(img, true);
+			}
+		});
+	}
+
+	loadAppropriateImage(img, forceUpdate = false) {
+		const targetSize = this.getTargetSize();
+		const newSrc = img.dataset[targetSize];
+
+		if (newSrc && (forceUpdate || newSrc !== img.currentSrc)) {
+			img.src = newSrc;
 		}
 	}
 
-	openGallery(target = null) {
-		document.addEventListener('keydown', this.clickHandler);
-		const id = target.dataset.opens;
-		this.initTouchHandling();
-		let focus;
-		if (!target) {
-			focus = this.images[0];
-		} else {
-			focus = target.dataset.focus;
-		}
-		if (focus) {
-			this.updateImage(this.gallery.querySelector(`#${focus}`));
-		}
-	}
-
-	closeGallery() {
-		this.cancelTouchHandling();
-		document.removeEventListener('keydown', this.keysHandler);
-		//Restore focus
-		if (this.lastFocusedElement) {
-			this.lastFocusedElement.focus();
-		}
+	getTargetSize() {
+		if (this.currentWidth < 768) return 'small';
+		if (this.currentWidth < 1200) return 'medium';
+		return 'full';
 	}
 
 	cleanup() {
-		this.observer.disconnect();
-		window.removeEventListener('resize', this.resizeHandler)
-		if (this.isTouching) {
-			this.cancelTouchHandling();
-			document.removeEventListener('keydown', this.keysHandler);
-		}
-		document.removeEventListener('click', this.clickHandler);
+		this.observer?.disconnect();
+		window.removeEventListener('resize', this.resizeHandler);
 	}
 }
+
+window.isLoaded = false;
+document.addEventListener('readystatechange', () => {
+	if (!window.isLoaded && document.querySelector('.wp-site-blocks img')) {
+		window.jvbMedia = new Media();
+		window.isLoaded = true;
+	}
+});
diff --git a/assets/js/concise/PopulateForm.js b/assets/js/concise/PopulateForm.js
index 4a56a10..a15d1fa 100644
--- a/assets/js/concise/PopulateForm.js
+++ b/assets/js/concise/PopulateForm.js
@@ -3,10 +3,6 @@
 **********************************************/
 class PopulateForm {
 	constructor(form, fieldData = {}, imageData = {}, options = {}) {
-		console.log('Populating field... ', form);
-		console.log('fieldData: ', fieldData);
-		console.log('imageData: ', imageData);
-		console.log('options: ', options);
 		for (let [fieldName, fieldValue] of Object.entries(fieldData)) {
 			let wrapper = form.querySelector(`[data-field="${fieldName}"]`);
 			if (wrapper) {
@@ -480,17 +476,78 @@
 	 * Populate gallery fields
 	 */
 	populateGalleryField(fieldWrapper, fieldName, fieldValue, imagesData = {}) {
-		this.populateImageField(fieldWrapper, fieldName, fieldValue, imagesData);
+		if (fieldName === 'timeline') {
+			this.populateTimelineGallery(fieldWrapper, fieldName, fieldValue, imagesData);
+		} else {
+			this.populateImageField(fieldWrapper, fieldName, fieldValue, imagesData);
+		}
+	}
+
+	populateTimelineGallery(fieldWrapper, fieldName, fieldValue, imagesData)
+	{
+		if (!fieldValue) {
+			return;
+		}
+
+		const imageIds = Object.keys(fieldValue);
+		if (imageIds.length === 0) {
+			return;
+		}
+
+		// Update image display
+		const grid = fieldWrapper.querySelector('.item-grid');
+		const uploadContainer = fieldWrapper.querySelector('.file-upload-container');
+		fieldWrapper.querySelector('.progress')?.remove();
+		if (grid) {
+			window.removeChildren(grid);
+			imageIds.forEach(imageId => {
+				let image = window.getTemplate('timelineItem');
+				let img = image.querySelector('img');
+
+				image.querySelector('video')?.remove();
+				image.querySelector('.select-item span')?.remove();
+				let data = fieldValue[imageId];
+
+				let imgData = imagesData[imageId];
+
+				img.src = imgData.medium;
+
+				data = { ... data, ... imgData};
+
+				let fields = image.querySelectorAll('.field');
+				fields.forEach(field => {
+					if (field.classList.contains('group')){
+						return;
+					}
+					let input = field.querySelector('input, textarea');
+
+					let label = field.querySelector('label');
+					let name = input.name.replace('upload_data::','');
+
+					let value = data[name];
+					this.populateField(field, name, value, imagesData);
+
+					let id = data.id;
+					let newName = `[${id}]${name}`;
+					[
+						input.name,
+						input.id,
+						label.htmlFor
+					] = [
+						newName,
+						newName,
+						newName,
+					]
+				});
+				grid.append(image);
+			});
+		}
 	}
 
 	/**
 	 * Populate repeater fields
 	 */
 	populateRepeaterField(fieldWrapper, fieldName, fieldValue, options = {}) {
-		console.log('fieldWrapper', fieldWrapper);
-		console.log('fieldName', fieldName);
-		console.log('fieldValue', fieldValue);
-		console.log('options', options);
 		if (!fieldValue || !Array.isArray(fieldValue)) {
 			return;
 		}
diff --git a/assets/js/concise/Queue.js b/assets/js/concise/Queue.js
index d1a3e70..38d9105 100644
--- a/assets/js/concise/Queue.js
+++ b/assets/js/concise/Queue.js
@@ -608,9 +608,9 @@
 	 *********************************************/
 	initUI() {
 		this.icons = {
-			queued: 'refresh', localProcessing: 'refresh', uploading: 'syncing',
-			pending: 'cloud', processing: 'syncing', completed: 'synced',
-			failed: 'error', failed_permanent: 'error'
+			queued: 'arrows-clockwise', localProcessing: 'arrows-clockwise', uploading: 'syncing',
+			pending: 'cloud', processing: 'syncing', completed: 'cloud-check',
+			failed: 'cloud-warning', failed_permanent: 'cloud-warning'
 		};
 
 		this.selectors = {
diff --git a/assets/js/concise/Referral.js b/assets/js/concise/Referral.js
index 6f2442d..61adcc2 100644
--- a/assets/js/concise/Referral.js
+++ b/assets/js/concise/Referral.js
@@ -323,7 +323,7 @@
 				data.context = {};
 				data.context['redirect_to'] = window.location.href+'?seeReferral=1';
 				console.log('Making Request with: ', data);
-				result = await this.makeRequest('magic-link', data);
+				result = await this.makeRequest('magic', data);
 			}
 
 
@@ -344,7 +344,7 @@
 
 	async makeRequest(endpoint, data) {
 		if (![
-			'magic-link',
+			'magic',
 			'referrals/register',
 			'referrals/check-code'
 		].includes(endpoint)) {
diff --git a/assets/js/concise/TaxonomySelector.js b/assets/js/concise/TaxonomySelector.js
index 3262ea8..1dcba38 100644
--- a/assets/js/concise/TaxonomySelector.js
+++ b/assets/js/concise/TaxonomySelector.js
@@ -960,8 +960,9 @@
 
 		if (!field) return;
 
-
+		// Store current value immediately (fixes fast typing issue)
 		const query = e.target.value.trim();
+		field.currentAutocompleteQuery = query;
 
 		if (query.length < 2) {
 			if (field.autocompleteDropdown) {
@@ -972,12 +973,6 @@
 		}
 
 		this.activeField = fieldId;
-		this.currentConfig = field;
-
-
-		if (field.canCreate && ! this.creator) {
-			this.creator = new window.jvbTaxCreator(this);
-		}
 		this.isAutocompleteActive = true;
 
 		if (field.autocompleteDropdown) {
@@ -1041,15 +1036,26 @@
 			});
 		}
 
-		// Offer to create new term if creator is available
-		if (this.creator) {
-			const createOption = this.creator.createAutocompleteOption(query, field);
+		// Use stored current query instead of debounced one
+		const currentQuery = field.currentAutocompleteQuery || query;
+		if (field.canCreate && currentQuery && window.jvbTaxCreator) {
+			const createOption = this.createNewTermOption(currentQuery);
 			dropdown.appendChild(createOption);
 		}
 
 		dropdown.hidden = false;
 	}
 
+	createNewTermOption(query) {
+		const button = document.createElement('button');
+		button.type = 'button';
+		button.className = 'autocomplete-item create-term';
+		button.dataset.query = query;
+		button.innerHTML = `<strong>Create:</strong> "${query}"`;
+
+		return button;
+	}
+
 	createAutocompleteTermElement(field, term) {
 		const item = document.createElement('button');
 		item.type = 'button';
diff --git a/assets/js/concise/UploadManager.js b/assets/js/concise/UploadManager.js
index 0fd8f6e..b59101c 100644
--- a/assets/js/concise/UploadManager.js
+++ b/assets/js/concise/UploadManager.js
@@ -99,6 +99,20 @@
 			'failed_permanent': 'Upload failed permanently'
 		};
 
+		// Sortable configuration
+		this.sortableInstances = new Map();
+		this.sortableConfig = {
+			animation: 150,
+			draggable: '.item',
+			handle: '.select-item-label, img', // Can drag by image or checkbox label
+			ghostClass: 'sortable-ghost',
+			chosenClass: 'sortable-chosen',
+			dragClass: 'sortable-drag',
+			onEnd: (evt) => {
+				this.handleReorder(evt);
+			}
+		};
+
 		this.init();
 	}
 
@@ -210,6 +224,9 @@
 		if (config.destination === 'post_group' && !this.dragController) {
 			this.initGroupFeatures();
 		}
+		if (config.type !== 'single') {
+			this.initSortable(field);
+		}
 
 		return fieldId;
 	}
@@ -356,6 +373,76 @@
 		});
 	}
 
+	initSortable(field) {
+		if (!window.Sortable) return;
+
+		// Main grid
+		const mainGrid = field.element.querySelector('.item-grid:not(.group)');
+		if (mainGrid) {
+			this.sortableInstances.set(`${field.id}-main`,
+				new Sortable(mainGrid, {
+					...this.sortableConfig,
+					group: {
+						name: field.id,
+						pull: true,
+						put: true
+					}
+				})
+			);
+		}
+
+		// Group grids (for selection mode with grouping)
+		const groupGrids = field.element.querySelectorAll('.item-grid.group');
+		groupGrids.forEach((grid, index) => {
+			this.sortableInstances.set(`${field.id}-group-${index}`,
+				new Sortable(grid, {
+					...this.sortableConfig,
+					group: {
+						name: field.id,
+						pull: true,
+						put: true
+					}
+				})
+			);
+		});
+	}
+
+// Add reorder handler
+	handleReorder(evt) {
+		const grid = evt.to;
+		const fieldWrapper = grid.closest('.field, .upload');
+		if (!fieldWrapper) return;
+
+		const form = fieldWrapper.closest('form');
+		if (!form) return;
+
+		// Get form config if available
+		const formId = form.dataset.formId;
+		if (formId && window.jvbForms) {
+			const formConfig = window.jvbForms.forms?.get(formId);
+			if (formConfig?.options.autosave) {
+				// Trigger autosave after reordering
+				window.jvbForms.scheduleSave(formConfig, 1000);
+			}
+		}
+
+		// Announce for accessibility
+		if (window.jvbA11y) {
+			window.jvbA11y.announce('Item reordered');
+		}
+
+		// Trigger custom event
+		fieldWrapper.dispatchEvent(new CustomEvent('jvb-items-reordered', {
+			detail: {
+				from: evt.from,
+				to: evt.to,
+				oldIndex: evt.oldIndex,
+				newIndex: evt.newIndex
+			},
+			bubbles: true
+		}));
+	}
+
 	/*******************************************************************************
 	 * EXTERNAL FILE DROP HANDLERS (for new uploads from desktop)
 	 *******************************************************************************/
@@ -2936,6 +3023,12 @@
 		this.selectionHandlers.clear();
 
 		this.cleanupAllPreviewUrls();
+		this.sortableInstances.forEach(instance => {
+			if (instance?.destroy) {
+				instance.destroy();
+			}
+		});
+		this.sortableInstances.clear();
 
 		// Clear data
 		this.fields.clear();
@@ -2945,6 +3038,20 @@
 		this.subscribers.clear();
 	}
 
+	destroySortable(fieldName) {
+		// Destroy all sortable instances for this field
+		const instances = Array.from(this.sortableInstances.keys())
+			.filter(key => key.startsWith(fieldName));
+
+		instances.forEach(key => {
+			const instance = this.sortableInstances.get(key);
+			if (instance?.destroy) {
+				instance.destroy();
+			}
+			this.sortableInstances.delete(key);
+		});
+	}
+
 	cleanupRestore() {
 		this.restoreModal.handleClose();
 		this.restoreSelection.destroy();
diff --git a/assets/js/concise/View.js b/assets/js/concise/View.js
index 96bbdc8..e1e5488 100644
--- a/assets/js/concise/View.js
+++ b/assets/js/concise/View.js
@@ -171,7 +171,7 @@
 			console.error('No store connected to renderer');
 			return;
 		}
-
+		console.log(items);
 
 		// Handle empty state
 		if (items.length === 0) {
@@ -268,15 +268,15 @@
 			item.id,
 			item.id
 		];
-		if (this.store.config.storeName === 'progress') {
-			[
-				img.src,
-				img.alt,
-			] = [
-				item.images[item.fields['timeline'][0].post_thumbnail]?.medium??'',
-				item.images[item.fields['timeline'][0].post_thumbnail]?.alt??'',
-			];
-		} else {
+		// if (this.store.config.storeName === 'progress') {
+		// 	[
+		// 		img.src,
+		// 		img.alt,
+		// 	] = [
+		// 		item.images[item.fields['timeline'][0].post_thumbnail]?.medium??'',
+		// 		item.images[item.fields['timeline'][0].post_thumbnail]?.alt??'',
+		// 	];
+		// } else {
 			[
 				img.src,
 				img.alt,
@@ -284,7 +284,7 @@
 				item.images[item.fields.post_thumbnail]?.medium??'',
 				item.images[item.fields.post_thumbnail]?.alt??'',
 			];
-		}
+		// }
 		return card;
 	}
 
diff --git a/assets/js/concise/quill.js b/assets/js/concise/quill.js
index b18718a..847f8eb 100644
--- a/assets/js/concise/quill.js
+++ b/assets/js/concise/quill.js
@@ -17,54 +17,54 @@
 			toolbar.innerHTML = `
                 <span class="ql-formats">
                     <button type="button" class="ql-p">
-                        ${jvbSettings.icons.paragraph}
+                        <i class="icon icon-paragraph"></i>
                     </button>
                     <button type="button" class="ql-h1">
-                        ${jvbSettings.icons.h1}
+                        <i class="icon icon-text-h-one"></i>
                     </button>
                     <button type="button" class="ql-h2">
-                        ${jvbSettings.icons.h2}
+                        <i class="icon icon-text-h-two"></i>
                     </button>
                     <button type="button" class="ql-h3">
-                        ${jvbSettings.icons.h3}
+                        <i class="icon icon-text-h-three"></i>
                     </button>
                 </span>
                 <span class="ql-formats">
                     <button type="button" class="ql-jvb_bold">
-                        ${jvbSettings.icons['bold']}
+                        <i class="icon icon-text-b-fi"></i>
                     </button>
                     <button type="button" class="ql-jvb_italic">
-                        ${jvbSettings.icons['italic']}
+                        <i class="icon icon-text-italic"></i>
                     </button>
                     <button type="button" class="ql-jvb_underline">
-                        ${jvbSettings.icons['underline']}
+                        <i class="icon icon-text-underline"></i>
                     </button>
                     <button type="button" class="ql-jvb_strike">
-                        ${jvbSettings.icons['strike']}
+                        <i class="icon icon-text-strikethrough"></i>
                     </button>
                 </span>
                 <span class="ql-formats">
                      <button type="button" class="ql-jvb_list" value="bullet">
-                        ${jvbSettings.icons['list-bullets']}
+                        <i class="icon icon-list-dashes"></i>
                     </button>
                     <button type="button" class="ql-jvb_list" value="ordered">
-                        ${jvbSettings.icons['list-numbers']}
+                        <i class="icon icon-list-numbers"></i>
                     </button>
                 </span>
                 <span class="ql-formats">
                      <button type="button" class="ql-jvb_align" value="left">
-                        ${jvbSettings.icons['align-left']}
+                        <i class="icon icon-text-align-left"></i>
                     </button>
                      <button type="button" class="ql-jvb_align" value="center">
-                        ${jvbSettings.icons['align-center']}
+                        <i class="icon icon-text-align-center"></i>
                     </button>
                      <button type="button" class="ql-jvb_align" value="right">
-                        ${jvbSettings.icons['align-right']}
+                        <i class="icon icon-text-align-right"></i>
                     </button>
                 </span>
                 <span class="ql-formats">
                      <button type="button" class="ql-jvb_link">
-                        ${jvbSettings.icons.link}
+                        <i class="icon icon-link"></i>
                     </button>
                     ${image}
                 </span>
diff --git a/assets/js/dash/CRUD.js b/assets/js/dash/CRUD.js
index 670f625..dfb568b 100644
--- a/assets/js/dash/CRUD.js
+++ b/assets/js/dash/CRUD.js
@@ -12,6 +12,7 @@
 		if (!this.content) {
 			return;
 		}
+		this.isTimeline = false;
 
 		this.initElements();
 		this.updateBulkOptions();
@@ -69,6 +70,14 @@
 		this.init();
 	}
 	handleFormChange(event, data) {
+		if (this.isTimeline) {
+			data.changes.content = this.content;
+			let changes = {};
+			let postID = data.config.id.replace('edit-', '');
+			changes[postID] = data.changes;
+			this.savePosts(changes, `Saving ${data.fullData.post_title}'s Changes`);
+			return;
+		}
 		data.changes.content = this.content;
 		let changes = {};
 		let title = '';
@@ -185,6 +194,7 @@
 			uploader: 'details.uploader'
 		};
 		this.ui = window.uiFromSelectors(this.elements);
+		this.isTimeline = Object.hasOwn(this.ui.forms.edit.dataset, 'timeline');
 	}
 	init() {
 		this.settings.addSetting(this.ui.uploader, 'open');
diff --git a/assets/js/dash/FavouritesManager.js b/assets/js/dash/FavouritesManager.js
index 7b57799..e204a04 100644
--- a/assets/js/dash/FavouritesManager.js
+++ b/assets/js/dash/FavouritesManager.js
@@ -1244,15 +1244,15 @@
         actions.className = 'list-actions';
         actions.innerHTML = `
             <button type="button" class="share-list" title="Share List">
-            <?= $icon->getIcon('share')?>
+            <i class="icon icon-share-fat"></i>
             <span>Share</span>
         </button>
         <button type="button" class="duplicate-list" title="Duplicate List">
-            <?= $icon->getIcon('copy')?>
+            <i class="icon icon-copy"></i>
             <span>Duplicate</span>
         </button>
         <button type="button" class="delete-list" title="Delete List">
-            <?= $icon->getIcon('delete')?>
+            <i class="icon icon-trash"></i>
             <span>Delete</span>
         </button>
         `;
diff --git a/assets/js/dash/NewsManager.js b/assets/js/dash/NewsManager.js
index b7eb5be..a4e03de 100644
--- a/assets/js/dash/NewsManager.js
+++ b/assets/js/dash/NewsManager.js
@@ -334,7 +334,7 @@
             [favourite.dataset.id, favourite.dataset.artist] = [item.id, item.artist.id];
             if(window.userFavourites.news?.includes(parseInt(item.id))){
                 removeChildren(favourite);
-                favourite.append(getIcon('star-filled'));
+                favourite.append(getIcon('star-fi'));
             }else{
                 removeChildren(favourite);
                 favourite.append(getIcon('star'));
diff --git a/assets/js/dash/TaxonomyCreator.js b/assets/js/dash/TaxonomyCreator.js
index 3c783c7..8f27b17 100644
--- a/assets/js/dash/TaxonomyCreator.js
+++ b/assets/js/dash/TaxonomyCreator.js
@@ -39,6 +39,11 @@
 		if (window.targetCheck(e, '.submit-term')) {
 			this.handleTermCreation(e);
 		}
+
+		// Handle autocomplete create button
+		if (window.targetCheck(e, '.create-term')) {
+			this.handleAutocompleteCreate(e);
+		}
 	}
 
 	async handleTermCreation(e) {
@@ -94,6 +99,56 @@
 		}
 	}
 
+	async handleAutocompleteCreate(e) {
+		const button = e.target;
+		const fieldId = this.selector.getFieldId(button);
+		const field = this.selector.fields.get(fieldId);
+
+		if (!field) return;
+
+		// Get the current input value (not the debounced query)
+		const input = field.container.querySelector('input[data-autocomplete]');
+		const termName = input?.value.trim() || button.dataset.query;
+
+		if (!termName) return;
+
+		try {
+			button.disabled = true;
+			const originalText = button.innerHTML;
+			button.textContent = 'Creating...';
+
+			const response = await this.createTerm(termName, 0);
+
+			if (response.success && response.term) {
+				const term = response.term;
+
+				// Add term to field
+				field.selectedTerms.add(parseInt(term.id));
+				this.selector.addTermToDisplay(field.id, term.id, term.name, term.path || term.name);
+
+				// Update input
+				field.input.value = Array.from(field.selectedTerms).join(',');
+				field.input.dispatchEvent(new Event('change', { bubbles: true }));
+
+				// Clear and hide dropdown
+				field.autocompleteDropdown.hidden = true;
+				if (input) input.value = '';
+
+				// Refresh the store to include the new term
+				await this.selector.store.setFilter('taxonomy', field.taxonomy);
+			} else {
+				button.innerHTML = originalText;
+			}
+		} catch (error) {
+			console.error('Error creating term:', error);
+			button.textContent = 'Failed - Try again';
+			setTimeout(() => {
+				button.innerHTML = `<strong>Create:</strong> "${termName}"`;
+				button.disabled = false;
+			}, 2000);
+		}
+	}
+
 	initTermCreation() {
 		if (!this.form) {
 			return;
@@ -328,6 +383,7 @@
 		return button;
 	}
 
+
 	/**
 	 * Handle term creation from autocomplete
 	 */
diff --git a/assets/js/dash/UtilityFunctions.js b/assets/js/dash/UtilityFunctions.js
index 4c337dd..23f0737 100644
--- a/assets/js/dash/UtilityFunctions.js
+++ b/assets/js/dash/UtilityFunctions.js
@@ -189,10 +189,17 @@
  * @param icon
  * @returns {Node | ActiveX.IXMLDOMNode}
  */
+window.icon = document.querySelector('.icon').cloneNode(true);
+window.icon.className = 'icon';
 window.getIcon = function getIcon(icon){
 	if (typeof icon === 'undefined') {
 		return '';
 	}
+	console.log('Getting icon: '+icon);
+
+	let theIcon = window.icon.cloneNode(true);
+	theIcon.classList.add('icon-'+icon);
+	return theIcon;
 
 
 	if(!window.jvbIcons){
diff --git a/assets/js/min/creator.min.js b/assets/js/min/creator.min.js
index 9f9f39f..b5672e4 100644
--- a/assets/js/min/creator.min.js
+++ b/assets/js/min/creator.min.js
@@ -1 +1 @@
-window.jvbTaxCreator=class{constructor(e){this.selector=e,this.taxonomy=e.currentConfig?.taxonomy,this.taxonomy?(this.createNew=e.modal.querySelector(".create-new-term"),this.toggle=e.modal.querySelector(".new-term-toggle"),this.form=this.createNew.querySelector(".create-new-term-section"),this.initListeners(),this.initTermCreation()):console.error("TaxonomyCreator: No active field or taxonomy found")}initListeners(){this.clickHandler=this.handleClick.bind(this),document.addEventListener("click",this.clickHandler)}handleClick(e){window.targetCheck(e,".create-new-term summary")&&(this.createNew.open&&this.createNew.querySelector('input[name="term_name"]').focus(),this.resetParentOptions()),window.targetCheck(e,".submit-term")&&this.handleTermCreation(e)}async handleTermCreation(e){const t=this.form.querySelector('input[name="term_name"]').value.trim(),r=parseInt(this.form.querySelector("input#select_parent")?.value)||0;if(t)try{this.form.querySelector("button").disabled=!0;const e=await this.createTerm(t,r);if(e.success&&e.term){let t=e.term;this.createNew.open=!1,await this.selector.store.invalidate({taxonomy:this.taxonomy}),this.selector.addSelectedTermToModal(t.id,t.name,t.path||t.name),(this.selector.store.filters.parent||0)===r&&await this.selector.store.setFilters({taxonomy:this.taxonomy,parent:r,page:1,search:""}),this.form.querySelector('input[name="term_name"]').value="";const n=this.createNew.querySelector(".term-suggestions");n&&(n.hidden=!0)}}catch(e){console.error("Error creating term:",e),this.selector.error?.log(e,{component:"TaxonomyCreator",action:"handleTermCreation"})||console.error("Failed to create term")}finally{this.form.querySelector("button").disabled=!1}}initTermCreation(){this.form&&this.form.addEventListener("change",(e=>{e.preventDefault(),e.stopPropagation()}))}resetParentOptions(){let e=this.createNew.querySelector("#select_parent");if(!e)return;let t=e.querySelector("option");if(!t)return;window.removeChildren(e),e.append(t.cloneNode(!0));const r=this.selector.store.filters.parent||0;if(0!==r){const n=this.selector.store.data.get(r);if(n){let r=t.cloneNode(!0);r.value=n.id,r.textContent=n.name,e.append(r)}}const n=[];this.selector.store.data.forEach((e=>{e.taxonomy===this.taxonomy&&e.parent===r&&n.push(e)})),n.sort(((e,t)=>e.name.localeCompare(t.name))),n.forEach((r=>{let n=t.cloneNode(!0);n.id=`select-parent-${r.id}`,n.value=r.id,n.textContent="  — "+r.name,e.append(n)}))}async createTerm(e,t=0){let r=this.createNew.querySelector(".loading-message.create-term"),n=r?.querySelector("span");try{r&&(r.hidden=!1),n&&window.typeText?window.typeText(n,"Checking term..."):n&&(n.textContent="Checking term...");const o=await this.searchExistingTerms(e),a=o.filter((t=>t.name.toLowerCase()===e.toLowerCase()));if(a.length>0)return this.showTermSuggestions(a,!0),{success:!1,reason:"exists"};if(o.length>0)return this.showTermSuggestions(o,!1),{success:!1,reason:"similar"};n&&(n.textContent="Creating term...");const s=await fetch(`${jvbSettings.api}terms`,{method:"POST",headers:{"Content-Type":"application/json","X-WP-Nonce":jvbSettings.nonce},body:JSON.stringify({taxonomy:this.taxonomy,name:e,parent:t})});if(!s.ok)throw new Error(`Server error: ${s.status}`);return await s.json()}catch(e){throw console.error("Error creating term:",e),e}finally{this.form.querySelector("button").disabled=!1,r&&(r.hidden=!0)}}async searchExistingTerms(e){return new Promise((t=>{const r=(e,n)=>{"data-loaded"===e&&(this.selector.store.unsubscribe(r),t(n.data?.items||[]))};this.selector.store.subscribe(r),this.selector.store.setFilters({taxonomy:this.taxonomy,search:e,page:1,parent:0})}))}showTermSuggestions(e,t=!1){const r=this.createNew.querySelector(".term-suggestions")||this.createSuggestionContainer();window.removeChildren(r);const n=document.createElement("h4");n.textContent=t?"This term already exists:":"Similar terms already exist:",r.appendChild(n);const o=document.createElement("ul");o.className="term-suggestion-list",e.forEach((e=>{const t=document.createElement("li"),n=document.createElement("button");n.type="button",n.className="use-existing-term",n.setAttribute("data-id",e.id),n.textContent=e.path||e.name,n.addEventListener("click",(()=>{this.selector.addSelectedTermToModal(e.id,e.name,e.path||e.name),this.createNew.open=!1,r.hidden=!0,this.form.querySelector('input[name="term_name"]').value=""})),t.appendChild(n),o.appendChild(t)})),r.appendChild(o),r.hidden=!1}createSuggestionContainer(){const e=document.createElement("div");return e.className="term-suggestions",e.hidden=!0,this.createNew.querySelector("form").after(e),e}createAutocompleteOption(e,t){const r=document.createElement("button");return r.type="button",r.className="autocomplete-item create-term",r.innerHTML=`<span>Create "${e}"</span>`,r.dataset.query=e,r.dataset.fieldId=t.id,r.addEventListener("click",(async()=>{await this.handleAutocompleteCreate(r,e,t)})),r}async handleAutocompleteCreate(e,t,r){if(!r)return;const n=e.innerHTML;try{e.disabled=!0,e.innerHTML="<span>Creating...</span>";const n=0,o=await this.createTerm(t,n);if(o.success&&o.term){const e=o.term;r.selectedTerms.add(parseInt(e.id)),this.selector.addTermToDisplay(r.id,e.id,e.name,e.path||e.name),r.input.value=Array.from(r.selectedTerms).join(","),r.input.dispatchEvent(new Event("change",{bubbles:!0})),await this.selector.store.invalidate({taxonomy:r.taxonomy}),r.autocompleteDropdown.hidden=!0;const t=r.container.querySelector("input[data-autocomplete]");t&&(t.value="")}}catch(t){console.error("Error creating term:",t),e.innerHTML=n,e.disabled=!1,this.selector.error?.log(t,{component:"TaxonomyCreator",action:"handleAutocompleteCreate"})}}destroy(){this.clickHandler&&document.removeEventListener("click",this.clickHandler);const e=this.createNew?.querySelector(".loading-message.create-term");e&&(e.hidden=!0);const t=this.createNew?.querySelector(".term-suggestions");t&&(t.hidden=!0)}};
\ No newline at end of file
+window.jvbTaxCreator=class{constructor(e){this.selector=e,this.taxonomy=e.currentConfig?.taxonomy,this.taxonomy?(this.createNew=e.modal.querySelector(".create-new-term"),this.toggle=e.modal.querySelector(".new-term-toggle"),this.form=this.createNew.querySelector(".create-new-term-section"),this.initListeners(),this.initTermCreation()):console.error("TaxonomyCreator: No active field or taxonomy found")}initListeners(){this.clickHandler=this.handleClick.bind(this),document.addEventListener("click",this.clickHandler)}handleClick(e){window.targetCheck(e,".create-new-term summary")&&(this.createNew.open&&this.createNew.querySelector('input[name="term_name"]').focus(),this.resetParentOptions()),window.targetCheck(e,".submit-term")&&this.handleTermCreation(e),window.targetCheck(e,".create-term")&&this.handleAutocompleteCreate(e)}async handleTermCreation(e){const t=this.form.querySelector('input[name="term_name"]').value.trim(),r=parseInt(this.form.querySelector("input#select_parent")?.value)||0;if(t)try{this.form.querySelector("button").disabled=!0;const e=await this.createTerm(t,r);if(e.success&&e.term){let t=e.term;this.createNew.open=!1,await this.selector.store.invalidate({taxonomy:this.taxonomy}),this.selector.addSelectedTermToModal(t.id,t.name,t.path||t.name),(this.selector.store.filters.parent||0)===r&&await this.selector.store.setFilters({taxonomy:this.taxonomy,parent:r,page:1,search:""}),this.form.querySelector('input[name="term_name"]').value="";const n=this.createNew.querySelector(".term-suggestions");n&&(n.hidden=!0)}}catch(e){console.error("Error creating term:",e),this.selector.error?.log(e,{component:"TaxonomyCreator",action:"handleTermCreation"})||console.error("Failed to create term")}finally{this.form.querySelector("button").disabled=!1}}async handleAutocompleteCreate(e){const t=e.target,r=this.selector.getFieldId(t),n=this.selector.fields.get(r);if(!n)return;const o=n.container.querySelector("input[data-autocomplete]"),a=o?.value.trim()||t.dataset.query;if(a)try{t.disabled=!0;const e=t.innerHTML;t.textContent="Creating...";const r=await this.createTerm(a,0);if(r.success&&r.term){const e=r.term;n.selectedTerms.add(parseInt(e.id)),this.selector.addTermToDisplay(n.id,e.id,e.name,e.path||e.name),n.input.value=Array.from(n.selectedTerms).join(","),n.input.dispatchEvent(new Event("change",{bubbles:!0})),n.autocompleteDropdown.hidden=!0,o&&(o.value=""),await this.selector.store.setFilter("taxonomy",n.taxonomy)}else t.innerHTML=e}catch(e){console.error("Error creating term:",e),t.textContent="Failed - Try again",setTimeout((()=>{t.innerHTML=`<strong>Create:</strong> "${a}"`,t.disabled=!1}),2e3)}}initTermCreation(){this.form&&this.form.addEventListener("change",(e=>{e.preventDefault(),e.stopPropagation()}))}resetParentOptions(){let e=this.createNew.querySelector("#select_parent");if(!e)return;let t=e.querySelector("option");if(!t)return;window.removeChildren(e),e.append(t.cloneNode(!0));const r=this.selector.store.filters.parent||0;if(0!==r){const n=this.selector.store.data.get(r);if(n){let r=t.cloneNode(!0);r.value=n.id,r.textContent=n.name,e.append(r)}}const n=[];this.selector.store.data.forEach((e=>{e.taxonomy===this.taxonomy&&e.parent===r&&n.push(e)})),n.sort(((e,t)=>e.name.localeCompare(t.name))),n.forEach((r=>{let n=t.cloneNode(!0);n.id=`select-parent-${r.id}`,n.value=r.id,n.textContent="  — "+r.name,e.append(n)}))}async createTerm(e,t=0){let r=this.createNew.querySelector(".loading-message.create-term"),n=r?.querySelector("span");try{r&&(r.hidden=!1),n&&window.typeText?window.typeText(n,"Checking term..."):n&&(n.textContent="Checking term...");const o=await this.searchExistingTerms(e),a=o.filter((t=>t.name.toLowerCase()===e.toLowerCase()));if(a.length>0)return this.showTermSuggestions(a,!0),{success:!1,reason:"exists"};if(o.length>0)return this.showTermSuggestions(o,!1),{success:!1,reason:"similar"};n&&(n.textContent="Creating term...");const s=await fetch(`${jvbSettings.api}terms`,{method:"POST",headers:{"Content-Type":"application/json","X-WP-Nonce":jvbSettings.nonce},body:JSON.stringify({taxonomy:this.taxonomy,name:e,parent:t})});if(!s.ok)throw new Error(`Server error: ${s.status}`);return await s.json()}catch(e){throw console.error("Error creating term:",e),e}finally{this.form.querySelector("button").disabled=!1,r&&(r.hidden=!0)}}async searchExistingTerms(e){return new Promise((t=>{const r=(e,n)=>{"data-loaded"===e&&(this.selector.store.unsubscribe(r),t(n.data?.items||[]))};this.selector.store.subscribe(r),this.selector.store.setFilters({taxonomy:this.taxonomy,search:e,page:1,parent:0})}))}showTermSuggestions(e,t=!1){const r=this.createNew.querySelector(".term-suggestions")||this.createSuggestionContainer();window.removeChildren(r);const n=document.createElement("h4");n.textContent=t?"This term already exists:":"Similar terms already exist:",r.appendChild(n);const o=document.createElement("ul");o.className="term-suggestion-list",e.forEach((e=>{const t=document.createElement("li"),n=document.createElement("button");n.type="button",n.className="use-existing-term",n.setAttribute("data-id",e.id),n.textContent=e.path||e.name,n.addEventListener("click",(()=>{this.selector.addSelectedTermToModal(e.id,e.name,e.path||e.name),this.createNew.open=!1,r.hidden=!0,this.form.querySelector('input[name="term_name"]').value=""})),t.appendChild(n),o.appendChild(t)})),r.appendChild(o),r.hidden=!1}createSuggestionContainer(){const e=document.createElement("div");return e.className="term-suggestions",e.hidden=!0,this.createNew.querySelector("form").after(e),e}createAutocompleteOption(e,t){const r=document.createElement("button");return r.type="button",r.className="autocomplete-item create-term",r.innerHTML=`<span>Create "${e}"</span>`,r.dataset.query=e,r.dataset.fieldId=t.id,r.addEventListener("click",(async()=>{await this.handleAutocompleteCreate(r,e,t)})),r}async handleAutocompleteCreate(e,t,r){if(!r)return;const n=e.innerHTML;try{e.disabled=!0,e.innerHTML="<span>Creating...</span>";const n=0,o=await this.createTerm(t,n);if(o.success&&o.term){const e=o.term;r.selectedTerms.add(parseInt(e.id)),this.selector.addTermToDisplay(r.id,e.id,e.name,e.path||e.name),r.input.value=Array.from(r.selectedTerms).join(","),r.input.dispatchEvent(new Event("change",{bubbles:!0})),await this.selector.store.invalidate({taxonomy:r.taxonomy}),r.autocompleteDropdown.hidden=!0;const t=r.container.querySelector("input[data-autocomplete]");t&&(t.value="")}}catch(t){console.error("Error creating term:",t),e.innerHTML=n,e.disabled=!1,this.selector.error?.log(t,{component:"TaxonomyCreator",action:"handleAutocompleteCreate"})}}destroy(){this.clickHandler&&document.removeEventListener("click",this.clickHandler);const e=this.createNew?.querySelector(".loading-message.create-term");e&&(e.hidden=!0);const t=this.createNew?.querySelector(".term-suggestions");t&&(t.hidden=!0)}};
\ No newline at end of file
diff --git a/assets/js/min/crud.min.js b/assets/js/min/crud.min.js
index 755b286..ce4c697 100644
--- a/assets/js/min/crud.min.js
+++ b/assets/js/min/crud.min.js
@@ -1 +1 @@
-(()=>{class e{constructor(e){this.queue=window.jvbQueue,console.log(this.queue),this.config=e,this.content=e.content||!1,this.settings=window.jvbUserSettings,this.content&&(this.initElements(),this.updateBulkOptions(),this.store=new window.jvbStore({name:this.content,storeName:this.content,endpoint:"content",headers:{action_nonce:jvbSettings.dash},indexes:[{name:"status",keyPath:"post_status"},{name:"modified",keyPath:"modified"}],filters:{content:this.content,user:jvbSettings.currentUser,page:1,status:"all"},TTL:36e5,cacheDOM:!0}),this.status="all",this.filterTimeout=null,this.viewController=new window.jvbViews(this.ui.container,this.store),this.formController=new window.jvbForm,this.formController.subscribe(((e,t)=>{switch(e){case"form-submit":case"form-autosave":this.handleFormChange(e,t)}})),window.jvbQueue&&window.jvbQueue.subscribe(((e,t)=>{"operation-completed"===e&&"form"===t.source?this.handleQueueSuccess(e,t):"operation-failed-permanent"===e&&"form"===t.source&&this.handleQueueFailure(e,t)})),this.initialized=!1,this.init())}handleFormChange(e,t){t.changes.content=this.content;let s={},i="",o=[];switch(!0){case t.config.element===this.ui.forms.edit:let l=t.config.id.replace("edit-","");console.log(l),s[l]=t.changes,i=`Saving ${t.fullData.post_title} Changes`,t.changes.post_status&&this.shouldRemoveItem(t.changes.post_status)&&o.push(l);break;case t.config.element===this.ui.forms.bulkEdit:let n=t.config.element.querySelectorAll(".selected input:checked");n.forEach((e=>{s[e.value]=t.changes,t.changes.post_status&&this.shouldRemoveItem(t.changes.post_status)&&o.push(e.value)})),i=`Updating ${n.length} ${this.config.plural??"posts"} Changes`;break;case t.config.element===this.ui.forms.create:"form-submit"===e&&(s[t.config.data["form-id"]]=t.fullData,i=`Saving ${t.fullData.post_title} Changes`)}if(o.length>0){let e=0;o.forEach((t=>{setTimeout((()=>{const e=document.querySelector(`.item[data-id="${t}"]`);e&&window.fade(e,!1)}),e),e+=50})),t.config.element===this.ui.forms.bulkEdit&&setTimeout((()=>{this.viewController.clearSelection()}),e+100)}window.isEmptyObject(s)||this.savePosts(s,i)}shouldRemoveItem(e){return"all"===this.status&&!["publish","draft"].includes(e)||e!==this.status}savePosts(e,t){if(window.isEmptyObject(e))return;let s={endpoint:"content",headers:{action_nonce:jvbSettings.dash},data:{posts:e},popup:"Saving changes",title:t};this.queue.addToQueue(s)}handleQueueSuccess(e,t){console.log("Handling queue success..."),console.log("Event",e),console.log("Data",t)}handleQueueFailure(e,t){console.log("Handling queue failure..."),console.log("Event",e),console.log("Data",t)}initElements(){this.elements={modals:{create:"dialog.create",edit:"dialog.edit",bulkEdit:"dialog.bulkEdit"},container:".crud[data-content]",grid:".item-grid",bulkSelectActions:".bulk-action-select",forms:{create:"dialog.create form",edit:"dialog.edit form",bulkEdit:"dialog.bulkEdit form"},uploader:"details.uploader"},this.ui=window.uiFromSelectors(this.elements)}init(){this.settings.addSetting(this.ui.uploader,"open"),this.ui.uploader.addEventListener("toggle",(e=>{console.log(e),console.log("Is Open: ",this.ui.uploader.open),console.log(this.ui.uploader.open?"on":"off"),this.settings.saveSetting("open",this.ui.uploader.open?"on":"off")})),this.filterHandler=this.handleFilterChange.bind(this),this.changeHandler=this.handleChange.bind(this),this.modals={};for(let[e,t]of Object.entries(this.ui.modals))this.modals[e]=new window.jvbModal(t),this.modals[e].subscribe(((t,s)=>{if("modal-close"===t)this.formController.cleanupForm(this.modals[e].modal.querySelector("form").dataset.formId),console.log("Data on modal close: ",s)}));this.setupEventDelegation(),this.setupFilters(),this.queue.subscribe(((e,t)=>{e})),this.initialized=!0}setupEventDelegation(){document.addEventListener("change",this.changeHandler),document.addEventListener("click",(e=>{const t=e.target.closest("[data-action]");if(t){e.preventDefault();const s=t.dataset.action,i=t.dataset.id;switch(s){case"edit":this.populateEditForm(i),this.modals.edit.handleOpen();break;case"delete":if(confirm("Delete this item?")){let e={};e[t.dataset.id]={post_status:"delete",content:this.content},window.fade(t.closest(".item"),!1),this.savePosts(e,`Sending ${this.singular} to trash...`),this.store.delete(i)}break;case"trash":let e={};e[t.dataset.id]={post_status:"trash",content:this.content},window.fade(t.closest(".item"),!1),this.savePosts(e,`Sending ${this.singular} to trash...`);break;case"create":this.modals.create.dataset.itemID="new",this.modals.create.dataset.content=this.content,this.modals.create.handleOpen();break;case"bulk-edit":Array.from(this.viewController.selectedItems).length>0&&this.modals.bulkEdit.handleOpen();break;case"bulk-delete":const s=Array.from(this.viewController.selectedItems);s.length>0&&confirm(`Delete ${s.length} items?`)&&(s.forEach((e=>this.store.delete(e))),this.viewController.clearSelection());break;case"sync":break;case"refresh":this.store.fetch()}}e.target.closest(".create-item")&&(this.formController.registerForm(this.ui.forms.create),this.modals.create.handleOpen()),e.target.closest(".cancel-bulk")&&this.viewController.selectAll(!1)})),document.addEventListener("keydown",(e=>{(e.ctrlKey||e.metaKey)&&"a"===e.key&&this.ui.container&&this.ui.container.contains(document.activeElement)&&(e.preventDefault(),this.viewController.selectAll()),"Escape"===e.key&&this.viewController?.selectedItems.size>0&&0===window.jvbModal.getAllModals().length&&this.viewController.clearSelection()}))}handleChange(e){if(e.target.classList.contains("bulk-action-select")){if(e.target.value.startsWith("tax-")){const t=e.target.value.replace("tax-","");return this.openTaxonomyModal(t),void(e.target.value="")}switch(e.target.value){case"edit":this.populateBulkEdit(),this.modals.bulkEdit.handleOpen();break;case"publish":this.setBulkStatus("publish");break;case"draft":case"restore":this.setBulkStatus("draft");break;case"trash":this.setBulkStatus("trash");break;case"delete":this.setBulkStatus("delete")}}}openTaxonomyModal(e){window.jvbSelector?window.jvbSelector.openForFilter(e,((e,t)=>this.handleBulkTaxonomy(e,t))):console.error("TaxonomySelector not initialized")}handleBulkTaxonomy(e,t){if(console.log(t,e),e.length>0){e=e.join(",");let s={},i=Array.from(this.viewController.selectedItems);console.log("selected",i),i.forEach((i=>{s[i]={content:this.content},s[i][t]=e})),console.log("Taxonomy changes: ",s);let o=`Adding ${i.length} ${this.config.plural??"posts"} to ${e.length} ${jvbSettings.labels[t].plural}`;this.viewController.clearSelection(),this.savePosts(s,o)}}setBulkStatus(e){if(!["publish","draft","trash","delete"].includes(e))return;console.log(`Setting status: ${e}`);let t,s={};for(let t of this.viewController.selectedItems)s[t]={post_status:e,content:this.content};if("delete"===e)t="Deleting";else t=window.uppercaseFirst(e)+"ing";if(console.log(this.status),"all"===this.status&&!["publish","draft"].includes(e)||e!==this.status){let e=0;for(let t of this.viewController.selectedItems)setTimeout((()=>{const e=document.querySelector(`.item[data-id="${t}"]`);e&&window.fade(e,!1)}),e),e+=50}this.viewController.clearSelection(),window.isEmptyObject(s)||this.savePosts(s,`${t} ${this.viewController.selectedItems.size} ${this.plural}...`)}handleFilterChange(e){let t=e.target,s=t.dataset.filter;if("taxonomies"===s){let e=t.dataset.taxonomy;this.store.setFilter(`tax_${e}`,s.value)}else this[t.dataset.filter]=t.value,this.store.setFilter(t.dataset.filter,t.value),"status"===t.dataset.filter&&this.updateBulkOptions(t.value)}updateBulkOptions(e="all"){if("trash"===e){if(this.ui.bulkSelectActions.querySelector('[value="edit"]')){window.removeChildren(this.ui.bulkSelectActions),window.getTemplate("trashOptions").querySelectorAll("option").forEach(((e,t)=>{0===t&&(e.checked=!0),this.ui.bulkSelectActions.append(e)}))}}else if(!this.ui.bulkSelectActions.querySelector('[value="edit"]')){window.removeChildren(this.ui.bulkSelectActions),window.getTemplate("notTrashOptions").querySelectorAll("option").forEach(((e,t)=>{this.ui.bulkSelectActions.append(e)}))}this.ui.bulkSelectActions.value=""}populateBulkEdit(){const e=this.modals.bulkEdit.modal.querySelector("form .selected");if(!e)return;window.removeChildren(e);for(let t of this.viewController.selectedItems){console.log(t);let s=this.store.get(t);console.log(s);const i=window.getTemplate("bulkItem");if(!i)return;const o=i.querySelector("input[type=checkbox]"),l=i.querySelector("img");o&&(o.id=`bulk_${s.id}`,o.value=s.id,o.checked=!0),l&&s.thumbnail&&(l.src=s.thumbnail,l.alt=s.alt||""),e.append(i)}let t=this.modals.bulkEdit.modal;[t.querySelector("h2 span").textContent]=[this.viewController.selectedItems.size],this.formController.registerForm(this.ui.forms.bulkEdit),console.log("Bulk Edit form registered")}populateEditForm(e){console.log(e);let t=this.store.get(parseInt(e));if(console.log(t),t){this.ui.modals.edit.dataset.itemID=e,this.ui.modals.edit.dataset.content=this.content;let s=this.ui.modals.edit.querySelector("form");[this.ui.modals.edit.querySelector("h2").textContent]=[`Editing ${t.fields.post_title}`],s.dataset.formId=`edit-${e}`,console.log(s.dataset.formId),new window.jvbPopulate(s,t.fields,t.images),this.formController.registerForm(this.ui.forms.edit),console.log("Edit form registered")}}setupFilters(){document.querySelectorAll("[data-filter]").forEach((e=>{this.settings.addSetting(e),e.addEventListener("change",(e=>{this.filterTimeout&&clearTimeout(this.filterTimeout),this.filterTimeout=setTimeout((()=>{this.filterHandler(e)}),300)}))}));const e=document.querySelector('input[type="search"]');if(e){let t;e.addEventListener("input",(()=>{e.value.length>3?(clearTimeout(t),t=setTimeout((()=>{this.store.setFilter("search",e.value)}),300)):0===e.value.length&&this.store.removeFilter("search")}))}}destroy(){document.querySelectorAll("[data-filter]").forEach((e=>{e.removeEventListener("change",this.filterHandler)})),this.store.subscribers.clear()}}document.addEventListener("DOMContentLoaded",(()=>{let t=document.querySelector("[data-content]");t&&(window.crudManager=new e({content:t.dataset.content}))}))})();
\ No newline at end of file
+(()=>{class e{constructor(e){this.queue=window.jvbQueue,console.log(this.queue),this.config=e,this.content=e.content||!1,this.settings=window.jvbUserSettings,this.content&&(this.isTimeline=!1,this.initElements(),this.updateBulkOptions(),this.store=new window.jvbStore({name:this.content,storeName:this.content,endpoint:"content",headers:{action_nonce:jvbSettings.dash},indexes:[{name:"status",keyPath:"post_status"},{name:"modified",keyPath:"modified"}],filters:{content:this.content,user:jvbSettings.currentUser,page:1,status:"all"},TTL:36e5,cacheDOM:!0}),this.status="all",this.filterTimeout=null,this.viewController=new window.jvbViews(this.ui.container,this.store),this.formController=new window.jvbForm,this.formController.subscribe(((e,t)=>{switch(e){case"form-submit":case"form-autosave":this.handleFormChange(e,t)}})),window.jvbQueue&&window.jvbQueue.subscribe(((e,t)=>{"operation-completed"===e&&"form"===t.source?this.handleQueueSuccess(e,t):"operation-failed-permanent"===e&&"form"===t.source&&this.handleQueueFailure(e,t)})),this.initialized=!1,this.init())}handleFormChange(e,t){if(this.isTimeline){t.changes.content=this.content;let e={};return e[t.config.id.replace("edit-","")]=t.changes,void this.savePosts(e,`Saving ${t.fullData.post_title}'s Changes`)}t.changes.content=this.content;let s={},i="",o=[];switch(!0){case t.config.element===this.ui.forms.edit:let l=t.config.id.replace("edit-","");console.log(l),s[l]=t.changes,i=`Saving ${t.fullData.post_title} Changes`,t.changes.post_status&&this.shouldRemoveItem(t.changes.post_status)&&o.push(l);break;case t.config.element===this.ui.forms.bulkEdit:let n=t.config.element.querySelectorAll(".selected input:checked");n.forEach((e=>{s[e.value]=t.changes,t.changes.post_status&&this.shouldRemoveItem(t.changes.post_status)&&o.push(e.value)})),i=`Updating ${n.length} ${this.config.plural??"posts"} Changes`;break;case t.config.element===this.ui.forms.create:"form-submit"===e&&(s[t.config.data["form-id"]]=t.fullData,i=`Saving ${t.fullData.post_title} Changes`)}if(o.length>0){let e=0;o.forEach((t=>{setTimeout((()=>{const e=document.querySelector(`.item[data-id="${t}"]`);e&&window.fade(e,!1)}),e),e+=50})),t.config.element===this.ui.forms.bulkEdit&&setTimeout((()=>{this.viewController.clearSelection()}),e+100)}window.isEmptyObject(s)||this.savePosts(s,i)}shouldRemoveItem(e){return"all"===this.status&&!["publish","draft"].includes(e)||e!==this.status}savePosts(e,t){if(window.isEmptyObject(e))return;let s={endpoint:"content",headers:{action_nonce:jvbSettings.dash},data:{posts:e},popup:"Saving changes",title:t};this.queue.addToQueue(s)}handleQueueSuccess(e,t){console.log("Handling queue success..."),console.log("Event",e),console.log("Data",t)}handleQueueFailure(e,t){console.log("Handling queue failure..."),console.log("Event",e),console.log("Data",t)}initElements(){this.elements={modals:{create:"dialog.create",edit:"dialog.edit",bulkEdit:"dialog.bulkEdit"},container:".crud[data-content]",grid:".item-grid",bulkSelectActions:".bulk-action-select",forms:{create:"dialog.create form",edit:"dialog.edit form",bulkEdit:"dialog.bulkEdit form"},uploader:"details.uploader"},this.ui=window.uiFromSelectors(this.elements),this.isTimeline=Object.hasOwn(this.ui.forms.edit.dataset,"timeline")}init(){this.settings.addSetting(this.ui.uploader,"open"),this.ui.uploader.addEventListener("toggle",(e=>{console.log(e),console.log("Is Open: ",this.ui.uploader.open),console.log(this.ui.uploader.open?"on":"off"),this.settings.saveSetting("open",this.ui.uploader.open?"on":"off")})),this.filterHandler=this.handleFilterChange.bind(this),this.changeHandler=this.handleChange.bind(this),this.modals={};for(let[e,t]of Object.entries(this.ui.modals))this.modals[e]=new window.jvbModal(t),this.modals[e].subscribe(((t,s)=>{if("modal-close"===t)this.formController.cleanupForm(this.modals[e].modal.querySelector("form").dataset.formId),console.log("Data on modal close: ",s)}));this.setupEventDelegation(),this.setupFilters(),this.queue.subscribe(((e,t)=>{e})),this.initialized=!0}setupEventDelegation(){document.addEventListener("change",this.changeHandler),document.addEventListener("click",(e=>{const t=e.target.closest("[data-action]");if(t){e.preventDefault();const s=t.dataset.action,i=t.dataset.id;switch(s){case"edit":this.populateEditForm(i),this.modals.edit.handleOpen();break;case"delete":if(confirm("Delete this item?")){let e={};e[t.dataset.id]={post_status:"delete",content:this.content},window.fade(t.closest(".item"),!1),this.savePosts(e,`Sending ${this.singular} to trash...`),this.store.delete(i)}break;case"trash":let e={};e[t.dataset.id]={post_status:"trash",content:this.content},window.fade(t.closest(".item"),!1),this.savePosts(e,`Sending ${this.singular} to trash...`);break;case"create":this.modals.create.dataset.itemID="new",this.modals.create.dataset.content=this.content,this.modals.create.handleOpen();break;case"bulk-edit":Array.from(this.viewController.selectedItems).length>0&&this.modals.bulkEdit.handleOpen();break;case"bulk-delete":const s=Array.from(this.viewController.selectedItems);s.length>0&&confirm(`Delete ${s.length} items?`)&&(s.forEach((e=>this.store.delete(e))),this.viewController.clearSelection());break;case"sync":break;case"refresh":this.store.fetch()}}e.target.closest(".create-item")&&(this.formController.registerForm(this.ui.forms.create),this.modals.create.handleOpen()),e.target.closest(".cancel-bulk")&&this.viewController.selectAll(!1)})),document.addEventListener("keydown",(e=>{(e.ctrlKey||e.metaKey)&&"a"===e.key&&this.ui.container&&this.ui.container.contains(document.activeElement)&&(e.preventDefault(),this.viewController.selectAll()),"Escape"===e.key&&this.viewController?.selectedItems.size>0&&0===window.jvbModal.getAllModals().length&&this.viewController.clearSelection()}))}handleChange(e){if(e.target.classList.contains("bulk-action-select")){if(e.target.value.startsWith("tax-")){const t=e.target.value.replace("tax-","");return this.openTaxonomyModal(t),void(e.target.value="")}switch(e.target.value){case"edit":this.populateBulkEdit(),this.modals.bulkEdit.handleOpen();break;case"publish":this.setBulkStatus("publish");break;case"draft":case"restore":this.setBulkStatus("draft");break;case"trash":this.setBulkStatus("trash");break;case"delete":this.setBulkStatus("delete")}}}openTaxonomyModal(e){window.jvbSelector?window.jvbSelector.openForFilter(e,((e,t)=>this.handleBulkTaxonomy(e,t))):console.error("TaxonomySelector not initialized")}handleBulkTaxonomy(e,t){if(console.log(t,e),e.length>0){e=e.join(",");let s={},i=Array.from(this.viewController.selectedItems);console.log("selected",i),i.forEach((i=>{s[i]={content:this.content},s[i][t]=e})),console.log("Taxonomy changes: ",s);let o=`Adding ${i.length} ${this.config.plural??"posts"} to ${e.length} ${jvbSettings.labels[t].plural}`;this.viewController.clearSelection(),this.savePosts(s,o)}}setBulkStatus(e){if(!["publish","draft","trash","delete"].includes(e))return;console.log(`Setting status: ${e}`);let t,s={};for(let t of this.viewController.selectedItems)s[t]={post_status:e,content:this.content};if("delete"===e)t="Deleting";else t=window.uppercaseFirst(e)+"ing";if(console.log(this.status),"all"===this.status&&!["publish","draft"].includes(e)||e!==this.status){let e=0;for(let t of this.viewController.selectedItems)setTimeout((()=>{const e=document.querySelector(`.item[data-id="${t}"]`);e&&window.fade(e,!1)}),e),e+=50}this.viewController.clearSelection(),window.isEmptyObject(s)||this.savePosts(s,`${t} ${this.viewController.selectedItems.size} ${this.plural}...`)}handleFilterChange(e){let t=e.target,s=t.dataset.filter;if("taxonomies"===s){let e=t.dataset.taxonomy;this.store.setFilter(`tax_${e}`,s.value)}else this[t.dataset.filter]=t.value,this.store.setFilter(t.dataset.filter,t.value),"status"===t.dataset.filter&&this.updateBulkOptions(t.value)}updateBulkOptions(e="all"){if("trash"===e){if(this.ui.bulkSelectActions.querySelector('[value="edit"]')){window.removeChildren(this.ui.bulkSelectActions),window.getTemplate("trashOptions").querySelectorAll("option").forEach(((e,t)=>{0===t&&(e.checked=!0),this.ui.bulkSelectActions.append(e)}))}}else if(!this.ui.bulkSelectActions.querySelector('[value="edit"]')){window.removeChildren(this.ui.bulkSelectActions),window.getTemplate("notTrashOptions").querySelectorAll("option").forEach(((e,t)=>{this.ui.bulkSelectActions.append(e)}))}this.ui.bulkSelectActions.value=""}populateBulkEdit(){const e=this.modals.bulkEdit.modal.querySelector("form .selected");if(!e)return;window.removeChildren(e);for(let t of this.viewController.selectedItems){console.log(t);let s=this.store.get(t);console.log(s);const i=window.getTemplate("bulkItem");if(!i)return;const o=i.querySelector("input[type=checkbox]"),l=i.querySelector("img");o&&(o.id=`bulk_${s.id}`,o.value=s.id,o.checked=!0),l&&s.thumbnail&&(l.src=s.thumbnail,l.alt=s.alt||""),e.append(i)}let t=this.modals.bulkEdit.modal;[t.querySelector("h2 span").textContent]=[this.viewController.selectedItems.size],this.formController.registerForm(this.ui.forms.bulkEdit),console.log("Bulk Edit form registered")}populateEditForm(e){console.log(e);let t=this.store.get(parseInt(e));if(console.log(t),t){this.ui.modals.edit.dataset.itemID=e,this.ui.modals.edit.dataset.content=this.content;let s=this.ui.modals.edit.querySelector("form");[this.ui.modals.edit.querySelector("h2").textContent]=[`Editing ${t.fields.post_title}`],s.dataset.formId=`edit-${e}`,console.log(s.dataset.formId),new window.jvbPopulate(s,t.fields,t.images),this.formController.registerForm(this.ui.forms.edit),console.log("Edit form registered")}}setupFilters(){document.querySelectorAll("[data-filter]").forEach((e=>{this.settings.addSetting(e),e.addEventListener("change",(e=>{this.filterTimeout&&clearTimeout(this.filterTimeout),this.filterTimeout=setTimeout((()=>{this.filterHandler(e)}),300)}))}));const e=document.querySelector('input[type="search"]');if(e){let t;e.addEventListener("input",(()=>{e.value.length>3?(clearTimeout(t),t=setTimeout((()=>{this.store.setFilter("search",e.value)}),300)):0===e.value.length&&this.store.removeFilter("search")}))}}destroy(){document.querySelectorAll("[data-filter]").forEach((e=>{e.removeEventListener("change",this.filterHandler)})),this.store.subscribers.clear()}}document.addEventListener("DOMContentLoaded",(()=>{let t=document.querySelector("[data-content]");t&&(window.crudManager=new e({content:t.dataset.content}))}))})();
\ No newline at end of file
diff --git a/assets/js/min/favouritesManager.min.js b/assets/js/min/favouritesManager.min.js
index 45db7de..e52cc4e 100644
--- a/assets/js/min/favouritesManager.min.js
+++ b/assets/js/min/favouritesManager.min.js
@@ -1 +1 @@
-window.favouritesManager=class{constructor(){this.queue=window.jvbQueue,this.loadingManager=window.jvbLoading,this.cache=window.jvbCache,this.a11y=window.jvbA11y,this.error=window.jvbError,this.tabs=new window.jvbTabs(document.querySelector(".replace")),this.config={endpoints:{favourites:"favourites",lists:"favourites/lists",shares:"favourites/lists/shares"},selectors:{container:".favourites.container",itemsTab:'.tab-content[data-tab="items"]',listsTab:'.tab-content[data-tab="lists"]',grid:".item-grid",typeFilters:".type-filters",viewControls:".view-controls",bulkControls:".bulk-controls",selectAll:"#select-all",createListModal:".create-list-modal",addToListModal:".add-to-list-modal",shareListModal:".share-list-modal",noItems:".no-favourites",listContainer:".lists-container",listDetails:".list-details",loader:".favourites-loader"},defaultPage:1,defaultPerPage:24,defaultViewMode:"grid",refreshInterval:6e4,toastDuration:3e3},document.addEventListener("keydown",this.handleKeyDown.bind(this)),this.state={selectedItems:new Set,page:this.config.defaultPage,filter:{type:"all",order:"desc",orderBy:"date_added"},view:{mode:localStorage.getItem("favourites_view")||this.config.defaultViewMode,activeTab:"items"},pagination:{hasMore:!1,totalItems:0,totalPages:0},currentListId:null,loading:!1,initialized:!1},this.initDom(),this.initEvents(),this.loadInitialData(),this.state.initialized=!0}initDom(){this.container=document.querySelector(this.config.selectors.container),this.container?(this.grid=this.container.querySelector(this.config.selectors.grid),this.typeFilters=this.container.querySelector(this.config.selectors.typeFilters),this.viewControls=this.container.querySelector(this.config.selectors.viewControls),this.bulkControls=this.container.querySelector(this.config.selectors.bulkControls),this.listContainer=this.container.querySelector(this.config.selectors.listContainer),this.listDetails=this.container.querySelector(this.config.selectors.listDetails),this.loader=this.container.querySelector(this.config.selectors.loader),this.createListModal=document.querySelector(this.config.selectors.createListModal),this.addToListModal=document.querySelector(this.config.selectors.addToListModal),this.shareListModal=document.querySelector(this.config.selectors.shareListModal),this.grid&&this.state.view.mode&&this.grid.classList.add(`${this.state.view.mode}-view`)):console.warn("Favourites container not found")}initEvents(){if(this.typeFilters&&this.typeFilters.addEventListener("click",(t=>{const e=t.target.closest(".type-filter");e&&this.setFilterType(e.dataset.type)})),this.viewControls&&this.viewControls.addEventListener("click",(t=>{const e=t.target.closest(".view-toggle");e&&this.setView(e.dataset.view)})),this.container){const t=this.container.querySelector(this.config.selectors.selectAll);t&&t.addEventListener("change",(()=>{this.toggleSelectAll(t.checked)})),this.container.addEventListener("change",(t=>{t.target.matches(".item-select input[type=checkbox]")&&this.handleItemSelection(t.target)}));const e=this.container.querySelector(".bulk-action-select"),s=this.container.querySelector(".apply-bulk");e&&s&&s.addEventListener("click",(()=>{this.applyBulkAction(e.value)}));const i=this.container.querySelector(".cancel-bulk");i&&i.addEventListener("click",(()=>{this.clearSelection()}))}this.initModalEvents(),this.container.addEventListener("click",this.handleItemActions.bind(this)),this.grid&&this.setupInfiniteScroll()}initModalEvents(){if(this.createListModal){const t=this.createListModal.querySelector("form");t&&t.addEventListener("submit",(e=>{e.preventDefault(),this.handleCreateList(new FormData(t))}));const e=this.createListModal.querySelector(".cancel");e&&e.addEventListener("click",(()=>{this.createListModal.close()}))}if(this.addToListModal){const t=this.addToListModal.querySelector("form");t&&t.addEventListener("submit",(e=>{e.preventDefault(),this.handleAddToList(new FormData(t))}));const e=this.addToListModal.querySelector(".cancel");e&&e.addEventListener("click",(()=>{this.addToListModal.close()}))}if(this.shareListModal){const t=this.shareListModal.querySelector("form");t&&t.addEventListener("submit",(e=>{e.preventDefault(),this.handleShareList(new FormData(t))}));const e=this.shareListModal.querySelector(".cancel");e&&e.addEventListener("click",(()=>{this.shareListModal.close()}));const s=this.shareListModal.querySelector(".add-email");s&&s.addEventListener("click",(()=>{const e=this.shareListModal.querySelector("#share-email");e&&e.value&&this.handleShareList(new FormData(t))}))}}setupInfiniteScroll(){let t=this.container.querySelector(".scroll-sentinel");t||(t=document.createElement("div"),t.className="scroll-sentinel",t.setAttribute("aria-hidden","true"),this.grid.parentNode.appendChild(t)),new IntersectionObserver((t=>{t.forEach((t=>{t.isIntersecting&&this.state.pagination.hasMore&&!this.state.loading&&(this.state.page++,this.loadFavourites())}))}),{rootMargin:"200px"}).observe(t)}async loadInitialData(){this.loadingManager.show();try{await this.loadFavourites(),this.loadLists().catch((t=>{console.error("Error loading lists:",t)}))}catch(t){this.handleError(t,"loading initial data")}finally{this.loadingManager.hide()}}async loadFavourites(t=!0){if(!this.state.loading)try{this.state.loading=!0,this.loadingManager.show();const e=new URLSearchParams({page:this.state.page,per_page:this.config.defaultPerPage,type:"all"!==this.state.filter.type?this.state.filter.type:"",order:this.state.filter.order,orderby:this.state.filter.orderBy});t&&(this.state.page=1,removeChildren(this.grid),this.grid.classList.remove("empty"));const s=await this.cache.fetchWithCache(`${jvbSettings.api}${this.config.endpoints.favourites}?${e}`,{method:"GET",headers:{"X-WP-Nonce":jvbSettings.nonce,action_nonce:jvbSettings.favourites}},{context:"favouritesManager",forceRefresh:!0});return this.renderFavourites(s.favourites||[],this.state.page>1),s.counts&&this.updateTypeFilters(s.counts),s.pagination&&(this.state.pagination={hasMore:s.pagination.has_more,totalItems:s.pagination.total_items,totalPages:s.pagination.total_pages}),s}catch(t){throw this.handleError(t,"loading favourites"),t}finally{this.state.loading=!1,this.loadingManager.hide()}}renderFavourites(t,e=!1){this.grid&&(0!==t.length||e?(this.hideEmptyState(),e||removeChildren(this.grid),t.forEach((t=>{const e=this.createItemElement(t);this.grid.appendChild(e),this.initItemFunctionality(e,t)})),window.jvbA11y&&window.jvbA11y.announce(`${e?"Added":"Loaded"} ${t.length} favourites`)):this.showEmptyState())}createItemElement(t){const e=document.createElement("div");e.className=`item ${t.type} favourited`,e.dataset.id=t.target_id,e.dataset.type=t.type;const s=sanitizeHtml(t.title||!1),i=sanitizeHtml(t.notes||"");let a="";return t.thumbnail&&(a=`\n                <div class="item-thumbnail">\n                    <a href="${t.url}">${t.thumbnail}</a>\n                </div>\n            `),e.innerHTML=`\n            <div class="item-select">\n                <input type="checkbox"\n                   class="favourite-checkbox"\n                    id="select-${t.target_id}"\n                    value="${t.target_id}">\n                <label for="select-${t.target_id}"><span class="screen-reader-text">Select this ${t.type}</span</label>\n            </div>\n\n            <button type="button" class="favourite-button favourited"\n                onclick="toggleFavourite(this)"\n                data-id="${t.target_id}"\n                data-type="${t.type}"\n                title="Remove from favourites">\n                ${jvbSettings.icons["heart-filled"]}\n            </button>\n\n            ${a}\n\n            <div class="item-info">\n                ${s?`<h3><a href="${t.url}">${s}</a></h3>`:`<a href="${t.url}">View Item</a>`}\n\n                ${t.author?`\n                <div class="item-artist">\n                    <span>By ${t.author.name}</span>\n                </div>`:""}\n\n                ${t.taxonomies?.length?`\n                <div class="taxonomy-lists">\n                    ${t.taxonomies.map((t=>`\n                        <div class="taxonomy-group">\n                            ${jvbSettings.icons[t.icon]}\n                            <ul>\n                                ${t.terms.slice(0,3).map((t=>`\n                                    <li>\n                                        <a href="${t.url}" ${t.umami_click}>\n                                            ${t.title}\n                                        </a>\n                                    </li>\n                                `)).join("")}\n                            </ul>\n                        </div>\n                    `)).join("")}\n                </div>\n            `:""}\n\n                <div class="notes-section">\n                    <button type="button" class="toggle-notes" aria-expanded="false">\n                        ${jvbSettings.icons.note||"Notes"}\n                        <span>Notes</span>\n                    </button>\n\n                    <div class="notes-content" hidden>\n                        <textarea class="notes-input"\n                            placeholder="Add notes about this item..."\n                            data-id="${t.target_id}"\n                            data-type="${t.type}">${i}</textarea>\n                        <button type="button" class="save-notes">Save Notes</button>\n                    </div>\n                </div>\n            </div>\n        `,e}initItemFunctionality(t,e){const s=t.querySelector(".toggle-notes"),i=t.querySelector(".notes-content");s&&i&&s.addEventListener("click",(()=>{const t="true"===s.ariaExpanded;s.ariaExpanded=!t.toString(),i.hidden=t,t||i.querySelector("textarea")?.focus()}));const a=t.querySelector(".save-notes"),n=t.querySelector(".notes-input");a&&n&&(a.addEventListener("click",(()=>{this.saveNotes(n)})),n.addEventListener("keydown",(t=>{"Enter"===t.key&&(t.ctrlKey||t.metaKey)&&(t.preventDefault(),this.saveNotes(n))})))}saveNotes(t){if(!t)return;const e=t.value.trim(),s=t.dataset.id,i=t.dataset.type;s&&i&&(this.queue.addToQueue({type:"favourite_notes",data:{type:i,target_id:parseInt(s),notes:e}}),showToast("Notes saved"),this.a11y.announce("Notes saved"))}showEmptyState(t=!1){const e=this.container.querySelector(this.config.selectors.noItems)??this.createEmptyElement;e&&(e.hidden=!1),this.grid&&this.grid.classList.add("empty"),this.a11y.announce("No favourites to show!")}hideEmptyState(){const t=this.container.querySelector(".no-favourites");t&&t.remove(),this.grid&&this.grid.classList.remove("empty")}createEmptyElement(t=!1){const e=document.createElement("div");e.className="no-favourites",e.innerHTML="\n            <h3>♡ BLANK CANVAS ♡</h3>\n            <p>You haven't fallen in love with any pieces... yet!</p>\n            <p>Hit that heart icon when something stops your scroll.</p>\n            <p>Your dream collection is waiting to start.</p>\n        ",this.grid.after(e)}showEmptyListState(t=!1){const e=document.createElement("div");e.className="no-favourites",e.innerHTML="\n            <h3>♡ FULL OF POSSIBILITY ♡</h3>\n            <p>There's nothing in this list yet.</p>\n            <p>Add some gap fillers from the main favourites tab.</p>\n        ",this.grid.after(e),this.grid.classList.add("empty"),this.a11y.announce("No favourites to show!")}async loadMoreItems(){!this.state.loading&&this.state.pagination.hasMore&&(this.state.page+=1,await this.loadFavourites())}updateTypeFilters(t){this.typeFilters&&this.typeFilters.querySelectorAll(".type-filter").forEach((e=>{const s=e.querySelector(".count");if(!s)return;const i=e.dataset.type;if("all"===i){const e=Object.values(t).reduce(((t,e)=>t+(parseInt(e)||0)),0);s.textContent=`(${e})`}else s.textContent=`(${t[i]||0})`}))}setFilterType(t){t!==this.state.filter.type&&(this.typeFilters&&this.typeFilters.querySelectorAll(".type-filter").forEach((e=>{e.classList.toggle("active",e.dataset.type===t),e.setAttribute("aria-selected",e.dataset.type===t)})),this.state.filter.type=t,this.state.page=1,this.loadFavourites(),window.jvbA11y&&window.jvbA11y.announce(`Filtered to show ${"all"===t?"all":t} items`))}setView(t){t!==this.state.view.mode&&(this.viewControls&&this.viewControls.querySelectorAll(".view-toggle").forEach((e=>{const s=e.dataset.view===t;e.setAttribute("aria-pressed",s)})),this.grid&&(this.grid.classList.remove("grid-view","list-view"),this.grid.classList.add(`${t}-view`)),this.state.view.mode=t,localStorage.setItem("favourites_view",t),window.jvbA11y&&window.jvbA11y.announce(`Changed to ${t} view`))}toggleSelectAll(t){const e=this.getVisibleItems();e.forEach((e=>{const s=e.querySelector('.item-select input[type="checkbox"]');s&&(s.checked=t,this.toggleItemSelection(s.value,t))})),this.updateBulkControls(),window.jvbA11y&&window.jvbA11y.announce(t?`Selected all ${e.length} items`:"Deselected all items")}getVisibleItems(){return this.grid?Array.from(this.grid.querySelectorAll(".item:not([hidden])")):[]}toggleItemSelection(t,e){e?this.state.selectedItems.add(t):this.state.selectedItems.delete(t);const s=this.grid.querySelector(`.item[data-id="${t}"]`);s&&s.classList.toggle("selected",e)}handleItemSelection(t){const e=t.checked,s=t.value;if(this.toggleItemSelection(s,e),this.updateBulkControls(),this.updateSelectAllState(),window.jvbA11y){const s=t.closest(".item"),i=s&&s.querySelector("h3")?.textContent||"item";window.jvbA11y.announce(e?`Selected ${i}`:`Deselected ${i}`)}}updateSelectAllState(){const t=this.container.querySelector(this.config.selectors.selectAll);if(!t)return;const e=this.getVisibleItems();if(0===e.length)return t.checked=!1,void(t.indeterminate=!1);const s=e.filter((t=>{const e=t.querySelector('.item-select input[type="checkbox"]');return e&&e.checked})).length;0===s?(t.checked=!1,t.indeterminate=!1):s===e.length?(t.checked=!0,t.indeterminate=!1):(t.checked=!1,t.indeterminate=!0)}updateBulkControls(){if(!this.bulkControls)return;const t=this.bulkControls.querySelector(".bulk-actions");if(!t)return;const e=this.state.selectedItems.size>0;t.hidden=!e;const s=this.bulkControls.querySelector(".selected-count");s&&(s.textContent=e?`${this.state.selectedItems.size} selected`:"")}handleKeyDown(t){"Escape"===t.key&&this.state.selectedItems.size>0&&(t.preventDefault(),this.clearSelection(),window.jvbA11y&&window.jvbA11y.announce("Selection cleared using Escape key"))}clearSelection(){this.state.selectedItems.clear(),this.getVisibleItems().forEach((t=>{const e=t.querySelector('.item-select input[type="checkbox"]');e&&(e.checked=!1),t.classList.remove("selected")}));const t=this.container.querySelector(this.config.selectors.selectAll);t&&(t.checked=!1,t.indeterminate=!1),this.updateBulkControls(),window.jvbA11y&&window.jvbA11y.announce("Selection cleared")}applyBulkAction(t){if(!t||0===this.state.selectedItems.size)return;switch(t){case"unfavourite":this.bulkUnfavourite();break;case"add-to-list":this.showAddToListModal();break;case"create-list":this.showCreateListModal();break;case"add-notes":this.showBulkNotesModal()}const e=this.container.querySelector(".bulk-action-select");e&&(e.value="")}handleItemActions(t){if(t.target.closest(".toggle-notes")){const e=t.target.closest(".toggle-notes"),s="true"===e.getAttribute("aria-expanded"),i=e.closest(".notes-section").querySelector(".notes-content");return e.setAttribute("aria-expanded",!s),i.hidden=s,!s&&i&&i.querySelector("textarea")?.focus(),void t.preventDefault()}if(t.target.closest(".save-notes")){const e=t.target.closest(".save-notes").closest(".notes-content").querySelector("textarea");return e&&this.saveNotes(e),void t.preventDefault()}if(t.target.closest(".view-list")){const e=t.target.closest(".view-list").closest(".list-card");return e&&e.dataset.id&&this.viewList(e.dataset.id),void t.preventDefault()}if(t.target.closest(".share-list")){const e=t.target.closest(".share-list").closest(".list-card");return e&&e.dataset.id&&this.showShareModal(e.dataset.id),void t.preventDefault()}if(t.target.closest(".delete-list")){const e=t.target.closest(".delete-list").closest(".list-card");return e&&e.dataset.id&&this.deleteList(e.dataset.id),void t.preventDefault()}if(t.target.closest(".back-to-lists"))return this.exitListView(),void t.preventDefault()}async loadLists(t=!0){try{this.state.loading=!0,this.loadingManager.show("Loading lists...");const t=await this.cache.fetchWithCache(`${jvbSettings.api}${this.config.endpoints.lists}`,{method:"GET",headers:{"X-WP-Nonce":jvbSettings.nonce,action_nonce:jvbSettings.favourites}},{context:"favourite-lists",forceRefresh:!1});return t.lists&&this.renderLists(t.lists),t}catch(t){throw this.handleError(t,"loading lists"),t}finally{this.state.loading=!1,this.loadingManager.hide()}}renderLists(t){if(!this.listContainer)return;if(removeChildren(this.listContainer),!t||0===t.length)return void(this.listContainer.innerHTML='\n                <div class="no-lists">\n                    <h3>No Lists Yet</h3>\n                    <p>Select favourites from the main tab to organize into lists.</p>\n                </div>\n            ');const e=t.owned,s=t.shared;if(e.length>0){const t=document.createElement("details");t.className="lists-section owned-lists",t.open=!0,t.innerHTML="<summary>Your Lists:</summary>",e.forEach((e=>{const s=this.createListCard(e);t.appendChild(s)})),this.listContainer.appendChild(t)}if(s.length>0){const t=document.createElement("details");t.className="lists-section shared-lists",t.innerHTML="<summary>Lists Shared with You:</summary>",s.forEach((e=>{const s=this.createListCard(e);t.appendChild(s)})),this.listContainer.appendChild(t)}}createListCard(t){const e=document.createElement("div");e.className="list-card",e.dataset.id=t.id;const s="1"===t.is_shared;s&&e.classList.add("shared"),t.is_temp&&e.classList.add("temp"),t.is_owner;const i=sanitizeHtml(t.name||"Untitled List"),a=sanitizeHtml(t.description||"");return e.innerHTML=`\n            <div class="list-header">\n                <h3>${i}</h3>\n                <div class="list-actions">\n                    <button type="button" class="view-list" title="View List">\n                        ${jvbSettings.icons?.show||"View"}\n                    </button>\n                    ${s?"":`\n                        <button type="button" class="share-list" title="Share List">\n                            ${jvbSettings.icons?.share||"Share"}\n                        </button>\n                        <button type="button" class="delete-list" title="Delete List">\n                            ${jvbSettings.icons?.delete||"Delete"}\n                        </button>\n                    `}\n                </div>\n            </div>\n\n            ${a?`<p class="list-description">${a}</p>`:""}\n\n            <div class="list-meta">\n                <div class="meta-stats">\n                    <span class="item-count">${t.item_count||0} items</span>\n                    <span class="date">${formatDate(t.created_at)}</span>\n                </div>\n\n\n                ${s?`\n                    <div class="owner-info">\n                        Shared by ${t.owner_name||"another user"}\n                    </div>\n                `:t.share_count>0?`\n                    <div class="share-info">\n                        Shared with ${t.share_count} ${1===t.share_count?"person":"people"}\n                    </div>\n                `:""}\n            </div>\n        `,e}async viewList(t){try{this.state.loading=!0,this.loadingManager.show("Loading list..."),this.state.currentListId=t;const e=await this.cache.fetchWithCache(`${jvbSettings.api}${this.config.endpoints.lists}?id=${t}`,{method:"GET",headers:{"X-WP-Nonce":jvbSettings.nonce,action_nonce:jvbSettings.favourites}},{context:"list-item",forceRefresh:!1});if(!e.list)throw new Error("List not found");this.showListDetails(e.list)}catch(t){this.handleError(t,"viewing list")}finally{this.state.loading=!1,this.loadingManager.hide()}}showListDetails(t){this.listDetails&&this.listContainer&&(console.log(t),this.listDetails.querySelector(".list-title").value=t.name||"Untitled List",this.listDetails.querySelector(".list-description").value=t.description||"",t.is_owner?this.listDetails.querySelector(".list-actions")||this.createListActions():this.listDetails.querySelector(".list-actions")?.remove(),removeChildren(this.grid),this.renderFavourites(t.items||[],!1),0===t.items.length&&this.showEmptyListState(),window.jvbA11y&&window.jvbA11y.announce(`Viewing list: ${t.name} with ${t.items?.length||0} items`))}createListActions(){const t=document.createElement("div");t.className="list-actions",t.innerHTML='\n            <button type="button" class="share-list" title="Share List">\n            <?= $icon->getIcon(\'share\')?>\n            <span>Share</span>\n        </button>\n        <button type="button" class="duplicate-list" title="Duplicate List">\n            <?= $icon->getIcon(\'copy\')?>\n            <span>Duplicate</span>\n        </button>\n        <button type="button" class="delete-list" title="Delete List">\n            <?= $icon->getIcon(\'delete\')?>\n            <span>Delete</span>\n        </button>\n        ',this.listDetails.insertBefore(t,this.listDetails.querySelector(".bulk-controls"))}exitListView(){this.listDetails&&this.listContainer&&(this.listDetails.hidden=!0,this.listContainer.hidden=!1,this.container.classList.remove("viewing-list"),this.state.currentListId=null,window.jvbA11y&&window.jvbA11y.announce("Returned to lists view"))}showCreateListModal(){this.createListModal&&(this.createListModal.querySelector("form")?.reset(),this.createListModal.showModal(),setTimeout((()=>{this.createListModal.querySelector("#list-name")?.focus()}),100),window.jvbA11y&&window.jvbA11y.announce("Create list dialog opened"))}showAddToListModal(){this.addToListModal&&(this.populateAddToListModal(),this.addToListModal.showModal(),window.jvbA11y&&window.jvbA11y.announce("Add to list dialog opened"))}async populateAddToListModal(){if(!this.addToListModal)return;const t=this.addToListModal.querySelector(".lists-options");if(t){removeChildren(t);try{const e=(await this.loadLists()).lists.owned;if(0===e.length)return t.innerHTML='\n                    <div class="no-lists">\n                        <p>You don\'t have any lists yet.</p>\n                        <button type="button" class="create-list-button">Create a list</button>\n                    </div>\n                ',void t.querySelector(".create-list-button")?.addEventListener("click",(()=>{this.addToListModal.close(),this.showCreateListModal()}));e.forEach((e=>{const s=document.createElement("div");s.className="list-option",s.innerHTML=`\n                    <input type="checkbox" id="${e.id}" name="list_ids[]" value="${e.id}">\n                    <label for="${e.id}">\n\n                        <span class="list-name">${sanitizeHtml(e.name)}</span>\n                        <span class="item-count">( ${e.item_count||0} items )</span>\n                    </label>\n                `,t.appendChild(s)}))}catch(e){t.innerHTML='\n                <div class="error-message">\n                    <p>Error loading lists. Please try again.</p>\n                </div>\n            ',console.error("Error loading lists for modal:",e)}}}async handleCreateList(t){const e=t.get("list_name"),s=t.get("list_description");if(e)try{this.showLoader("Creating list...");const t=[];this.state.selectedItems.forEach((e=>{const s=this.grid.querySelector(`.item[data-id="${e}"]`);s&&t.push({type:s.dataset.type,target_id:parseInt(e)})})),this.queue.addToQueue({type:"favourite_list_create",data:{name:e,description:s,items:t}}),showToast(`List "${e}" created`),this.a11y.announce(`List ${e} created with ${t.length} items`),this.createListModal.close(),this.clearSelection(),this.switchTab("lists")}catch(t){this.handleError(t,"creating list")}finally{this.hideLoader()}else showToast("Please enter a list name","error")}async handleAddToList(t){const e=t.getAll("list_ids[]");if(e.length)try{this.showLoader("Adding to list...");const t=[];this.state.selectedItems.forEach((e=>{const s=this.grid.querySelector(`.item[data-id="${e}"]`);s&&t.push({type:s.dataset.type,target_id:parseInt(e)})})),this.queue.addToQueue({type:"favourite_list_add",data:{list_id:e.join(","),items:t}}),showToast(`Added to ${e.length} ${1===e.length?"list":"lists"}`),this.a11y.announce(`Added ${t.length} items to ${e.length} ${1===e.length?"list":"lists"}`),this.addToListModal.close(),this.clearSelection()}catch(t){this.handleError(t,"adding to list")}finally{this.hideLoader()}else showToast("Please select at least one list","error")}async handleRemoveFromList(t){const e=t.getAll("list_ids[]");if(e.length)try{this.showLoader("Removing from list...");const t=[];this.state.selectedItems.forEach((e=>{const s=this.grid.querySelector(`.item[data-id="${e}"]`);s&&t.push({type:s.dataset.type,target_id:parseInt(e)})})),this.queue.addToQueue({type:"favourite_list_remove",data:{list_id:e.join(","),items:t}}),showToast(`Removed from ${e.length} ${1===e.length?"list":"lists"}`),this.a11y.announce(`Removed ${t.length} items to ${e.length} ${1===e.length?"list":"lists"}`),this.addToListModal.close(),this.clearSelection()}catch(t){this.handleError(t,"remove from list")}finally{this.hideLoader()}else showToast("Please select at least one list","error")}showShareModal(t){this.shareListModal&&(this.state.currentListId=t,this.shareListModal.querySelector("form")?.reset(),this.loadSharedUsers(t),this.shareListModal.showModal(),setTimeout((()=>{this.shareListModal.querySelector("#share-email")?.focus()}),100),window.jvbA11y&&window.jvbA11y.announce("Share list dialog opened"))}async loadSharedUsers(t){try{const e=this.shareListModal.querySelector(".shared-users");if(!e)return;e.innerHTML='<div class="loading">Loading shared users...</div>';const s=await this.cache.fetchWithCache(`${jvbSettings.api}${this.config.endpoints.lists}?id=${t}`,{method:"GET",headers:{"X-WP-Nonce":jvbSettings.nonce,action_nonce:jvbSettings.favourites}},{context:"list-item",forceRefresh:!1});removeChildren(e),s.list&&s.list.shared_users&&s.list.shared_users.length>0?(s.list.shared_users.forEach((t=>{const s=document.createElement("div");s.className=`shared-user ${t.status}`,s.innerHTML=`\n                        <span class="user-email">${t.email}</span>\n                        ${"pending"===t.status?'<span class="pending-badge">Invitation sent</span>':`<span class="permission-badge">${t.permission_type||"view"}</span>`}\n                        <button type="button" class="remove-share" data-email="${t.email}">\n                            ${jvbSettings.icons?.delete||"Remove"}\n                        </button>\n                    `,e.appendChild(s)})),e.querySelectorAll(".remove-share").forEach((t=>{t.addEventListener("click",(()=>{this.unshareList(t.dataset.email)}))}))):e.innerHTML='<div class="no-shares">This list is not shared with anyone yet.</div>'}catch(t){console.error("Error loading shared users:",t)}}async unshareList(t){if(confirm(`Remove ${t}'s access to this list?`))if(this.state.currentListId)try{this.showLoader("Removing access..."),this.queue.addToQueue({type:"favourite_list_unshare",data:{list_id:parseInt(this.state.currentListId),email:t}});const e=Array.from(this.shareListModal.querySelectorAll(".shared-user")).find((e=>e.querySelector(".user-email")?.textContent===t));e&&(e.classList.add("removing"),setTimeout((()=>{if(e.remove(),0===this.shareListModal.querySelectorAll(".shared-user").length){const t=this.shareListModal.querySelector(".shared-users");t&&(t.innerHTML='<div class="no-shares">This list is not shared with anyone yet.</div>')}}),300)),showToast(`Removed ${t}'s access`),this.a11y.announce(`Removed ${t}'s access to list`)}catch(t){this.handleError(t,"removing share access")}finally{this.hideLoader()}else showToast("No list selected","error")}async deleteList(t){if(confirm("Are you sure you want to delete this list? This cannot be undone."))try{this.showLoader("Deleting list..."),this.queue.addToQueue({type:"favourite_list_delete",data:{list_id:parseInt(t)}});const e=this.container.querySelector(`.list-card[data-id="${t}"]`);e&&(e.classList.add("removing"),setTimeout((()=>{e.remove(),0===this.container.querySelectorAll(".list-card").length&&(this.listContainer.innerHTML='\n                                <div class="no-lists">\n                                    <h3>No Lists Yet</h3>\n                                    <p>Create your first list to organize your favourites!</p>\n                                </div>\n                            ')}),300)),showToast("List deleted"),this.a11y.announce("List deleted")}catch(t){this.handleError(t,"deleting list")}finally{this.hideLoader()}}showBulkNotesModal(){let t=document.querySelector(".bulk-notes-modal");t||(t=document.createElement("dialog"),t.className="bulk-notes-modal",t.innerHTML='\n                <form method="dialog" data-save="favourites">\n                    <h2>Add Notes to Selected Items</h2>\n\n                    <div class="field">\n                        <label for="bulk-notes">Notes (will be applied to all selected items)</label>\n                        <textarea id="bulk-notes" name="bulk_notes" rows="5"></textarea>\n                    </div>\n\n                    <div class="actions">\n                        <button type="button" class="cancel">Cancel</button>\n                        <button type="submit" class="save">Save Notes</button>\n                    </div>\n                </form>\n            ',document.body.appendChild(t),t.querySelector("form").addEventListener("submit",(e=>{e.preventDefault();const s=t.querySelector("#bulk-notes").value;this.saveBulkNotes(s),t.close()})),t.querySelector(".cancel").addEventListener("click",(()=>{t.close()}))),t.querySelector("form")?.reset(),t.showModal(),setTimeout((()=>{t.querySelector("#bulk-notes")?.focus()}),100),window.jvbA11y&&window.jvbA11y.announce("Add notes dialog opened")}saveBulkNotes(t){if(t)try{this.showLoader("Saving notes...");let e=Array.from(this.state.selectedItems.values());this.queue.addToQueue({type:"favourite_notes",data:{target_id:e.join(","),notes:t}}),showToast(`Notes saved for ${e.length} items`),this.a11y.announce(`Notes saved for ${e.length} items`),this.clearSelection()}catch(t){this.handleError(t,"saving bulk notes")}finally{this.hideLoader()}}async bulkUnfavourite(){if(confirm("Are you sure you want to remove these items from your favourites?"))try{this.showLoader("Removing from favourites...");const t=[];this.state.selectedItems.forEach((e=>{const s=this.grid.querySelector(`.item[data-id="${e}"]`);if(!s)return;const i=s.dataset.type;t.push({target_id:parseInt(e),type:i,action:"remove"})})),this.queue.addToQueue({type:"favourite_toggle",data:t});const e=[];this.state.selectedItems.forEach((t=>{const s=this.grid.querySelector(`.item[data-id="${t}"]`);if(!s)return;s.style.opacity="0",s.style.transform="scale(0.9)",s.style.transition="opacity 0.3s ease, transform 0.3s ease";const i=new Promise((t=>{setTimeout((()=>{s.remove(),t()}),300)}));e.push(i)})),await Promise.all(e),0===this.grid.children.length&&this.showEmptyState(),this.clearSelection(),showToast(`Removed ${t.length} items from favourites`),this.a11y.announce(`Removed ${t.length} items from favourites`)}catch(t){this.handleError(t,"removing favourites")}finally{this.hideLoader()}}async handleShareList(t){const e=t.get("share_email");if(e)if(this.validateEmail(e))try{this.showLoader("Sharing list..."),this.queue.addToQueue({type:"favourite_list_share",data:{list_id:parseInt(this.state.currentListId),email:e,permission_type:"view"}}),this.shareListModal.querySelector("#share-email").value="",this.loadSharedUsers(this.state.currentListId),showToast(`Invitation sent to ${e}`),this.a11y.announce(`Invitation sent to ${e}`)}catch(t){this.handleError(t,"sharing list")}finally{this.hideLoader()}else showToast("Please enter a valid email address","error");else showToast("Please enter an email address","error")}showLoader(t="Loading..."){if(!this.loader)return;const e=this.loader.querySelector(".loader-message");e&&(e.textContent=t),this.loader.hidden=!1}hideLoader(){this.loader&&(this.loader.hidden=!0)}showToast(t,e){window.jvbNotifications.showToast(t,e)}handleError(t,e){console.error(`Favourites error (${e}):`,t),showToast(`Error ${e}: ${t.message||"Something went wrong"}`,"error"),window.jvbError&&window.jvbError.log(t,{component:"FavouritesManager",action:e}),window.jvbA11y&&window.jvbA11y.announce(`Error ${e}. ${t.message||"Please try again."}`)}validateEmail(t){return/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(t)}};
\ No newline at end of file
+window.favouritesManager=class{constructor(){this.queue=window.jvbQueue,this.loadingManager=window.jvbLoading,this.cache=window.jvbCache,this.a11y=window.jvbA11y,this.error=window.jvbError,this.tabs=new window.jvbTabs(document.querySelector(".replace")),this.config={endpoints:{favourites:"favourites",lists:"favourites/lists",shares:"favourites/lists/shares"},selectors:{container:".favourites.container",itemsTab:'.tab-content[data-tab="items"]',listsTab:'.tab-content[data-tab="lists"]',grid:".item-grid",typeFilters:".type-filters",viewControls:".view-controls",bulkControls:".bulk-controls",selectAll:"#select-all",createListModal:".create-list-modal",addToListModal:".add-to-list-modal",shareListModal:".share-list-modal",noItems:".no-favourites",listContainer:".lists-container",listDetails:".list-details",loader:".favourites-loader"},defaultPage:1,defaultPerPage:24,defaultViewMode:"grid",refreshInterval:6e4,toastDuration:3e3},document.addEventListener("keydown",this.handleKeyDown.bind(this)),this.state={selectedItems:new Set,page:this.config.defaultPage,filter:{type:"all",order:"desc",orderBy:"date_added"},view:{mode:localStorage.getItem("favourites_view")||this.config.defaultViewMode,activeTab:"items"},pagination:{hasMore:!1,totalItems:0,totalPages:0},currentListId:null,loading:!1,initialized:!1},this.initDom(),this.initEvents(),this.loadInitialData(),this.state.initialized=!0}initDom(){this.container=document.querySelector(this.config.selectors.container),this.container?(this.grid=this.container.querySelector(this.config.selectors.grid),this.typeFilters=this.container.querySelector(this.config.selectors.typeFilters),this.viewControls=this.container.querySelector(this.config.selectors.viewControls),this.bulkControls=this.container.querySelector(this.config.selectors.bulkControls),this.listContainer=this.container.querySelector(this.config.selectors.listContainer),this.listDetails=this.container.querySelector(this.config.selectors.listDetails),this.loader=this.container.querySelector(this.config.selectors.loader),this.createListModal=document.querySelector(this.config.selectors.createListModal),this.addToListModal=document.querySelector(this.config.selectors.addToListModal),this.shareListModal=document.querySelector(this.config.selectors.shareListModal),this.grid&&this.state.view.mode&&this.grid.classList.add(`${this.state.view.mode}-view`)):console.warn("Favourites container not found")}initEvents(){if(this.typeFilters&&this.typeFilters.addEventListener("click",(t=>{const e=t.target.closest(".type-filter");e&&this.setFilterType(e.dataset.type)})),this.viewControls&&this.viewControls.addEventListener("click",(t=>{const e=t.target.closest(".view-toggle");e&&this.setView(e.dataset.view)})),this.container){const t=this.container.querySelector(this.config.selectors.selectAll);t&&t.addEventListener("change",(()=>{this.toggleSelectAll(t.checked)})),this.container.addEventListener("change",(t=>{t.target.matches(".item-select input[type=checkbox]")&&this.handleItemSelection(t.target)}));const e=this.container.querySelector(".bulk-action-select"),s=this.container.querySelector(".apply-bulk");e&&s&&s.addEventListener("click",(()=>{this.applyBulkAction(e.value)}));const i=this.container.querySelector(".cancel-bulk");i&&i.addEventListener("click",(()=>{this.clearSelection()}))}this.initModalEvents(),this.container.addEventListener("click",this.handleItemActions.bind(this)),this.grid&&this.setupInfiniteScroll()}initModalEvents(){if(this.createListModal){const t=this.createListModal.querySelector("form");t&&t.addEventListener("submit",(e=>{e.preventDefault(),this.handleCreateList(new FormData(t))}));const e=this.createListModal.querySelector(".cancel");e&&e.addEventListener("click",(()=>{this.createListModal.close()}))}if(this.addToListModal){const t=this.addToListModal.querySelector("form");t&&t.addEventListener("submit",(e=>{e.preventDefault(),this.handleAddToList(new FormData(t))}));const e=this.addToListModal.querySelector(".cancel");e&&e.addEventListener("click",(()=>{this.addToListModal.close()}))}if(this.shareListModal){const t=this.shareListModal.querySelector("form");t&&t.addEventListener("submit",(e=>{e.preventDefault(),this.handleShareList(new FormData(t))}));const e=this.shareListModal.querySelector(".cancel");e&&e.addEventListener("click",(()=>{this.shareListModal.close()}));const s=this.shareListModal.querySelector(".add-email");s&&s.addEventListener("click",(()=>{const e=this.shareListModal.querySelector("#share-email");e&&e.value&&this.handleShareList(new FormData(t))}))}}setupInfiniteScroll(){let t=this.container.querySelector(".scroll-sentinel");t||(t=document.createElement("div"),t.className="scroll-sentinel",t.setAttribute("aria-hidden","true"),this.grid.parentNode.appendChild(t)),new IntersectionObserver((t=>{t.forEach((t=>{t.isIntersecting&&this.state.pagination.hasMore&&!this.state.loading&&(this.state.page++,this.loadFavourites())}))}),{rootMargin:"200px"}).observe(t)}async loadInitialData(){this.loadingManager.show();try{await this.loadFavourites(),this.loadLists().catch((t=>{console.error("Error loading lists:",t)}))}catch(t){this.handleError(t,"loading initial data")}finally{this.loadingManager.hide()}}async loadFavourites(t=!0){if(!this.state.loading)try{this.state.loading=!0,this.loadingManager.show();const e=new URLSearchParams({page:this.state.page,per_page:this.config.defaultPerPage,type:"all"!==this.state.filter.type?this.state.filter.type:"",order:this.state.filter.order,orderby:this.state.filter.orderBy});t&&(this.state.page=1,removeChildren(this.grid),this.grid.classList.remove("empty"));const s=await this.cache.fetchWithCache(`${jvbSettings.api}${this.config.endpoints.favourites}?${e}`,{method:"GET",headers:{"X-WP-Nonce":jvbSettings.nonce,action_nonce:jvbSettings.favourites}},{context:"favouritesManager",forceRefresh:!0});return this.renderFavourites(s.favourites||[],this.state.page>1),s.counts&&this.updateTypeFilters(s.counts),s.pagination&&(this.state.pagination={hasMore:s.pagination.has_more,totalItems:s.pagination.total_items,totalPages:s.pagination.total_pages}),s}catch(t){throw this.handleError(t,"loading favourites"),t}finally{this.state.loading=!1,this.loadingManager.hide()}}renderFavourites(t,e=!1){this.grid&&(0!==t.length||e?(this.hideEmptyState(),e||removeChildren(this.grid),t.forEach((t=>{const e=this.createItemElement(t);this.grid.appendChild(e),this.initItemFunctionality(e,t)})),window.jvbA11y&&window.jvbA11y.announce(`${e?"Added":"Loaded"} ${t.length} favourites`)):this.showEmptyState())}createItemElement(t){const e=document.createElement("div");e.className=`item ${t.type} favourited`,e.dataset.id=t.target_id,e.dataset.type=t.type;const s=sanitizeHtml(t.title||!1),i=sanitizeHtml(t.notes||"");let a="";return t.thumbnail&&(a=`\n                <div class="item-thumbnail">\n                    <a href="${t.url}">${t.thumbnail}</a>\n                </div>\n            `),e.innerHTML=`\n            <div class="item-select">\n                <input type="checkbox"\n                   class="favourite-checkbox"\n                    id="select-${t.target_id}"\n                    value="${t.target_id}">\n                <label for="select-${t.target_id}"><span class="screen-reader-text">Select this ${t.type}</span</label>\n            </div>\n\n            <button type="button" class="favourite-button favourited"\n                onclick="toggleFavourite(this)"\n                data-id="${t.target_id}"\n                data-type="${t.type}"\n                title="Remove from favourites">\n                ${jvbSettings.icons["heart-filled"]}\n            </button>\n\n            ${a}\n\n            <div class="item-info">\n                ${s?`<h3><a href="${t.url}">${s}</a></h3>`:`<a href="${t.url}">View Item</a>`}\n\n                ${t.author?`\n                <div class="item-artist">\n                    <span>By ${t.author.name}</span>\n                </div>`:""}\n\n                ${t.taxonomies?.length?`\n                <div class="taxonomy-lists">\n                    ${t.taxonomies.map((t=>`\n                        <div class="taxonomy-group">\n                            ${jvbSettings.icons[t.icon]}\n                            <ul>\n                                ${t.terms.slice(0,3).map((t=>`\n                                    <li>\n                                        <a href="${t.url}" ${t.umami_click}>\n                                            ${t.title}\n                                        </a>\n                                    </li>\n                                `)).join("")}\n                            </ul>\n                        </div>\n                    `)).join("")}\n                </div>\n            `:""}\n\n                <div class="notes-section">\n                    <button type="button" class="toggle-notes" aria-expanded="false">\n                        ${jvbSettings.icons.note||"Notes"}\n                        <span>Notes</span>\n                    </button>\n\n                    <div class="notes-content" hidden>\n                        <textarea class="notes-input"\n                            placeholder="Add notes about this item..."\n                            data-id="${t.target_id}"\n                            data-type="${t.type}">${i}</textarea>\n                        <button type="button" class="save-notes">Save Notes</button>\n                    </div>\n                </div>\n            </div>\n        `,e}initItemFunctionality(t,e){const s=t.querySelector(".toggle-notes"),i=t.querySelector(".notes-content");s&&i&&s.addEventListener("click",(()=>{const t="true"===s.ariaExpanded;s.ariaExpanded=!t.toString(),i.hidden=t,t||i.querySelector("textarea")?.focus()}));const a=t.querySelector(".save-notes"),n=t.querySelector(".notes-input");a&&n&&(a.addEventListener("click",(()=>{this.saveNotes(n)})),n.addEventListener("keydown",(t=>{"Enter"===t.key&&(t.ctrlKey||t.metaKey)&&(t.preventDefault(),this.saveNotes(n))})))}saveNotes(t){if(!t)return;const e=t.value.trim(),s=t.dataset.id,i=t.dataset.type;s&&i&&(this.queue.addToQueue({type:"favourite_notes",data:{type:i,target_id:parseInt(s),notes:e}}),showToast("Notes saved"),this.a11y.announce("Notes saved"))}showEmptyState(t=!1){const e=this.container.querySelector(this.config.selectors.noItems)??this.createEmptyElement;e&&(e.hidden=!1),this.grid&&this.grid.classList.add("empty"),this.a11y.announce("No favourites to show!")}hideEmptyState(){const t=this.container.querySelector(".no-favourites");t&&t.remove(),this.grid&&this.grid.classList.remove("empty")}createEmptyElement(t=!1){const e=document.createElement("div");e.className="no-favourites",e.innerHTML="\n            <h3>♡ BLANK CANVAS ♡</h3>\n            <p>You haven't fallen in love with any pieces... yet!</p>\n            <p>Hit that heart icon when something stops your scroll.</p>\n            <p>Your dream collection is waiting to start.</p>\n        ",this.grid.after(e)}showEmptyListState(t=!1){const e=document.createElement("div");e.className="no-favourites",e.innerHTML="\n            <h3>♡ FULL OF POSSIBILITY ♡</h3>\n            <p>There's nothing in this list yet.</p>\n            <p>Add some gap fillers from the main favourites tab.</p>\n        ",this.grid.after(e),this.grid.classList.add("empty"),this.a11y.announce("No favourites to show!")}async loadMoreItems(){!this.state.loading&&this.state.pagination.hasMore&&(this.state.page+=1,await this.loadFavourites())}updateTypeFilters(t){this.typeFilters&&this.typeFilters.querySelectorAll(".type-filter").forEach((e=>{const s=e.querySelector(".count");if(!s)return;const i=e.dataset.type;if("all"===i){const e=Object.values(t).reduce(((t,e)=>t+(parseInt(e)||0)),0);s.textContent=`(${e})`}else s.textContent=`(${t[i]||0})`}))}setFilterType(t){t!==this.state.filter.type&&(this.typeFilters&&this.typeFilters.querySelectorAll(".type-filter").forEach((e=>{e.classList.toggle("active",e.dataset.type===t),e.setAttribute("aria-selected",e.dataset.type===t)})),this.state.filter.type=t,this.state.page=1,this.loadFavourites(),window.jvbA11y&&window.jvbA11y.announce(`Filtered to show ${"all"===t?"all":t} items`))}setView(t){t!==this.state.view.mode&&(this.viewControls&&this.viewControls.querySelectorAll(".view-toggle").forEach((e=>{const s=e.dataset.view===t;e.setAttribute("aria-pressed",s)})),this.grid&&(this.grid.classList.remove("grid-view","list-view"),this.grid.classList.add(`${t}-view`)),this.state.view.mode=t,localStorage.setItem("favourites_view",t),window.jvbA11y&&window.jvbA11y.announce(`Changed to ${t} view`))}toggleSelectAll(t){const e=this.getVisibleItems();e.forEach((e=>{const s=e.querySelector('.item-select input[type="checkbox"]');s&&(s.checked=t,this.toggleItemSelection(s.value,t))})),this.updateBulkControls(),window.jvbA11y&&window.jvbA11y.announce(t?`Selected all ${e.length} items`:"Deselected all items")}getVisibleItems(){return this.grid?Array.from(this.grid.querySelectorAll(".item:not([hidden])")):[]}toggleItemSelection(t,e){e?this.state.selectedItems.add(t):this.state.selectedItems.delete(t);const s=this.grid.querySelector(`.item[data-id="${t}"]`);s&&s.classList.toggle("selected",e)}handleItemSelection(t){const e=t.checked,s=t.value;if(this.toggleItemSelection(s,e),this.updateBulkControls(),this.updateSelectAllState(),window.jvbA11y){const s=t.closest(".item"),i=s&&s.querySelector("h3")?.textContent||"item";window.jvbA11y.announce(e?`Selected ${i}`:`Deselected ${i}`)}}updateSelectAllState(){const t=this.container.querySelector(this.config.selectors.selectAll);if(!t)return;const e=this.getVisibleItems();if(0===e.length)return t.checked=!1,void(t.indeterminate=!1);const s=e.filter((t=>{const e=t.querySelector('.item-select input[type="checkbox"]');return e&&e.checked})).length;0===s?(t.checked=!1,t.indeterminate=!1):s===e.length?(t.checked=!0,t.indeterminate=!1):(t.checked=!1,t.indeterminate=!0)}updateBulkControls(){if(!this.bulkControls)return;const t=this.bulkControls.querySelector(".bulk-actions");if(!t)return;const e=this.state.selectedItems.size>0;t.hidden=!e;const s=this.bulkControls.querySelector(".selected-count");s&&(s.textContent=e?`${this.state.selectedItems.size} selected`:"")}handleKeyDown(t){"Escape"===t.key&&this.state.selectedItems.size>0&&(t.preventDefault(),this.clearSelection(),window.jvbA11y&&window.jvbA11y.announce("Selection cleared using Escape key"))}clearSelection(){this.state.selectedItems.clear(),this.getVisibleItems().forEach((t=>{const e=t.querySelector('.item-select input[type="checkbox"]');e&&(e.checked=!1),t.classList.remove("selected")}));const t=this.container.querySelector(this.config.selectors.selectAll);t&&(t.checked=!1,t.indeterminate=!1),this.updateBulkControls(),window.jvbA11y&&window.jvbA11y.announce("Selection cleared")}applyBulkAction(t){if(!t||0===this.state.selectedItems.size)return;switch(t){case"unfavourite":this.bulkUnfavourite();break;case"add-to-list":this.showAddToListModal();break;case"create-list":this.showCreateListModal();break;case"add-notes":this.showBulkNotesModal()}const e=this.container.querySelector(".bulk-action-select");e&&(e.value="")}handleItemActions(t){if(t.target.closest(".toggle-notes")){const e=t.target.closest(".toggle-notes"),s="true"===e.getAttribute("aria-expanded"),i=e.closest(".notes-section").querySelector(".notes-content");return e.setAttribute("aria-expanded",!s),i.hidden=s,!s&&i&&i.querySelector("textarea")?.focus(),void t.preventDefault()}if(t.target.closest(".save-notes")){const e=t.target.closest(".save-notes").closest(".notes-content").querySelector("textarea");return e&&this.saveNotes(e),void t.preventDefault()}if(t.target.closest(".view-list")){const e=t.target.closest(".view-list").closest(".list-card");return e&&e.dataset.id&&this.viewList(e.dataset.id),void t.preventDefault()}if(t.target.closest(".share-list")){const e=t.target.closest(".share-list").closest(".list-card");return e&&e.dataset.id&&this.showShareModal(e.dataset.id),void t.preventDefault()}if(t.target.closest(".delete-list")){const e=t.target.closest(".delete-list").closest(".list-card");return e&&e.dataset.id&&this.deleteList(e.dataset.id),void t.preventDefault()}if(t.target.closest(".back-to-lists"))return this.exitListView(),void t.preventDefault()}async loadLists(t=!0){try{this.state.loading=!0,this.loadingManager.show("Loading lists...");const t=await this.cache.fetchWithCache(`${jvbSettings.api}${this.config.endpoints.lists}`,{method:"GET",headers:{"X-WP-Nonce":jvbSettings.nonce,action_nonce:jvbSettings.favourites}},{context:"favourite-lists",forceRefresh:!1});return t.lists&&this.renderLists(t.lists),t}catch(t){throw this.handleError(t,"loading lists"),t}finally{this.state.loading=!1,this.loadingManager.hide()}}renderLists(t){if(!this.listContainer)return;if(removeChildren(this.listContainer),!t||0===t.length)return void(this.listContainer.innerHTML='\n                <div class="no-lists">\n                    <h3>No Lists Yet</h3>\n                    <p>Select favourites from the main tab to organize into lists.</p>\n                </div>\n            ');const e=t.owned,s=t.shared;if(e.length>0){const t=document.createElement("details");t.className="lists-section owned-lists",t.open=!0,t.innerHTML="<summary>Your Lists:</summary>",e.forEach((e=>{const s=this.createListCard(e);t.appendChild(s)})),this.listContainer.appendChild(t)}if(s.length>0){const t=document.createElement("details");t.className="lists-section shared-lists",t.innerHTML="<summary>Lists Shared with You:</summary>",s.forEach((e=>{const s=this.createListCard(e);t.appendChild(s)})),this.listContainer.appendChild(t)}}createListCard(t){const e=document.createElement("div");e.className="list-card",e.dataset.id=t.id;const s="1"===t.is_shared;s&&e.classList.add("shared"),t.is_temp&&e.classList.add("temp"),t.is_owner;const i=sanitizeHtml(t.name||"Untitled List"),a=sanitizeHtml(t.description||"");return e.innerHTML=`\n            <div class="list-header">\n                <h3>${i}</h3>\n                <div class="list-actions">\n                    <button type="button" class="view-list" title="View List">\n                        ${jvbSettings.icons?.show||"View"}\n                    </button>\n                    ${s?"":`\n                        <button type="button" class="share-list" title="Share List">\n                            ${jvbSettings.icons?.share||"Share"}\n                        </button>\n                        <button type="button" class="delete-list" title="Delete List">\n                            ${jvbSettings.icons?.delete||"Delete"}\n                        </button>\n                    `}\n                </div>\n            </div>\n\n            ${a?`<p class="list-description">${a}</p>`:""}\n\n            <div class="list-meta">\n                <div class="meta-stats">\n                    <span class="item-count">${t.item_count||0} items</span>\n                    <span class="date">${formatDate(t.created_at)}</span>\n                </div>\n\n\n                ${s?`\n                    <div class="owner-info">\n                        Shared by ${t.owner_name||"another user"}\n                    </div>\n                `:t.share_count>0?`\n                    <div class="share-info">\n                        Shared with ${t.share_count} ${1===t.share_count?"person":"people"}\n                    </div>\n                `:""}\n            </div>\n        `,e}async viewList(t){try{this.state.loading=!0,this.loadingManager.show("Loading list..."),this.state.currentListId=t;const e=await this.cache.fetchWithCache(`${jvbSettings.api}${this.config.endpoints.lists}?id=${t}`,{method:"GET",headers:{"X-WP-Nonce":jvbSettings.nonce,action_nonce:jvbSettings.favourites}},{context:"list-item",forceRefresh:!1});if(!e.list)throw new Error("List not found");this.showListDetails(e.list)}catch(t){this.handleError(t,"viewing list")}finally{this.state.loading=!1,this.loadingManager.hide()}}showListDetails(t){this.listDetails&&this.listContainer&&(console.log(t),this.listDetails.querySelector(".list-title").value=t.name||"Untitled List",this.listDetails.querySelector(".list-description").value=t.description||"",t.is_owner?this.listDetails.querySelector(".list-actions")||this.createListActions():this.listDetails.querySelector(".list-actions")?.remove(),removeChildren(this.grid),this.renderFavourites(t.items||[],!1),0===t.items.length&&this.showEmptyListState(),window.jvbA11y&&window.jvbA11y.announce(`Viewing list: ${t.name} with ${t.items?.length||0} items`))}createListActions(){const t=document.createElement("div");t.className="list-actions",t.innerHTML='\n            <button type="button" class="share-list" title="Share List">\n            <i class="icon icon-share-fat"></i>\n            <span>Share</span>\n        </button>\n        <button type="button" class="duplicate-list" title="Duplicate List">\n            <i class="icon icon-copy"></i>\n            <span>Duplicate</span>\n        </button>\n        <button type="button" class="delete-list" title="Delete List">\n            <i class="icon icon-trash"></i>\n            <span>Delete</span>\n        </button>\n        ',this.listDetails.insertBefore(t,this.listDetails.querySelector(".bulk-controls"))}exitListView(){this.listDetails&&this.listContainer&&(this.listDetails.hidden=!0,this.listContainer.hidden=!1,this.container.classList.remove("viewing-list"),this.state.currentListId=null,window.jvbA11y&&window.jvbA11y.announce("Returned to lists view"))}showCreateListModal(){this.createListModal&&(this.createListModal.querySelector("form")?.reset(),this.createListModal.showModal(),setTimeout((()=>{this.createListModal.querySelector("#list-name")?.focus()}),100),window.jvbA11y&&window.jvbA11y.announce("Create list dialog opened"))}showAddToListModal(){this.addToListModal&&(this.populateAddToListModal(),this.addToListModal.showModal(),window.jvbA11y&&window.jvbA11y.announce("Add to list dialog opened"))}async populateAddToListModal(){if(!this.addToListModal)return;const t=this.addToListModal.querySelector(".lists-options");if(t){removeChildren(t);try{const e=(await this.loadLists()).lists.owned;if(0===e.length)return t.innerHTML='\n                    <div class="no-lists">\n                        <p>You don\'t have any lists yet.</p>\n                        <button type="button" class="create-list-button">Create a list</button>\n                    </div>\n                ',void t.querySelector(".create-list-button")?.addEventListener("click",(()=>{this.addToListModal.close(),this.showCreateListModal()}));e.forEach((e=>{const s=document.createElement("div");s.className="list-option",s.innerHTML=`\n                    <input type="checkbox" id="${e.id}" name="list_ids[]" value="${e.id}">\n                    <label for="${e.id}">\n\n                        <span class="list-name">${sanitizeHtml(e.name)}</span>\n                        <span class="item-count">( ${e.item_count||0} items )</span>\n                    </label>\n                `,t.appendChild(s)}))}catch(e){t.innerHTML='\n                <div class="error-message">\n                    <p>Error loading lists. Please try again.</p>\n                </div>\n            ',console.error("Error loading lists for modal:",e)}}}async handleCreateList(t){const e=t.get("list_name"),s=t.get("list_description");if(e)try{this.showLoader("Creating list...");const t=[];this.state.selectedItems.forEach((e=>{const s=this.grid.querySelector(`.item[data-id="${e}"]`);s&&t.push({type:s.dataset.type,target_id:parseInt(e)})})),this.queue.addToQueue({type:"favourite_list_create",data:{name:e,description:s,items:t}}),showToast(`List "${e}" created`),this.a11y.announce(`List ${e} created with ${t.length} items`),this.createListModal.close(),this.clearSelection(),this.switchTab("lists")}catch(t){this.handleError(t,"creating list")}finally{this.hideLoader()}else showToast("Please enter a list name","error")}async handleAddToList(t){const e=t.getAll("list_ids[]");if(e.length)try{this.showLoader("Adding to list...");const t=[];this.state.selectedItems.forEach((e=>{const s=this.grid.querySelector(`.item[data-id="${e}"]`);s&&t.push({type:s.dataset.type,target_id:parseInt(e)})})),this.queue.addToQueue({type:"favourite_list_add",data:{list_id:e.join(","),items:t}}),showToast(`Added to ${e.length} ${1===e.length?"list":"lists"}`),this.a11y.announce(`Added ${t.length} items to ${e.length} ${1===e.length?"list":"lists"}`),this.addToListModal.close(),this.clearSelection()}catch(t){this.handleError(t,"adding to list")}finally{this.hideLoader()}else showToast("Please select at least one list","error")}async handleRemoveFromList(t){const e=t.getAll("list_ids[]");if(e.length)try{this.showLoader("Removing from list...");const t=[];this.state.selectedItems.forEach((e=>{const s=this.grid.querySelector(`.item[data-id="${e}"]`);s&&t.push({type:s.dataset.type,target_id:parseInt(e)})})),this.queue.addToQueue({type:"favourite_list_remove",data:{list_id:e.join(","),items:t}}),showToast(`Removed from ${e.length} ${1===e.length?"list":"lists"}`),this.a11y.announce(`Removed ${t.length} items to ${e.length} ${1===e.length?"list":"lists"}`),this.addToListModal.close(),this.clearSelection()}catch(t){this.handleError(t,"remove from list")}finally{this.hideLoader()}else showToast("Please select at least one list","error")}showShareModal(t){this.shareListModal&&(this.state.currentListId=t,this.shareListModal.querySelector("form")?.reset(),this.loadSharedUsers(t),this.shareListModal.showModal(),setTimeout((()=>{this.shareListModal.querySelector("#share-email")?.focus()}),100),window.jvbA11y&&window.jvbA11y.announce("Share list dialog opened"))}async loadSharedUsers(t){try{const e=this.shareListModal.querySelector(".shared-users");if(!e)return;e.innerHTML='<div class="loading">Loading shared users...</div>';const s=await this.cache.fetchWithCache(`${jvbSettings.api}${this.config.endpoints.lists}?id=${t}`,{method:"GET",headers:{"X-WP-Nonce":jvbSettings.nonce,action_nonce:jvbSettings.favourites}},{context:"list-item",forceRefresh:!1});removeChildren(e),s.list&&s.list.shared_users&&s.list.shared_users.length>0?(s.list.shared_users.forEach((t=>{const s=document.createElement("div");s.className=`shared-user ${t.status}`,s.innerHTML=`\n                        <span class="user-email">${t.email}</span>\n                        ${"pending"===t.status?'<span class="pending-badge">Invitation sent</span>':`<span class="permission-badge">${t.permission_type||"view"}</span>`}\n                        <button type="button" class="remove-share" data-email="${t.email}">\n                            ${jvbSettings.icons?.delete||"Remove"}\n                        </button>\n                    `,e.appendChild(s)})),e.querySelectorAll(".remove-share").forEach((t=>{t.addEventListener("click",(()=>{this.unshareList(t.dataset.email)}))}))):e.innerHTML='<div class="no-shares">This list is not shared with anyone yet.</div>'}catch(t){console.error("Error loading shared users:",t)}}async unshareList(t){if(confirm(`Remove ${t}'s access to this list?`))if(this.state.currentListId)try{this.showLoader("Removing access..."),this.queue.addToQueue({type:"favourite_list_unshare",data:{list_id:parseInt(this.state.currentListId),email:t}});const e=Array.from(this.shareListModal.querySelectorAll(".shared-user")).find((e=>e.querySelector(".user-email")?.textContent===t));e&&(e.classList.add("removing"),setTimeout((()=>{if(e.remove(),0===this.shareListModal.querySelectorAll(".shared-user").length){const t=this.shareListModal.querySelector(".shared-users");t&&(t.innerHTML='<div class="no-shares">This list is not shared with anyone yet.</div>')}}),300)),showToast(`Removed ${t}'s access`),this.a11y.announce(`Removed ${t}'s access to list`)}catch(t){this.handleError(t,"removing share access")}finally{this.hideLoader()}else showToast("No list selected","error")}async deleteList(t){if(confirm("Are you sure you want to delete this list? This cannot be undone."))try{this.showLoader("Deleting list..."),this.queue.addToQueue({type:"favourite_list_delete",data:{list_id:parseInt(t)}});const e=this.container.querySelector(`.list-card[data-id="${t}"]`);e&&(e.classList.add("removing"),setTimeout((()=>{e.remove(),0===this.container.querySelectorAll(".list-card").length&&(this.listContainer.innerHTML='\n                                <div class="no-lists">\n                                    <h3>No Lists Yet</h3>\n                                    <p>Create your first list to organize your favourites!</p>\n                                </div>\n                            ')}),300)),showToast("List deleted"),this.a11y.announce("List deleted")}catch(t){this.handleError(t,"deleting list")}finally{this.hideLoader()}}showBulkNotesModal(){let t=document.querySelector(".bulk-notes-modal");t||(t=document.createElement("dialog"),t.className="bulk-notes-modal",t.innerHTML='\n                <form method="dialog" data-save="favourites">\n                    <h2>Add Notes to Selected Items</h2>\n\n                    <div class="field">\n                        <label for="bulk-notes">Notes (will be applied to all selected items)</label>\n                        <textarea id="bulk-notes" name="bulk_notes" rows="5"></textarea>\n                    </div>\n\n                    <div class="actions">\n                        <button type="button" class="cancel">Cancel</button>\n                        <button type="submit" class="save">Save Notes</button>\n                    </div>\n                </form>\n            ',document.body.appendChild(t),t.querySelector("form").addEventListener("submit",(e=>{e.preventDefault();const s=t.querySelector("#bulk-notes").value;this.saveBulkNotes(s),t.close()})),t.querySelector(".cancel").addEventListener("click",(()=>{t.close()}))),t.querySelector("form")?.reset(),t.showModal(),setTimeout((()=>{t.querySelector("#bulk-notes")?.focus()}),100),window.jvbA11y&&window.jvbA11y.announce("Add notes dialog opened")}saveBulkNotes(t){if(t)try{this.showLoader("Saving notes...");let e=Array.from(this.state.selectedItems.values());this.queue.addToQueue({type:"favourite_notes",data:{target_id:e.join(","),notes:t}}),showToast(`Notes saved for ${e.length} items`),this.a11y.announce(`Notes saved for ${e.length} items`),this.clearSelection()}catch(t){this.handleError(t,"saving bulk notes")}finally{this.hideLoader()}}async bulkUnfavourite(){if(confirm("Are you sure you want to remove these items from your favourites?"))try{this.showLoader("Removing from favourites...");const t=[];this.state.selectedItems.forEach((e=>{const s=this.grid.querySelector(`.item[data-id="${e}"]`);if(!s)return;const i=s.dataset.type;t.push({target_id:parseInt(e),type:i,action:"remove"})})),this.queue.addToQueue({type:"favourite_toggle",data:t});const e=[];this.state.selectedItems.forEach((t=>{const s=this.grid.querySelector(`.item[data-id="${t}"]`);if(!s)return;s.style.opacity="0",s.style.transform="scale(0.9)",s.style.transition="opacity 0.3s ease, transform 0.3s ease";const i=new Promise((t=>{setTimeout((()=>{s.remove(),t()}),300)}));e.push(i)})),await Promise.all(e),0===this.grid.children.length&&this.showEmptyState(),this.clearSelection(),showToast(`Removed ${t.length} items from favourites`),this.a11y.announce(`Removed ${t.length} items from favourites`)}catch(t){this.handleError(t,"removing favourites")}finally{this.hideLoader()}}async handleShareList(t){const e=t.get("share_email");if(e)if(this.validateEmail(e))try{this.showLoader("Sharing list..."),this.queue.addToQueue({type:"favourite_list_share",data:{list_id:parseInt(this.state.currentListId),email:e,permission_type:"view"}}),this.shareListModal.querySelector("#share-email").value="",this.loadSharedUsers(this.state.currentListId),showToast(`Invitation sent to ${e}`),this.a11y.announce(`Invitation sent to ${e}`)}catch(t){this.handleError(t,"sharing list")}finally{this.hideLoader()}else showToast("Please enter a valid email address","error");else showToast("Please enter an email address","error")}showLoader(t="Loading..."){if(!this.loader)return;const e=this.loader.querySelector(".loader-message");e&&(e.textContent=t),this.loader.hidden=!1}hideLoader(){this.loader&&(this.loader.hidden=!0)}showToast(t,e){window.jvbNotifications.showToast(t,e)}handleError(t,e){console.error(`Favourites error (${e}):`,t),showToast(`Error ${e}: ${t.message||"Something went wrong"}`,"error"),window.jvbError&&window.jvbError.log(t,{component:"FavouritesManager",action:e}),window.jvbA11y&&window.jvbA11y.announce(`Error ${e}. ${t.message||"Please try again."}`)}validateEmail(t){return/^[^\s@]+@[^\s@]+\.[^\s@]+$/.test(t)}};
\ No newline at end of file
diff --git a/assets/js/min/form.min.js b/assets/js/min/form.min.js
index bc32190..63e4b33 100644
--- a/assets/js/min/form.min.js
+++ b/assets/js/min/form.min.js
@@ -1 +1 @@
-(()=>{class e{constructor(){this.store=new window.jvbStore({name:"forms",storeName:"forms",keyPath:"formId",indexes:[{name:"status",keyPath:"status"},{name:"operationId",keyPath:"operationId"},{name:"timestamp",keyPath:"timestamp"},{name:"formType",keyPath:"type"}],TTL:6048e5}),this.debouncer=window.debouncer,this.ignore=[],this.populateForm=window.jvbPopulate,this.subscribers=new Set,this.forms=new Map,this.specialFields=new Map,this.dependencies=new Map,this.validators=this.initValidators(),this.touchedFields=new Set,this.autoSaveDefaults={delay:3e3,typingDelay:1500,enabled:!0},this.activeRepeaters=new Map,this.repeaterDelays={change:6e3,typing:3e3,blur:1500,add:500,remove:800,reorder:1e3},this.clickHandler=this.handleClick.bind(this),this.changeHandler=this.handleChange.bind(this),this.submitHandler=this.handleSubmit.bind(this),this.inputHandler=this.handleInput.bind(this),this.focusHandler=this.handleFocus.bind(this),this.blurHandler=this.handleBlur.bind(this),this.scanForms(),this.init()}async init(){await this.checkPendingOperations(),this.store.subscribe(this.handleStoreEvent.bind(this)),this.initListeners()}handleStoreEvent(e,t){switch(e){case"item-saved":t.item.status;break;case"data-loaded":this.checkPendingForms()}}async checkPendingForms(){(await this.store.query("status","draft")).forEach((e=>{let t=this.forms.get(e.formId);t&&t.element&&(t.element.querySelector(".restore-form").hidden=!1,new this.populateForm(t.element,e.data))}))}async checkPendingOperations(){const e=await this.store.query("status","pending");if(0===e.length)return;const t=this.groupPendingForms(e);this.showPendingNotification(t)}showPendingNotification(e){const t=document.querySelector(`[data-form-id="${e.formId}"]`);if(!t)return;const r=document.createElement("div");r.className="pending-changes-notification",r.innerHTML=`\n\t\t\t<p>We noticed unsaved changes from last time. Would you like to restore them?</p>\n\t\t\t<button class="restore-changes" data-form-id="${e.formId}">Restore</button>\n\t\t\t<button class="discard-changes" data-form-id="${e.formId}">Discard</button>\n\t\t`,t.insertBefore(r,t.firstChild),r.querySelector(".restore-changes").addEventListener("click",(()=>{this.restorePendingForm(e),r.remove()})),r.querySelector(".discard-changes").addEventListener("click",(()=>{this.discardPendingForm(e.formId),r.remove()}))}restorePendingForm(e){const t=document.querySelector(`[data-form-id="${e.formId}"]`);t&&(new this.populateForm(t,e.formData),e.status="restored",this.pendingForms.set(e.formId,e),window.jvbA11y&&window.jvbA11y.announce("Previous changes restored"))}async discardPendingForm(e){this.store.delete(e),window.jvbA11y&&window.jvbA11y.announce("Previous changes discarded")}initListeners(){this.globalHandlersAdded||(document.addEventListener("click",this.clickHandler),document.addEventListener("change",this.changeHandler),document.addEventListener("focus",this.focusHandler,!0),document.addEventListener("blur",this.blurHandler,!0),document.addEventListener("input",this.inputHandler),this.globalHandlersAdded=!0)}scanForms(){document.querySelectorAll("form").forEach((e=>{console.log("Registering form...",e),this.registerForm(e)}))}registerForm(e,t={}){const r=e.dataset.formId||`form_${Date.now()}`;e.dataset.formId=r,e.addEventListener("submit",this.submitHandler);const s={element:e,id:r,options:{autoSave:"autosave"in e.dataset,saveDelay:this.autoSaveDefaults.delay,endpoint:e.dataset.save??"",cache:!0,...t},dependencies:new Map,data:this.collectFormData(e),isDirty:!1};if(this.initializeFormFields(e,s),this.forms.set(r,s),this.store&&s.options.cache){const e=this.store.get(r);e&&e.formData&&this.showPendingNotification(e)}return s}initializeFormFields(e,t=null){this.initQuillEditors(e),this.initRepeaterFields(e,t),t&&this.initConditionalFields(e,t),this.initCharacterLimits(e),this.initImageUploadFields(e),window.jvbTabs&&e.querySelector("nav.tabs")&&(t.tabs=new window.jvbTabs(e),this.forms.set(t.formId,t),this.initSteppedForm(t.formId)),window.jvbSelector&&window.jvbSelector.scanExistingFields(e)}initSteppedForm(e){const t=this.forms.get(e),r=t.element,s=t.tabs,a=r.querySelectorAll(".tab-content").length,i=r.querySelector(".form-progress .fill"),n=r.querySelector(".step-text .current"),o=r.querySelectorAll("nav.tabs button"),l=e=>{const t=e/a*100;i&&(i.style.width=t+"%"),n&&(n.textContent=e),o.forEach(((t,r)=>{const s=r+1;t.classList.remove("current","completed","pending"),s<e?t.classList.add("completed"):s===e?t.classList.add("current"):t.classList.add("pending")}))};r.addEventListener("click",(e=>{const t=e.target.closest('[data-action="next-step"]'),a=e.target.closest('[data-action="prev-step"]');if(t){e.preventDefault();const a=t.closest(".tab-content"),i=parseInt(a.dataset.step),n=r.querySelector(`.tab-content[data-step="${i+1}"]`);if(n&&this.validateStep(a)){const e=n.dataset.tab;s.switchTab(e,!0),l(i+1),r.scrollIntoView({behavior:"smooth",block:"start"})}}if(a){e.preventDefault();const t=a.closest(".tab-content"),i=parseInt(t.dataset.step),n=r.querySelector(`.tab-content[data-step="${i-1}"]`);if(n){const e=n.dataset.tab;s.switchTab(e,!0),l(i-1),r.scrollIntoView({behavior:"smooth",block:"start"})}}}));const c=s.switchTab.bind(s);s.switchTab=(e,t)=>{c(e,t);const s=r.querySelector(`.tab-content[data-tab="${e}"]`);if(s){const e=parseInt(s.dataset.step);l(e)}},l(1)}validateStep(e){const t=e.querySelectorAll(".field");let r=!0;return t.forEach((e=>{const t=e.querySelector("input, textarea, select");if(t&&!t.closest("[hidden]")){this.validateField(t,e)||(r=!1)}})),r}initQuillEditors(e){window.jvbQuill(e)}initRepeaterFields(e,t){e.querySelectorAll(".repeater").forEach((e=>{const r=e.querySelector(".add-repeater-row"),s=e.querySelector(".repeater-items"),a=e.querySelector("template");r&&a&&s&&(window.Sortable&&new Sortable(s,{handle:".repeater-row-header",animation:150,onEnd:()=>{this.updateRepeaterOrder(e,t)}}),r.addEventListener("click",(()=>{this.addRepeaterRow(e,t)})),s.addEventListener("click",(e=>{e.target.closest(".remove-row")&&this.removeRepeaterRow(e.target.closest(".repeater-row"),t)})))}))}addRepeaterRow(e,t){const r=e.querySelector(".repeater-items"),s=e.querySelector("template"),a=r.children.length,i=e.dataset.field,n=s.content.cloneNode(!0).firstElementChild;n.dataset.index=a,n.querySelectorAll("input, select, textarea").forEach((e=>{const t=e.name;e.name=`${i}:${a}:${t}`,e.id=`${i}-${a}-${t}`;const r=e.nextElementSibling;r&&"LABEL"===r.tagName&&(r.htmlFor=e.id)})),r.appendChild(n),t&&t.options.autoSave&&this.scheduleSave(t,{type:"repeater",action:"add",fieldName:i,delay:this.repeaterDelays.add}),window.jvbA11y&&window.jvbA11y.announce("Row added")}removeRepeaterRow(e,t){const r=e.closest(".repeater"),s=r.dataset.field;e.remove(),this.updateRepeaterOrder(r,t),t&&t.options.autoSave&&this.scheduleSave(t,{type:"repeater",action:"remove",fieldName:s,delay:this.repeaterDelays.remove}),window.jvbA11y&&window.jvbA11y.announce("Row removed")}updateRepeaterOrder(e,t){const r=e.querySelector(".repeater-items"),s=e.dataset.field;Array.from(r.children).forEach(((e,t)=>{e.dataset.index=t,e.querySelectorAll("input, select, textarea").forEach((e=>{const r=e.name.split(":");if(3===r.length){const a=r[2];e.name=`${s}:${t}:${a}`,e.id=`${s}-${t}-${a}`;const i=e.nextElementSibling;i&&"LABEL"===i.tagName&&(i.htmlFor=e.id)}}))})),t&&t.options.autoSave&&this.scheduleSave(t,{type:"repeater",action:"reorder",fieldName:s,delay:this.repeaterDelays.reorder})}initConditionalFields(e,t){e.querySelectorAll("[data-depends-on]").forEach((r=>{const s=r.dataset.dependsOn,a=r.dataset.dependsValue,i=r.dataset.dependsOperator||"==";t.dependencies.has(s)||t.dependencies.set(s,[]),t.dependencies.get(s).push({field:r,requiredValue:a,operator:i}),this.checkFieldDependency(e,r,s,a,i)}))}checkFieldDependency(e,t,r,s,a){const i=e.querySelector(`[name="${r}"]`);if(!i)return;const n=this.getFieldValue(i),o=this.evaluateCondition(n,s,a);this.toggleFieldVisibility(t,o)}evaluateCondition(e,t,r){const s=String(e||""),a=String(t||"");switch(r){case"==":default:return s==a;case"!=":return s!=a;case">":return parseFloat(s)>parseFloat(a);case"<":return parseFloat(s)<parseFloat(a);case">=":return parseFloat(s)>=parseFloat(a);case"<=":return parseFloat(s)<=parseFloat(a);case"contains":return s.includes(a);case"empty":return""===s;case"not_empty":return""!==s}}toggleFieldVisibility(e,t){const r=e.closest(".field, fieldset");r&&(r.hidden=!t,r.querySelectorAll("input, select, textarea").forEach((e=>{e.disabled=!t,!t&&e.hasAttribute("required")?(e.dataset.wasRequired="true",e.removeAttribute("required")):t&&"true"===e.dataset.wasRequired&&(e.setAttribute("required",""),delete e.dataset.wasRequired)})))}initCharacterLimits(e){e.querySelectorAll("[data-limit]").forEach((e=>{const t=parseInt(e.dataset.limit,10),r=e.closest(".field");let s=r?.querySelector(".char-count");!s&&r&&(s=document.createElement("div"),s.className="char-count",s.innerHTML=`<span class="current">0</span> / <span class="limit">${t}</span>`,r.appendChild(s));const a=()=>{const r=e.value.length;s&&(s.querySelector(".current").textContent=r,s.classList.toggle("exceeded",r>t)),r>t&&(e.value=e.value.substring(0,t),s&&(s.querySelector(".current").textContent=t))};e.addEventListener("input",a),a()}))}initImageUploadFields(e){window.jvbUploads.scanFields(e)}handleSubmit(e){if(this.subscribers.size>0){const t=e.target;if(!t.dataset.formId)return;e.preventDefault();const r=this.forms.get(t.dataset.formId);if(!r)return;const s=this.collectFormData(t);this.notify("form-submit",{formId:r.id,data:s,config:r})}}handleClick(e){if(window.targetCheck(e,"div.quantity")){let t=window.targetCheck(e,"div.quantity");this.handleNumberClick(e,t.querySelector("input"))}else if(window.targetCheck(e,"[data-action]")){let t=window.targetCheck(e,"[data-action]");switch(t=t.dataset.action,t){case"clear-form":let t=e.target.closest("form");this.store.delete(t.dataset.formId),t?.reset(),e.target.closest(".restore-form").hidden=!0;break;case"dismiss-restore":e.target.closest(".restore-form").hidden=!0}}}handleNumberClick(e,t){let r=0;if(e.target.closest(".increase")?r+=1:e.target.closest(".decrease")&&(r-=1),0!==r){let s=parseFloat(t.step);s=Math.max(s,1),e.ctrlKey&&e.shiftKey?s*=50:e.ctrlKey?s*=5:e.shiftKey&&(s*=10);let a=""===t.value?0:parseFloat(t.value);t.value=a+s*r,this.handleNumberLimits(t)}}handleNumberLimits(e){let[t,r,s,a]=[e.min,e.max,e.closest(".quantity")?.querySelector(".increase"),e.closest(".quantity")?.querySelector(".decrease")],i=parseFloat(e.value);i<t?(e.value=t,a.disabled=!0):i>r?(e.value=r,s.disabled=!1):s.disabled?s.disabled=!1:a.disabled&&(a.disabled=!1)}handleChange(e){if(!e.target.closest("[data-ignore]")&&this.subscribers.size>0){const t=e.target,r=t.form||t.closest("form");if(!r)return;const s=this.forms?.get(r.dataset.formId);if(!s)return;const a=s.dependencies.get(t.name);if(a&&a.forEach((e=>{this.checkFieldDependency(r,e.field,t.name,e.requiredValue,e.operator)})),s.options.autoSave&&!r.dataset.noautosave){const e=this.getDelayForField(t);this.scheduleSave(s,e)}}}handleFocus(e){const t=e.target;t.matches("input, textarea, select")&&(this.currentFocus=t)}handleBlur(e){if(e.target.closest("[data-ignore]"))return;const t=e.target,r=t.form||t.closest("form");if(!r)return;const s=e.target.closest("input, textarea, select");if(s){const e=this.findFieldWrapper(s);if(e){const t=e.dataset.field;t&&(this.shouldDebounce(s)&&window.debouncer.cancel(`validate_${t}`),this.touchedFields.add(t)),this.validateField(s,e)}const a=this.forms?.get(r.dataset.formId);a&&a.options.autoSave&&!r.dataset.noautosave&&this.scheduleSave(a,{type:"blur",fieldName:t.name,delay:1500})}}handleInput(e){if(e.target.closest("[data-ignore]")||!e.target.closest("form"))return;const t=e.target.closest("input, textarea, select");if(!t)return;let r=t.closest("form");this.showFormStatus(r.dataset.formId,"pending");const s=this.findFieldWrapper(t);if(!s)return;const a=s.dataset.field;a&&this.touchedFields.add(a),this.shouldDebounce(t)&&window.debouncer.schedule(`validate_${a}`,((e,t)=>this.validateField.bind(this)),500)}initValidators(){return{email:{pattern:/^[^\s@]+@[^\s@]+\.[^\s@]+$/,message:"Please enter a valid email address"},url:{pattern:/^https?:\/\/.+\..+/,message:"Please enter a valid URL starting with http:// or https://"},phone:{pattern:/^[\d\s\-\+\(\)\.]+$/,message:"Please enter a valid phone number"},number:{test:(e,t)=>{const r=parseFloat(e);if(isNaN(r))return"Please enter a valid number";const s=t.dataset.min,a=t.dataset.max;return void 0!==s&&r<parseFloat(s)?`Value must be at least ${s}`:!(void 0!==a&&r>parseFloat(a))||`Value must be at most ${a}`}},text:{test:(e,t)=>{const r=t.dataset.minlength,s=t.dataset.maxlength;return r&&e.length<parseInt(r)?`Must be at least ${r} characters`:!(s&&e.length>parseInt(s))||`Must be no more than ${s} characters`}}}}findFieldWrapper(e){let t=e.closest(".field");return t||(t=e.closest("[data-field]")),t}shouldDebounce(e){return["text","email","url","tel","search"].includes(e.type)||"TEXTAREA"===e.tagName}validateField(e,t){const r=this.getFieldValue(e),s=t.dataset.field;if(!this.touchedFields.has(s)&&!e.required)return!0;if(!r&&!e.required)return this.clearValidation(t),!0;if(e.required&&!r)return this.showError(t,"This field is required"),!1;if(e.checkValidity&&!e.checkValidity())return this.showError(t,e.validationMessage),!1;const a=t.dataset.pattern;if(a&&r){if(!new RegExp(a).test(r)){const e=t.dataset.validationMessage||"Invalid format";return this.showError(t,e),!1}}const i=t.dataset.validate||e.type;if(i&&this.validators[i]){const e=this.validators[i];if(e.pattern&&!e.pattern.test(r))return this.showError(t,e.message),!1;if(e.test){const s=e.test(r,t);if(!0!==s)return this.showError(t,s),!1}}return this.showSuccess(t),!0}getFieldValue(e){if(!e)return"";if("checkbox"===e.type)return e.checked?e.value||"1":"";if("radio"===e.type){const t=e.form?.querySelector(`[name="${e.name}"]:checked`);return t?t.value:""}return"select-multiple"===e.type?Array.from(e.selectedOptions).map((e=>e.value)):e.value?.trim()||""}showSuccess(e){if(!e)return;const t=e.querySelector(".validation-icon.success"),r=e.querySelector(".validation-icon.error"),s=e.querySelector(".validation-message"),a=e.querySelector("input, textarea, select");e.classList.remove("has-error"),a?.classList.remove("error"),e.classList.add("has-success"),t&&(t.hidden=!1),r&&(r.hidden=!0),s&&(s.hidden=!0,s.textContent="")}showError(e,t){if(!e)return;const r=e.querySelector(".validation-icon.success"),s=e.querySelector(".validation-icon.error"),a=e.querySelector(".validation-message"),i=e.querySelector("input, textarea, select");e.classList.remove("has-success"),e.classList.add("has-error"),i?.classList.add("error"),r&&(r.hidden=!0),s&&(s.hidden=!1),a&&(a.hidden=!1,a.textContent=t)}clearValidation(e){if(!e)return;const t=e.querySelector(".validation-icon"),r=e.querySelector(".validation-message"),s=e.querySelector("input, textarea, select");e.classList.remove("has-error","has-success"),s?.classList.remove("error"),t&&(t.hidden=!0),r&&(r.hidden=!0,r.textContent="")}validateAllFields(e){if(!e)return!0;const t=e.querySelectorAll(".field:not([hidden])");let r=!0;return t.forEach((e=>{if(this.isComplexFieldWrapper(e))return;const t=e.querySelector('input:not([type="hidden"]), textarea, select');if(t&&!t.closest("[hidden]")){const s=e.dataset.field;s&&this.touchedFields.add(s);this.validateField(t,e)||(r=!1,!1===r&&(t.scrollIntoView({behavior:"smooth",block:"center"}),t.focus()))}})),r}isComplexFieldWrapper(e){return e.classList.contains("repeater")||e.classList.contains("group")||e.classList.contains("upload")}attachRepeaterValidation(e){e.addEventListener("click",(t=>{t.target.closest(".add-repeater-row")&&setTimeout((()=>{e.querySelectorAll(".repeater-row").forEach((e=>{e.querySelectorAll("input, textarea, select").forEach((e=>{const t=this.findFieldWrapper(e);t&&this.clearValidation(t)}))}))}),100)}))}attachGroupValidation(e){e.addEventListener("change",(t=>{const r=t.target.closest("input, select");if(!r)return;const s=r.name;if(!s)return;e.querySelectorAll(`[data-show-if*="${s}"]`).forEach((e=>{e.hidden&&this.clearValidation(e)}))}))}resetForm(e){if(!e)return;this.touchedFields.clear();e.querySelectorAll(".field").forEach((e=>{this.clearValidation(e)}))}getFormErrors(e){const t={};return e.querySelectorAll(".field.has-error").forEach((e=>{const r=e.dataset.field,s=e.querySelector(".validation-message");r&&s&&(t[r]=s.textContent)})),t}addValidator(e,t){this.validators[e]=t}getDelayForField(e){return"text"===e.type||"textarea"===e.type?this.autoSaveDefaults.typingDelay:["checkbox","radio","select-one","select-multiple"].includes(e.type)?1e3:this.autoSaveDefaults.delay}scheduleSave(e,t=this.autoSaveDefaults.delay){document.addEventListener("input",this.saveCheck,{passive:!0});const r=`autosave_${e.id}`;this.debouncer.schedule(r,(()=>this.autosave(e)),t)}saveCheck(e){let t=e.target.closest("form[data-id]");t&&this.scheduleSave(this.forms.get(t.dataset.id))}async autosave(e){const t=this.collectFormData(e.element);this.showFormStatus(e.id,"saving"),await this.store.save({formId:e.id,data:t,status:"draft",timestamp:Date.now()}).then((()=>{this.showFormStatus(e.id,"autosaved")}));const r=this.getChangedFields(e.data,t);if(0!==Object.keys(r).length){e.data=t,this.forms.set(e.id,e),document.removeEventListener("input",this.handleInput);for(let[e,s]of Object.entries(t))"object"==typeof s&&(r[e]=s);this.notify("form-autosave",{formId:e.id,changes:r,fullData:t,config:e})}}hasUnsavedChanges(e){const t=this.forms.get(e);if(!t)return!1;if(t.operations?.size>0)return!0;const r=this.collectFormData(t.element),s=this.getChangedFields(t.lastSnapshot,r);return Object.keys(s).length>0}showFormStatus(e,t){let r=this.forms.get(e);console.log("Setting status: ",t);const s=r.element.querySelector(".fstatus");s.hidden=!1;const a=s.querySelector(".message");a.textContent="",s.querySelector(".icon")?.remove();const i={saving:"Saving changes...",autosaved:"Changes saved locally. Submit form to send to server.",uploading:"Uploading your form to server",submitted:"Successfully sent to server",pending:"Unsaved changes",error:"Failed to save changes. Refresh and try again?",offline:"Changes will be saved when online"},n={autosaved:"check",submitted:"check",error:"close",offline:"cloud-slash",pending:"exclamation-mark"};let o=window.getIcon(n[t]);o&&s.prepend(o),console.log(t,i[t]),console.log(t,n[t]),a.textContent=i[t]||t,s.classList.toggle("loading",["uploading","saving"].includes(t)),"submitted"===t&&setTimeout((()=>s.hidden=!0),3e3)}cleanupSpecialFields(){this.specialFields.forEach((e=>{if("quill"===e.type&&e.instance){const t=e.instance.container.previousSibling;t?.classList.contains("ql-toolbar")&&t.remove()}})),this.uploader?.destroy(),this.specialFields.clear()}collectFormData(e){const t=new FormData(e);let r={};const s={},a={};for(let[i,n]of t.entries()){if(this.ignore.includes(i)||i.endsWith("_temp"))continue;this.getFieldProcessor(i)(i,n,r,s,a,e)}return window.isEmptyObject(a)?this.mergeRepeaterData(r,s):(r=this.mergeRepeaterData(r,s),this.mergePostData(r,a))}getFieldProcessor(e){return e.includes("|")?this.processTableField:e.includes("::")?this.processGroupField:e.includes(":")?this.processRepeaterField:/\[[^\]]+\]/.test(e)?this.processLocationField:this.processRegularField}mergeRepeaterData(e,t){return Object.keys(t).forEach((r=>{const s={};Object.keys(t[r]).forEach((e=>{const a=t[r][e];Object.keys(a).length>0&&(s[e]=a)})),e[r]=Object.values(s)})),e}mergePostData(e,t){for(let[t,r]in Object.entries(r))e[t]=r;return e}processTableField(e,t,r,s,a,i){let[n,o]=e.split("|");!n in a&&(a[n]={});this.getFieldProcessor(o)(o,t,a,s,a,i)}processRepeaterField(e,t,r,s,a,i){let[n,o,l]=e.split(":");const c=l.endsWith("[]");l=l.replace("[]",""),s[n]||(s[n]={}),s[n][o]||(s[n][o]={}),c||s[n][o][l]?(s[n][o][l]?Array.isArray(s[n][o][l])||(s[n][o][l]=[s[n][o][l]]):s[n][o][l]=[],s[n][o][l].push(t)):s[n][o][l]=t}processGroupField(e,t,r,s,a,i){const n=e.split("::"),o=n[0];r[o]||(r[o]={});let l=r[o];for(let e=1;e<n.length-1;e++){const t=n[e];l[t]||(l[t]={}),l=l[t]}const c=n[n.length-1];void 0!==l[c]?(Array.isArray(l[c])||(l[c]=[l[c]]),l[c].push(t)):l[c]=t}processLocationField(e,t,r,s,a,i){let[n,o]=e.split("[");o=o.replace("]",""),Object.hasOwn(r,n)||(r[n]={},Object.hasOwn(r,"sendAll")?r.sendAll.includes(n)||r.sendAll.push(n):r.sendAll=[n]),r[n][o]=t}processRegularField(e,t,r,s,a,i){r[e=e.replace("[]","")]?(Array.isArray(r[e])||(r[e]=[r[e]]),r[e].push(t)):r[e]=t}getFieldValue(e){if(!e)return"";if("checkbox"===e.type)return e.checked?e.value||"1":"";if("radio"===e.type){const t=e.form.querySelector(`[name="${e.name}"]:checked`);return t?t.value:""}return"select-multiple"===e.type?Array.from(e.selectedOptions).map((e=>e.value)):e.value}getChangedFields(e,t){return window.getDifferences?.map(e,t)||{}}showSummary(e,t="form"){const r=this.forms.get(e);if(!r)return;const s=r.element||document.querySelector(`[data-form-id="${e}"]`),a=window.getTemplate("formSummary"),[i,n,o]=[a.querySelector("h2"),a.querySelector(".summary"),a.querySelector(".result")],l=["sendAll",...this.ignore];for(const[e,t]of Object.entries(r.data)){if(l.includes(e)||this.isEmptyValue(t))continue;const r=this.getFieldInfo(s,e);if(!r.label)continue;const a=this.createResultElement(o,r,t,s);a&&n.appendChild(a)}o.remove(),(t="form"!==t?s.closest(t)??s:s).after(a),window.fade(t,!1)}isEmptyValue(e){return null==e||""===e||(!(!Array.isArray(e)||0!==e.length)||"object"==typeof e&&0===Object.keys(e).length)}getFieldInfo(e,t){let r=e.querySelector(`label[for="${t}"]`),s=null,a=null;if(s||(s=e.querySelector(`[name="${t}"]`)),s||(s=e.querySelector(`[name="${t}[]"]`)),!s){const a=e.querySelector(`fieldset[data-field="${t}"]`);a&&(r=a.querySelector("legend"),s=a.querySelector("input, select, textarea"))}if(!r&&s){const e=s.closest(".field, fieldset");e&&(r=e.querySelector("label, legend"))}a=e.querySelector(`.field[data-field="${t}"], fieldset[data-field="${t}"]`);let i="text";return a?.dataset.type?i=a.dataset.type:s&&(i="checkbox"===s.type&&s.name.endsWith("[]")?"checkbox":"checkbox"===s.type?"true_false":"SELECT"===s.tagName&&s.multiple?"select":s.type||"text"),{label:r?.textContent.replace("*","").trim()||null,type:i,wrapper:a,input:s}}createResultElement(e,t,r,s){const a=e.cloneNode(!0),i=a.querySelector("h4"),n=a.querySelector("p");i.textContent=t.label;const o=this.formatFieldValue(r,t.type,s);return this.isHtmlContent(o)?n.innerHTML=o:n.textContent=o,a}isHtmlContent(e){return"string"==typeof e&&(e.includes("<br>")||e.includes("<p>")||e.includes("<ul>")||e.includes("<ol>")||e.includes("<a ")||e.includes("<strong>")||e.includes("<em>")||e.includes("<div"))}formatFieldValue(e,t,r){switch(t){case"textarea":case"wysiwyg":return this.formatTextareaValue(e,t);case"true_false":return"1"===e||1===e||!0===e?"Yes":"No";case"checkbox":return Array.isArray(e)?this.formatArrayValue(e):"1"===e||1===e||!0===e?"Yes":"No";case"select":return Array.isArray(e)?this.formatArrayValue(e):this.getSelectLabel(e,r,t);case"date":case"datetime":case"time":return window.formatDate?window.formatDate(e):e;case"radio":return this.getSelectLabel(e,r,t);case"repeater":return this.formatRepeaterValue(e);case"group":return this.formatGroupValue(e);case"location":return this.formatLocationValue(e);case"file":case"image":return this.formatFileValue(e);case"number":return this.formatNumber(e);case"email":return`<a href="mailto:${e}">${e}</a>`;case"url":return`<a href="${e}" target="_blank" rel="noopener">${e}</a>`;case"phone":return`<a href="tel:${e.replace(/\D/g,"")}">${e}</a>`;default:return Array.isArray(e)?this.formatArrayValue(e):e}}formatRepeaterValue(e){if(!Array.isArray(e)||0===e.length)return"<em>No entries</em>";let t='<div class="repeater-summary">';return e.forEach(((e,r)=>{t+='<div class="repeater-row">',t+=`<strong>Entry ${r+1}:</strong><ul>`;for(const[r,s]of Object.entries(e))if(!this.isEmptyValue(s)){const e=r.replace(/_/g," ").replace(/\b\w/g,(e=>e.toUpperCase()));t+=`<li><strong>${e}:</strong> ${s}</li>`}t+="</ul></div>"})),t+="</div>",t}formatGroupValue(e){if("object"!=typeof e||0===Object.keys(e).length)return"<em>No data</em>";let t='<div class="group-summary"><ul>';for(const[r,s]of Object.entries(e))if(!this.isEmptyValue(s)){const e=r.replace(/_/g," ").replace(/\b\w/g,(e=>e.toUpperCase()));"object"!=typeof s||Array.isArray(s)?t+=`<li><strong>${e}:</strong> ${s}</li>`:t+=`<li><strong>${e}:</strong> ${this.formatGroupValue(s)}</li>`}return t+="</ul></div>",t}formatLocationValue(e){if("object"!=typeof e)return e;const t=[];return["address","city","state","zip","country"].forEach((r=>{e[r]&&t.push(e[r])})),t.join(", ")}formatFileValue(e){return"string"==typeof e?e.startsWith("http")?`<a href="${e}" target="_blank">View file</a>`:e:Array.isArray(e)?e.map((e=>"string"==typeof e?`<a href="${e}" target="_blank">View file</a>`:e.name||"File")).join(", "):"File uploaded"}formatNumber(e){const t=parseFloat(e);return isNaN(t)?e:e.toString().includes(".")&&2===e.toString().split(".")[1].length?new Intl.NumberFormat("en-CA",{style:"currency",currency:"USD"}).format(t):new Intl.NumberFormat("en-CA").format(t)}formatArrayValue(e,t=null,r=null){if(0===e.length)return"<em>None selected</em>";if(t&&r&&r.input){return"<ul><li>"+e.map((e=>this.getSelectLabel(e,t,r.type))).join("</li><li>")+"</li></ul>"}return"<ul><li>"+e.join("</li><li>")+"</li></ul>"}getSelectLabel(e,t,r){if("select"===r){const r=t.querySelector(`option[value="${e}"]`);return r?.textContent||e}if("radio"===r){const r=t.querySelector(`input[type="radio"][value="${e}"]`),s=r?.nextElementSibling;return s?.textContent||e}if("checkbox"===r){const r=t.querySelector(`input[type="checkbox"][value="${e}"]`);if(r){const e=t.querySelector(`label[for="${r.id}"]`);if(e)return e.textContent.trim();const s=r.nextElementSibling;if("LABEL"===s?.tagName)return s.textContent.trim()}}return e}formatTextareaValue(e,t){return e?"wysiwyg"===t||this.containsHtml(e)?e:this.formatPlainText(e):"<em>Empty</em>"}containsHtml(e){return/<(p|strong|em|u|s|ol|ul|li|blockquote|h[1-6]|a|br|span)\b[^>]*>/i.test(e)}formatPlainText(e){if(!e)return"";const t=(e=e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")).split(/\n\n+/);return t.length>1?t.map((e=>`<p>${e.replace(/\n/g,"<br>")}</p>`)).join(""):e.replace(/\n/g,"<br>")}nl2br(e){return this.formatPlainText(e)}subscribe(e){return this.subscribers.add(e),()=>this.subscribers.delete(e)}notify(e,t){this.subscribers.forEach((r=>r(e,t)))}cleanupForm(e){const t=this.forms.get(e);t&&(this.hasUnsavedChanges(e)&&this.autosave(t),this.cleanupSpecialFields(),this.forms.delete(e))}destroy(){this.globalHandlersAdded&&(document.removeEventListener("change",this.changeHandler),document.removeEventListener("focus",this.focusHandler,!0),document.removeEventListener("blur",this.blurHandler,!0),document.removeEventListener("input",this.inputHandler,!0)),this.forms.forEach((e=>{let t=e.element;t&&t.removeEventListener("submit",this.submitHandler)})),this.specialFields.clear(),this.forms.clear(),this.activeRepeaters.clear(),this.forms&&this.forms.clear()}}document.addEventListener("DOMContentLoaded",(()=>{window.jvbForm=e,console.log("FormController in window")}))})();
\ No newline at end of file
+(()=>{class e{constructor(){this.store=new window.jvbStore({name:"forms",storeName:"forms",keyPath:"formId",indexes:[{name:"status",keyPath:"status"},{name:"operationId",keyPath:"operationId"},{name:"timestamp",keyPath:"timestamp"},{name:"formType",keyPath:"type"}],TTL:6048e5}),this.debouncer=window.debouncer,this.ignore=[],this.populateForm=window.jvbPopulate,this.subscribers=new Set,this.forms=new Map,this.specialFields=new Map,this.dependencies=new Map,this.validators=this.initValidators(),this.touchedFields=new Set,this.autoSaveDefaults={delay:3e3,typingDelay:1500,enabled:!0},this.activeRepeaters=new Map,this.repeaterDelays={change:6e3,typing:3e3,blur:1500,add:500,remove:800,reorder:1e3},this.clickHandler=this.handleClick.bind(this),this.changeHandler=this.handleChange.bind(this),this.submitHandler=this.handleSubmit.bind(this),this.inputHandler=this.handleInput.bind(this),this.focusHandler=this.handleFocus.bind(this),this.blurHandler=this.handleBlur.bind(this),this.init()}async init(){await this.checkPendingOperations(),this.store.subscribe(this.handleStoreEvent.bind(this)),this.initListeners()}handleStoreEvent(e,t){switch(e){case"item-saved":t.item.status;break;case"data-loaded":this.checkPendingForms()}}async checkPendingForms(){(await this.store.query("status","draft")).forEach((e=>{let t=this.forms.get(e.formId);t&&t.element&&(t.element.querySelector(".restore-form").hidden=!1,new this.populateForm(t.element,e.data))}))}async checkPendingOperations(){const e=await this.store.query("status","pending");if(0===e.length)return;const t=this.groupPendingForms(e);this.showPendingNotification(t)}showPendingNotification(e){const t=document.querySelector(`[data-form-id="${e.formId}"]`);if(!t)return;const r=document.createElement("div");r.className="pending-changes-notification",r.innerHTML=`\n\t\t\t<p>We noticed unsaved changes from last time. Would you like to restore them?</p>\n\t\t\t<button class="restore-changes" data-form-id="${e.formId}">Restore</button>\n\t\t\t<button class="discard-changes" data-form-id="${e.formId}">Discard</button>\n\t\t`,t.insertBefore(r,t.firstChild),r.querySelector(".restore-changes").addEventListener("click",(()=>{this.restorePendingForm(e),r.remove()})),r.querySelector(".discard-changes").addEventListener("click",(()=>{this.discardPendingForm(e.formId),r.remove()}))}restorePendingForm(e){const t=document.querySelector(`[data-form-id="${e.formId}"]`);t&&(new this.populateForm(t,e.formData),e.status="restored",this.pendingForms.set(e.formId,e),window.jvbA11y&&window.jvbA11y.announce("Previous changes restored"))}async discardPendingForm(e){this.store.delete(e),window.jvbA11y&&window.jvbA11y.announce("Previous changes discarded")}initListeners(){this.globalHandlersAdded||(document.addEventListener("click",this.clickHandler),document.addEventListener("change",this.changeHandler),document.addEventListener("focus",this.focusHandler,!0),document.addEventListener("blur",this.blurHandler,!0),document.addEventListener("input",this.inputHandler),this.globalHandlersAdded=!0)}registerForm(e,t={}){if(!e)return;const r=e.dataset.formId||`form_${Date.now()}`;e.dataset.formId=r,e.addEventListener("submit",this.submitHandler);const s={element:e,id:r,status:"",options:{autosave:"autosave"in e.dataset,saveDelay:this.autoSaveDefaults.delay,endpoint:e.dataset.save??"",formStatus:!0,cache:!0,...t},dependencies:new Map,data:this.collectFormData(e)};if(this.initializeFormFields(e,s),this.forms.set(r,s),this.store&&s.options.cache){const e=this.store.get(r);e&&e.formData&&this.showPendingNotification(e)}return s}initializeFormFields(e,t=null){this.initQuillEditors(e),this.initRepeaterFields(e,t),t&&this.initConditionalFields(e,t),this.initCharacterLimits(e),this.initImageUploadFields(e),window.jvbTabs&&e.querySelector("nav.tabs")&&(t.tabs=new window.jvbTabs(e),this.forms.set(t.formId,t),this.initSteppedForm(t.formId)),window.jvbSelector&&window.jvbSelector.scanExistingFields(e)}initSteppedForm(e){const t=this.forms.get(e),r=t.element,s=t.tabs,a=r.querySelectorAll(".tab-content").length,i=r.querySelector(".form-progress .fill"),n=r.querySelector(".step-text .current"),o=r.querySelectorAll("nav.tabs button"),l=e=>{const t=e/a*100;i&&(i.style.width=t+"%"),n&&(n.textContent=e),o.forEach(((t,r)=>{const s=r+1;t.classList.remove("current","completed","pending"),s<e?t.classList.add("completed"):s===e?t.classList.add("current"):t.classList.add("pending")}))};r.addEventListener("click",(e=>{const t=e.target.closest('[data-action="next-step"]'),a=e.target.closest('[data-action="prev-step"]');if(t){e.preventDefault();const a=t.closest(".tab-content"),i=parseInt(a.dataset.step),n=r.querySelector(`.tab-content[data-step="${i+1}"]`);if(n&&this.validateStep(a)){const e=n.dataset.tab;s.switchTab(e,!0),l(i+1),r.scrollIntoView({behavior:"smooth",block:"start"})}}if(a){e.preventDefault();const t=a.closest(".tab-content"),i=parseInt(t.dataset.step),n=r.querySelector(`.tab-content[data-step="${i-1}"]`);if(n){const e=n.dataset.tab;s.switchTab(e,!0),l(i-1),r.scrollIntoView({behavior:"smooth",block:"start"})}}}));const c=s.switchTab.bind(s);s.switchTab=(e,t)=>{c(e,t);const s=r.querySelector(`.tab-content[data-tab="${e}"]`);if(s){const e=parseInt(s.dataset.step);l(e)}},l(1)}validateStep(e){const t=e.querySelectorAll(".field");let r=!0;return t.forEach((e=>{const t=e.querySelector("input, textarea, select");if(t&&!t.closest("[hidden]")){this.validateField(t,e)||(r=!1)}})),r}initQuillEditors(e){window.jvbQuill(e)}initRepeaterFields(e,t){e.querySelectorAll(".repeater").forEach((e=>{const r=e.querySelector(".add-repeater-row"),s=e.querySelector(".repeater-items"),a=e.querySelector("template");r&&a&&s&&(window.Sortable&&new Sortable(s,{handle:".repeater-row-header",animation:150,onEnd:()=>{this.updateRepeaterOrder(e,t)}}),r.addEventListener("click",(()=>{this.addRepeaterRow(e,t)})),s.addEventListener("click",(e=>{e.target.closest(".remove-row")&&this.removeRepeaterRow(e.target.closest(".repeater-row"),t)})))}))}addRepeaterRow(e,t){const r=e.querySelector(".repeater-items"),s=e.querySelector("template"),a=r.children.length,i=e.dataset.field,n=s.content.cloneNode(!0).firstElementChild;n.dataset.index=a,n.querySelectorAll("input, select, textarea").forEach((e=>{const t=e.name;e.name=`${i}:${a}:${t}`,e.id=`${i}-${a}-${t}`;const r=e.nextElementSibling;r&&"LABEL"===r.tagName&&(r.htmlFor=e.id)})),r.appendChild(n),t&&this.scheduleSave(t,{type:"repeater",action:"add",fieldName:i,delay:this.repeaterDelays.add}),window.jvbA11y&&window.jvbA11y.announce("Row added")}removeRepeaterRow(e,t){const r=e.closest(".repeater"),s=r.dataset.field;e.remove(),this.updateRepeaterOrder(r,t),t&&this.scheduleSave(t,{type:"repeater",action:"remove",fieldName:s,delay:this.repeaterDelays.remove}),window.jvbA11y&&window.jvbA11y.announce("Row removed")}updateRepeaterOrder(e,t){const r=e.querySelector(".repeater-items"),s=e.dataset.field;Array.from(r.children).forEach(((e,t)=>{e.dataset.index=t,e.querySelectorAll("input, select, textarea").forEach((e=>{const r=e.name.split(":");if(3===r.length){const a=r[2];e.name=`${s}:${t}:${a}`,e.id=`${s}-${t}-${a}`;const i=e.nextElementSibling;i&&"LABEL"===i.tagName&&(i.htmlFor=e.id)}}))})),t&&this.scheduleSave(t,{type:"repeater",action:"reorder",fieldName:s,delay:this.repeaterDelays.reorder})}initConditionalFields(e,t){e.querySelectorAll("[data-depends-on]").forEach((r=>{const s=r.dataset.dependsOn,a=r.dataset.dependsValue,i=r.dataset.dependsOperator||"==";t.dependencies.has(s)||t.dependencies.set(s,[]),t.dependencies.get(s).push({field:r,requiredValue:a,operator:i}),this.checkFieldDependency(e,r,s,a,i)}))}checkFieldDependency(e,t,r,s,a){const i=e.querySelector(`[name="${r}"]`);if(!i)return;const n=this.getFieldValue(i),o=this.evaluateCondition(n,s,a);this.toggleFieldVisibility(t,o)}evaluateCondition(e,t,r){const s=String(e||""),a=String(t||"");switch(r){case"==":default:return s==a;case"!=":return s!=a;case">":return parseFloat(s)>parseFloat(a);case"<":return parseFloat(s)<parseFloat(a);case">=":return parseFloat(s)>=parseFloat(a);case"<=":return parseFloat(s)<=parseFloat(a);case"contains":return s.includes(a);case"empty":return""===s;case"not_empty":return""!==s}}toggleFieldVisibility(e,t){const r=e.closest(".field, fieldset");r&&(r.hidden=!t,r.querySelectorAll("input, select, textarea").forEach((e=>{e.disabled=!t,!t&&e.hasAttribute("required")?(e.dataset.wasRequired="true",e.removeAttribute("required")):t&&"true"===e.dataset.wasRequired&&(e.setAttribute("required",""),delete e.dataset.wasRequired)})))}initCharacterLimits(e){e.querySelectorAll("[data-limit]").forEach((e=>{const t=parseInt(e.dataset.limit,10),r=e.closest(".field");let s=r?.querySelector(".char-count");!s&&r&&(s=document.createElement("div"),s.className="char-count",s.innerHTML=`<span class="current">0</span> / <span class="limit">${t}</span>`,r.appendChild(s));const a=()=>{const r=e.value.length;s&&(s.querySelector(".current").textContent=r,s.classList.toggle("exceeded",r>t)),r>t&&(e.value=e.value.substring(0,t),s&&(s.querySelector(".current").textContent=t))};e.addEventListener("input",a),a()}))}initImageUploadFields(e){window.jvbUploads.scanFields(e)}async handleSubmit(e){const t=e.target;if(!t.dataset.formId)return;const r=this.forms.get(t.dataset.formId);if(this.subscribers.size>0){e.preventDefault();const s=this.collectFormData(t);this.notify("form-submit",{formId:r.id,data:s,config:r})}}handleFormSuccess(e,t){if(e.querySelectorAll(".error-message").forEach((e=>e.remove())),e.querySelectorAll(".field-error").forEach((e=>e.classList.remove("field-error"))),e.classList.add("form-success"),t.message){const r=document.createElement("div");r.className="form-success-message success-message",r.textContent=t.message,e.insertBefore(r,e.firstChild);const s=window.getIcon?.("check-circle");s&&(s.classList.add("success-icon"),r.prepend(s))}if(t.title||t.description){const r=document.createElement("div");if(r.className="success-box",t.title){const e=document.createElement("h3");e.textContent=t.title,r.appendChild(e)}if(t.description){(Array.isArray(t.description)?t.description:[t.description]).forEach((e=>{const t=document.createElement("p");t.textContent=e,r.appendChild(t)}))}e.insertBefore(r,e.firstChild)}window.jvbA11y&&window.jvbA11y.announce(t.message||"Form submitted successfully"),e.dispatchEvent(new CustomEvent("jvb-form-success",{detail:t}))}handleFormError(e,t){if(e.querySelectorAll(".error-message").forEach((e=>e.remove())),e.querySelectorAll(".field-error, .has-error").forEach((e=>{e.classList.remove("field-error","has-error")})),e.querySelectorAll(".field").forEach((e=>{this.clearValidation(e)})),t.field){const r=e.querySelector(`[data-field="${t.field}"]`);if(r){this.showError(r,t.message),this.touchedFields.add(t.field),r.scrollIntoView({behavior:"smooth",block:"center"});const e=r.querySelector("input, textarea, select");e&&e.focus()}}else{const r=document.createElement("div");r.className="form-error error-message",r.textContent=t.message;const s=window.getIcon?.("close-circle");s&&(s.classList.add("error-icon"),r.prepend(s)),e.insertBefore(r,e.firstChild),e.scrollIntoView({behavior:"smooth",block:"start"})}if(window.jvbA11y){const e=t.field?`Error in ${t.field}: ${t.message}`:`Form error: ${t.message}`;window.jvbA11y.announce(e)}e.dispatchEvent(new CustomEvent("jvb-form-error",{detail:t}))}handleClick(e){if(window.targetCheck(e,"div.quantity")){let t=window.targetCheck(e,"div.quantity");this.handleNumberClick(e,t.querySelector("input"))}else if(window.targetCheck(e,"[data-action]")){let t=window.targetCheck(e,"[data-action]");switch(t=t.dataset.action,t){case"clear-form":let t=e.target.closest("form");this.store.delete(t.dataset.formId),t?.reset(),e.target.closest(".restore-form").hidden=!0;break;case"dismiss-restore":e.target.closest(".restore-form").hidden=!0}}}handleNumberClick(e,t){let r=0;if(e.target.closest(".increase")?r+=1:e.target.closest(".decrease")&&(r-=1),0!==r){let s=parseFloat(t.step);s=Math.max(s,1),e.ctrlKey&&e.shiftKey?s*=50:e.ctrlKey?s*=5:e.shiftKey&&(s*=10);let a=""===t.value?0:parseFloat(t.value);t.value=a+s*r,this.handleNumberLimits(t)}}handleNumberLimits(e){let[t,r,s,a]=[e.min,e.max,e.closest(".quantity")?.querySelector(".increase"),e.closest(".quantity")?.querySelector(".decrease")],i=parseFloat(e.value);i<t?(e.value=t,a.disabled=!0):i>r?(e.value=r,s.disabled=!1):s.disabled?s.disabled=!1:a.disabled&&(a.disabled=!1)}handleChange(e){if(e.target.closest("[data-ignore]"))return;const t=e.target,r=t.form||t.closest("form");if(!r)return;const s=this.forms?.get(r.dataset.formId);if(s&&(console.log(s.options),s.options.autosave||this.subscribers.size>0)){const e=s.dependencies.get(t.name);e&&e.forEach((e=>{this.checkFieldDependency(r,e.field,t.name,e.requiredValue,e.operator)}));const a=this.getDelayForField(t);this.scheduleSave(s,a)}}handleFocus(e){const t=e.target;t.matches("input, textarea, select")&&(this.currentFocus=t)}handleBlur(e){if(e.target.closest("[data-ignore]"))return;const t=e.target,r=t.form||t.closest("form");if(!r)return;const s=e.target.closest("input, textarea, select");if(s){const e=this.findFieldWrapper(s);if(e){const t=e.dataset.field;t&&(this.shouldDebounce(s)&&window.debouncer.cancel(`validate_${t}`),this.touchedFields.add(t)),this.validateField(s,e)}const a=this.forms?.get(r.dataset.formId);a&&this.scheduleSave(a,{type:"blur",fieldName:t.name,delay:1500})}}handleInput(e){if(e.target.closest("[data-ignore]")||!e.target.closest("form"))return;const t=e.target.closest("input, textarea, select");if(!t)return;let r=t.closest("form");this.showFormStatus(r.dataset.formId,"pending");const s=this.findFieldWrapper(t);if(!s)return;const a=s.dataset.field;a&&this.touchedFields.add(a),this.shouldDebounce(t)&&window.debouncer.schedule(`validate_${a}`,((e,t)=>this.validateField.bind(this)),500)}initValidators(){return{email:{pattern:/^[^\s@]+@[^\s@]+\.[^\s@]+$/,message:"Please enter a valid email address"},url:{pattern:/^https?:\/\/.+\..+/,message:"Please enter a valid URL starting with http:// or https://"},phone:{pattern:/^[\d\s\-\+\(\)\.]+$/,message:"Please enter a valid phone number"},number:{test:(e,t)=>{const r=parseFloat(e);if(isNaN(r))return"Please enter a valid number";const s=t.dataset.min,a=t.dataset.max;return void 0!==s&&r<parseFloat(s)?`Value must be at least ${s}`:!(void 0!==a&&r>parseFloat(a))||`Value must be at most ${a}`}},text:{test:(e,t)=>{const r=t.dataset.minlength,s=t.dataset.maxlength;return r&&e.length<parseInt(r)?`Must be at least ${r} characters`:!(s&&e.length>parseInt(s))||`Must be no more than ${s} characters`}}}}findFieldWrapper(e){let t=e.closest(".field");return t||(t=e.closest("[data-field]")),t}shouldDebounce(e){return["text","email","url","tel","search"].includes(e.type)||"TEXTAREA"===e.tagName}validateField(e,t){const r=this.getFieldValue(e),s=t.dataset.field;if(!this.touchedFields.has(s)&&!e.required)return!0;if(!r&&!e.required)return this.clearValidation(t),!0;if(e.required&&!r)return this.showError(t,"This field is required"),!1;if(e.checkValidity&&!e.checkValidity())return this.showError(t,e.validationMessage),!1;const a=t.dataset.pattern;if(a&&r){if(!new RegExp(a).test(r)){const e=t.dataset.validationMessage||"Invalid format";return this.showError(t,e),!1}}const i=t.dataset.validate||e.type;if(i&&this.validators[i]){const e=this.validators[i];if(e.pattern&&!e.pattern.test(r))return this.showError(t,e.message),!1;if(e.test){const s=e.test(r,t);if(!0!==s)return this.showError(t,s),!1}}return this.showSuccess(t),this.notify("field-validated",e),!0}getFieldValue(e){if(!e)return"";if("checkbox"===e.type)return e.checked?e.value||"1":"";if("radio"===e.type){const t=e.form?.querySelector(`[name="${e.name}"]:checked`);return t?t.value:""}return"select-multiple"===e.type?Array.from(e.selectedOptions).map((e=>e.value)):e.value?.trim()||""}showSuccess(e,t=""){if(!e)return;const r=e.querySelector(".validation-icon.success"),s=e.querySelector(".validation-icon.error"),a=e.querySelector(".validation-message"),i=e.querySelector("input, textarea, select");e.classList.remove("has-error"),i?.classList.remove("error"),e.classList.add("has-success"),r&&(r.hidden=!1),s&&(s.hidden=!0),a&&(""===t?(a.hidden=!0,a.textContent=""):(a.hidden=!1,a.textContent=t))}showError(e,t){if(!e)return;const r=e.querySelector(".validation-icon.success"),s=e.querySelector(".validation-icon.error"),a=e.querySelector(".validation-message"),i=e.querySelector("input, textarea, select");e.classList.remove("has-success"),e.classList.add("has-error"),i?.classList.add("error"),r&&(r.hidden=!0),s&&(s.hidden=!1),a&&(a.hidden=!1,a.textContent=t)}clearValidation(e){if(!e)return;const t=e.querySelector(".validation-icon"),r=e.querySelector(".validation-message"),s=e.querySelector("input, textarea, select");e.classList.remove("has-error","has-success"),s?.classList.remove("error"),t&&(t.hidden=!0),r&&(r.hidden=!0,r.textContent="")}validateAllFields(e){if(!e)return!0;const t=e.querySelectorAll(".field:not([hidden])");let r=!0;return t.forEach((e=>{if(this.isComplexFieldWrapper(e))return;const t=e.querySelector('input:not([type="hidden"]), textarea, select');if(t&&!t.closest("[hidden]")){const s=e.dataset.field;s&&this.touchedFields.add(s);this.validateField(t,e)||(r=!1,!1===r&&(t.scrollIntoView({behavior:"smooth",block:"center"}),t.focus()))}})),r}isComplexFieldWrapper(e){return e.classList.contains("repeater")||e.classList.contains("group")||e.classList.contains("upload")}attachRepeaterValidation(e){e.addEventListener("click",(t=>{t.target.closest(".add-repeater-row")&&setTimeout((()=>{e.querySelectorAll(".repeater-row").forEach((e=>{e.querySelectorAll("input, textarea, select").forEach((e=>{const t=this.findFieldWrapper(e);t&&this.clearValidation(t)}))}))}),100)}))}attachGroupValidation(e){e.addEventListener("change",(t=>{const r=t.target.closest("input, select");if(!r)return;const s=r.name;if(!s)return;e.querySelectorAll(`[data-show-if*="${s}"]`).forEach((e=>{e.hidden&&this.clearValidation(e)}))}))}resetForm(e){if(!e)return;this.touchedFields.clear();e.querySelectorAll(".field").forEach((e=>{this.clearValidation(e)}))}getFormErrors(e){const t={};return e.querySelectorAll(".field.has-error").forEach((e=>{const r=e.dataset.field,s=e.querySelector(".validation-message");r&&s&&(t[r]=s.textContent)})),t}addValidator(e,t){this.validators[e]=t}getDelayForField(e){return"text"===e.type||"textarea"===e.type?this.autoSaveDefaults.typingDelay:["checkbox","radio","select-one","select-multiple"].includes(e.type)?1e3:this.autoSaveDefaults.delay}scheduleSave(e,t=this.autoSaveDefaults.delay){if(!e.options.autosave)return;document.addEventListener("input",this.saveCheck,{passive:!0});const r=`autosave_${e.id}`;this.debouncer.schedule(r,(()=>this.autosave(e)),t)}saveCheck(e){let t=e.target.closest("form[data-id]");t&&this.scheduleSave(this.forms.get(t.dataset.id))}async autosave(e){const t=this.collectFormData(e.element);this.showFormStatus(e.id,"saving"),await this.store.save({formId:e.id,data:t,status:"draft",timestamp:Date.now()}).then((()=>{this.showFormStatus(e.id,"autosaved")}));const r=this.getChangedFields(e.data,t);if(console.log("Changes:",r),0!==Object.keys(r).length){console.log("Continuing on:"),e.data=t,this.forms.set(e.id,e),document.removeEventListener("input",this.handleInput);for(let[e,s]of Object.entries(t))"object"==typeof s&&(r[e]=s);this.notify("form-autosave",{formId:e.id,changes:r,fullData:t,config:e})}}hasUnsavedChanges(e){const t=this.forms.get(e);if(!t)return!1;if(t.operations?.size>0)return!0;const r=this.collectFormData(t.element),s=this.getChangedFields(t.data,r);return Object.keys(s).length>0}showFormStatus(e,t,r=""){let s=this.forms.get(e);if(!s.options.formStatus)return;if(s.status===t)return;s.status=t,console.log("Setting status: ",t);const a=s.element.querySelector(".fstatus");a.hidden=!1;const i=a.querySelector(".message");i.textContent="",a.querySelector(".icon")?.remove();const n={saving:"Saving changes...",autosaved:"Changes saved locally. Submit form to send to server.",uploading:"Uploading your form to server",submitted:"Successfully sent to server",pending:"Unsaved changes",error:"Failed to save changes. Refresh and try again?",offline:"Changes will be saved when online"};let o=window.getIcon({autosaved:"check-circle",submitted:"check-circle",error:"close-circle",offline:"cloud-slash",pending:"exclamation-mark"}[t]);o&&a.prepend(o),""===r&&(r=n[t]||t),i.textContent=r,a.classList.toggle("loading",["uploading","saving"].includes(t)),"submitted"===t&&setTimeout((()=>a.hidden=!0),3e3)}cleanupSpecialFields(){this.specialFields.forEach((e=>{if("quill"===e.type&&e.instance){const t=e.instance.container.previousSibling;t?.classList.contains("ql-toolbar")&&t.remove()}})),this.uploader?.destroy(),this.specialFields.clear()}collectFormData(e){if(Object.hasOwn(e.dataset,"timeline"))return this.collectTimeline(e);const t=new FormData(e);let r={};const s={},a={};for(let[i,n]of t.entries()){if(this.ignore.includes(i)||i.endsWith("_temp"))continue;this.getFieldProcessor(i)(i,n,r,s,a,e)}return window.isEmptyObject(a)?this.mergeRepeaterData(r,s):(r=this.mergeRepeaterData(r,s),this.mergePostData(r,a))}collectTimeline(e){console.log("Collecting Timeline data:");let t={},r={},s=[],a=new FormData(e);for(const[i,n]of a.entries()){if(this.ignore.includes(i)||i.endsWith("_temp"))continue;const a=i.match(/^\[(\d+)\](.+)$/);if(a){const[,t,i]=a;r[t]||(r[t]={id:parseInt(t)},s.push(t));this.getFieldProcessor(i)(i,n,r[t],{},{},e)}else{this.getFieldProcessor(i)(i,n,t,{},{},e)}}return t.timeline=s.map((e=>r[e])),delete t["form-id"],delete t.sendAll,delete t.timeline_temp,delete t[""],console.log("Data: ",t),t}getFieldProcessor(e){return e.includes("|")?this.processTableField:e.includes("::")?this.processGroupField:e.includes(":")?this.processRepeaterField:/\[[^\]]+\]/.test(e)?this.processLocationField:this.processRegularField}mergeRepeaterData(e,t){return Object.keys(t).forEach((r=>{const s={};Object.keys(t[r]).forEach((e=>{const a=t[r][e];Object.keys(a).length>0&&(s[e]=a)})),e[r]=Object.values(s)})),e}mergePostData(e,t){for(let[t,r]in Object.entries(r))e[t]=r;return e}processTableField(e,t,r,s,a,i){let[n,o]=e.split("|");!n in a&&(a[n]={});this.getFieldProcessor(o)(o,t,a,s,a,i)}processRepeaterField(e,t,r,s,a,i){let[n,o,l]=e.split(":");const c=l.endsWith("[]");l=l.replace("[]",""),s[n]||(s[n]={}),s[n][o]||(s[n][o]={}),c||s[n][o][l]?(s[n][o][l]?Array.isArray(s[n][o][l])||(s[n][o][l]=[s[n][o][l]]):s[n][o][l]=[],s[n][o][l].push(t)):s[n][o][l]=t}processGroupField(e,t,r,s,a,i){const n=e.split("::"),o=n[0];r[o]||(r[o]={});let l=r[o];for(let e=1;e<n.length-1;e++){const t=n[e];l[t]||(l[t]={}),l=l[t]}const c=n[n.length-1];void 0!==l[c]?(Array.isArray(l[c])||(l[c]=[l[c]]),l[c].push(t)):l[c]=t}processLocationField(e,t,r,s,a,i){let[n,o]=e.split("[");o=o.replace("]",""),Object.hasOwn(r,n)||(r[n]={},Object.hasOwn(r,"sendAll")?r.sendAll.includes(n)||r.sendAll.push(n):r.sendAll=[n]),r[n][o]=t}processRegularField(e,t,r,s,a,i){r[e=e.replace("[]","")]?(Array.isArray(r[e])||(r[e]=[r[e]]),r[e].push(t)):r[e]=t}getFieldValue(e){if(!e)return"";if("checkbox"===e.type)return e.checked?e.value||"1":"";if("radio"===e.type){const t=e.form.querySelector(`[name="${e.name}"]:checked`);return t?t.value:""}return"select-multiple"===e.type?Array.from(e.selectedOptions).map((e=>e.value)):e.value}getChangedFields(e,t){return window.getDifferences?.map(e,t)||{}}showSummary(e,t="form"){const r=this.forms.get(e);if(!r)return;const s=r.element||document.querySelector(`[data-form-id="${e}"]`),a=window.getTemplate("formSummary"),[i,n,o]=[a.querySelector("h2"),a.querySelector(".summary"),a.querySelector(".result")],l=["sendAll",...this.ignore];for(const[e,t]of Object.entries(r.data)){if(l.includes(e)||this.isEmptyValue(t))continue;const r=this.getFieldInfo(s,e);if(!r.label)continue;const a=this.createResultElement(o,r,t,s);a&&n.appendChild(a)}o.remove(),(t="form"!==t?s.closest(t)??s:s).after(a),window.fade(t,!1)}isEmptyValue(e){return null==e||""===e||(!(!Array.isArray(e)||0!==e.length)||"object"==typeof e&&0===Object.keys(e).length)}getFieldInfo(e,t){let r=e.querySelector(`label[for="${t}"]`),s=null,a=null;if(s||(s=e.querySelector(`[name="${t}"]`)),s||(s=e.querySelector(`[name="${t}[]"]`)),!s){const a=e.querySelector(`fieldset[data-field="${t}"]`);a&&(r=a.querySelector("legend"),s=a.querySelector("input, select, textarea"))}if(!r&&s){const e=s.closest(".field, fieldset");e&&(r=e.querySelector("label, legend"))}a=e.querySelector(`.field[data-field="${t}"], fieldset[data-field="${t}"]`);let i="text";return a?.dataset.type?i=a.dataset.type:s&&(i="checkbox"===s.type&&s.name.endsWith("[]")?"checkbox":"checkbox"===s.type?"true_false":"SELECT"===s.tagName&&s.multiple?"select":s.type||"text"),{label:r?.textContent.replace("*","").trim()||null,type:i,wrapper:a,input:s}}createResultElement(e,t,r,s){const a=e.cloneNode(!0),i=a.querySelector("h4"),n=a.querySelector("p");i.textContent=t.label;const o=this.formatFieldValue(r,t.type,s);return this.isHtmlContent(o)?n.innerHTML=o:n.textContent=o,a}isHtmlContent(e){return"string"==typeof e&&(e.includes("<br>")||e.includes("<p>")||e.includes("<ul>")||e.includes("<ol>")||e.includes("<a ")||e.includes("<strong>")||e.includes("<em>")||e.includes("<div"))}formatFieldValue(e,t,r){switch(t){case"textarea":case"wysiwyg":return this.formatTextareaValue(e,t);case"true_false":return"1"===e||1===e||!0===e?"Yes":"No";case"checkbox":return Array.isArray(e)?this.formatArrayValue(e):"1"===e||1===e||!0===e?"Yes":"No";case"select":return Array.isArray(e)?this.formatArrayValue(e):this.getSelectLabel(e,r,t);case"date":case"datetime":case"time":return window.formatDate?window.formatDate(e):e;case"radio":return this.getSelectLabel(e,r,t);case"repeater":return this.formatRepeaterValue(e);case"group":return this.formatGroupValue(e);case"location":return this.formatLocationValue(e);case"file":case"image":return this.formatFileValue(e);case"number":return this.formatNumber(e);case"email":return`<a href="mailto:${e}">${e}</a>`;case"url":return`<a href="${e}" target="_blank" rel="noopener">${e}</a>`;case"phone":return`<a href="tel:${e.replace(/\D/g,"")}">${e}</a>`;default:return Array.isArray(e)?this.formatArrayValue(e):e}}formatRepeaterValue(e){if(!Array.isArray(e)||0===e.length)return"<em>No entries</em>";let t='<div class="repeater-summary">';return e.forEach(((e,r)=>{t+='<div class="repeater-row">',t+=`<strong>Entry ${r+1}:</strong><ul>`;for(const[r,s]of Object.entries(e))if(!this.isEmptyValue(s)){const e=r.replace(/_/g," ").replace(/\b\w/g,(e=>e.toUpperCase()));t+=`<li><strong>${e}:</strong> ${s}</li>`}t+="</ul></div>"})),t+="</div>",t}formatGroupValue(e){if("object"!=typeof e||0===Object.keys(e).length)return"<em>No data</em>";let t='<div class="group-summary"><ul>';for(const[r,s]of Object.entries(e))if(!this.isEmptyValue(s)){const e=r.replace(/_/g," ").replace(/\b\w/g,(e=>e.toUpperCase()));"object"!=typeof s||Array.isArray(s)?t+=`<li><strong>${e}:</strong> ${s}</li>`:t+=`<li><strong>${e}:</strong> ${this.formatGroupValue(s)}</li>`}return t+="</ul></div>",t}formatLocationValue(e){if("object"!=typeof e)return e;const t=[];return["address","city","state","zip","country"].forEach((r=>{e[r]&&t.push(e[r])})),t.join(", ")}formatFileValue(e){return"string"==typeof e?e.startsWith("http")?`<a href="${e}" target="_blank">View file</a>`:e:Array.isArray(e)?e.map((e=>"string"==typeof e?`<a href="${e}" target="_blank">View file</a>`:e.name||"File")).join(", "):"File uploaded"}formatNumber(e){const t=parseFloat(e);return isNaN(t)?e:e.toString().includes(".")&&2===e.toString().split(".")[1].length?new Intl.NumberFormat("en-CA",{style:"currency",currency:"USD"}).format(t):new Intl.NumberFormat("en-CA").format(t)}formatArrayValue(e,t=null,r=null){if(0===e.length)return"<em>None selected</em>";if(t&&r&&r.input){return"<ul><li>"+e.map((e=>this.getSelectLabel(e,t,r.type))).join("</li><li>")+"</li></ul>"}return"<ul><li>"+e.join("</li><li>")+"</li></ul>"}getSelectLabel(e,t,r){if("select"===r){const r=t.querySelector(`option[value="${e}"]`);return r?.textContent||e}if("radio"===r){const r=t.querySelector(`input[type="radio"][value="${e}"]`),s=r?.nextElementSibling;return s?.textContent||e}if("checkbox"===r){const r=t.querySelector(`input[type="checkbox"][value="${e}"]`);if(r){const e=t.querySelector(`label[for="${r.id}"]`);if(e)return e.textContent.trim();const s=r.nextElementSibling;if("LABEL"===s?.tagName)return s.textContent.trim()}}return e}formatTextareaValue(e,t){return e?"wysiwyg"===t||this.containsHtml(e)?e:this.formatPlainText(e):"<em>Empty</em>"}containsHtml(e){return/<(p|strong|em|u|s|ol|ul|li|blockquote|h[1-6]|a|br|span)\b[^>]*>/i.test(e)}formatPlainText(e){if(!e)return"";const t=(e=e.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;")).split(/\n\n+/);return t.length>1?t.map((e=>`<p>${e.replace(/\n/g,"<br>")}</p>`)).join(""):e.replace(/\n/g,"<br>")}nl2br(e){return this.formatPlainText(e)}subscribe(e){return this.subscribers.add(e),()=>this.subscribers.delete(e)}notify(e,t){this.subscribers.forEach((r=>r(e,t)))}cleanupForm(e){const t=this.forms.get(e);t&&(this.hasUnsavedChanges(e)&&this.autosave(t),this.cleanupSpecialFields(),this.forms.delete(e))}destroy(){this.globalHandlersAdded&&(document.removeEventListener("change",this.changeHandler),document.removeEventListener("focus",this.focusHandler,!0),document.removeEventListener("blur",this.blurHandler,!0),document.removeEventListener("input",this.inputHandler,!0)),this.forms.forEach((e=>{let t=e.element;t&&t.removeEventListener("submit",this.submitHandler)})),this.specialFields.clear(),this.forms.clear(),this.activeRepeaters.clear(),this.forms&&this.forms.clear()}}document.addEventListener("DOMContentLoaded",(()=>{window.jvbForm=e}))})();
\ No newline at end of file
diff --git a/assets/js/min/gallery.min.js b/assets/js/min/gallery.min.js
index 3499cd1..6f7511b 100644
--- a/assets/js/min/gallery.min.js
+++ b/assets/js/min/gallery.min.js
@@ -1 +1 @@
-window.jvbGallery=class{constructor(e,t){this.imageWrapper=t.imageWrapper??null,this.container=t.container?document.querySelector(t.container):document.querySelector("main"),this.modal=new window.jvbModal(e,{onOpen:!1,onSave:!1,onClose:()=>this.closeGallery(!1)}),this.items=this.getGalleryItems()||[],this.loadMore=t.loadMore??!1,this.modalElement="string"==typeof e?document.querySelector(e)??!1:e,this.modal&&(this.a11y=window.jvbA11y,this.openWhenReady=!1,this.initElements(),this.imageWrapper="string"==typeof this.imageWrapper?this.imageWrapper:"."+this.imageWrapper.classList.join("."),this.index=0,this.swipe={touchStart:null,touchEnd:null,minSwipe:50},this.isLoading=!1)}initElements(){this.prevBtn=this.modalElement.querySelector(".prev"),this.nextBtn=this.modalElement.querySelector(".next"),this.favourite=this.modalElement.querySelector(".favourite"),this.image=this.modalElement.querySelector(".image"),this.counter=this.modalElement.querySelector(".counter"),this.extra=this.modalElement.querySelector(".item-info"),this.imageWrapper||(this.favourite.remove(),this.extra.remove())}getGalleryItems(){let e=this.imageWrapper?this.imageWrapper:"img";return Array.from(this.container.querySelectorAll(e)).map((e=>{const t=this.imageWrapper?e.querySelector("img"):e;return t?{id:this.imageWrapper?e.querySelector("button.favourite").dataset.id:"",small:t.dataset.small||t.src,large:t.dataset.large||t.src,full:t.dataset.full||t.src,alt:t.alt||"",fav:this.imageWrapper?e.querySelector("button.favourite")?.cloneNode(!0):"",info:this.imageWrapper?e.querySelector(".item-info")?.cloneNode(!0):""}:null})).filter(Boolean)}initListeners(){this.container.addEventListener("click",this.handleClick.bind(this)),document.addEventListener("keydown",this.handleKeys.bind(this)),document.addEventListener("touchstart",this.handleTouchStart.bind(this)),document.addEventListener("touchend",this.handleTouchEnd.bind(this)),document.addEventListener("touchmove",this.handleTouchMove.bind(this))}destroyListeners(){this.container.removeEventListener("click",this.handleClick.bind(this)),document.removeEventListener("keydown",this.handleKeys.bind(this)),document.removeEventListener("touchstart",this.handleTouchStart.bind(this)),document.removeEventListener("touchend",this.handleTouchEnd.bind(this)),document.removeEventListener("touchmove",this.handleTouchMove.bind(this))}handleKeys(e){switch(e.key){case"ArrowLeft":this.navigate(-1);break;case"ArrowRight":this.navigate(1)}}handleTouchStart(e){this.swipe.touchStart=e.touches[0].clientX}handleTouchMove(e){this.swipe.touchEnd=e.touches[0].clientX}handleTouchEnd(e){if(!this.swipe.touchStart||!this.swipe.touchEnd)return;const t=this.swipe.touchStart-this.swipe.touchEnd,i=t>this.swipe.minSwipe,s=t<-this.swipe.minSwipe;i?this.navigate(1):s&&this.navigate(-1),this.swipe.touchStart=null,this.swipe.touchEnd=null}handleClick(e){window.targetCheck(e,".feed-images")&&this.handleGalleryOpen(e),window.targetCheck(e,".nav")&&(window.targetCheck(e,".next")?this.navigate(1):this.navigate(-1)),window.targetCheck(e,"button.cancel")&&this.closeGallery()}async navigate(e){let t=this.index+e;(t<0||t>=this.items.length)&&(this.a11y.announceNavigation(t,this.items.length,e<0,e>0),t=t<0?this.items.length-1:0),this.index=t,this.updateDisplay(),this.preloadImages(),this.a11y.announceNavigation(this.index,this.items.length),"function"==typeof this.loadMore&&e>0&&t>=this.items.length-3&&window.feedBlock.hasMore&&(await this.loadMore(),this.updateGalleryItems(this.getGalleryItems()))}updateGalleryItems(e){const t=this.items[this.index];if(this.items=e,t){const e=this.items.findIndex((e=>e.full===t.full||e.large===t.large));-1!==e&&(this.index=e)}if(this.updateNavigationButtons(),this.items.length>0&&this.openWhenReady){let e=this.findIndex("id",this.openWhenReady);this.openGallery(e)}}preloadImages(){[-1,0,1].forEach((e=>{const t=this.index+e;if(t>=0&&t<this.items.length){const e=new Image,i=this.items[t];window.innerWidth<1e3?e.src=i.large||i.src:e.src=i.full||i.src}}))}updateDisplay(){const e=this.items[this.index];e&&([this.image.src,this.image.alt,this.counter.textContent]=[window.innerWidth<1e3?e.large||e.src:e.full||e.src,e.alt||"",`${this.index+1} / ${this.items.length}`],this.imageWrapper&&(e.fav&&(window.removeChildren(this.favourite),this.favourite.appendChild(e.fav.cloneNode(!0))),e.info&&(window.removeChildren(this.extra),this.extra.appendChild(e.info.cloneNode(!0)))),this.updateNavigationButtons())}updateNavigationButtons(){this.prevBtn.classList.toggle("end",this.index>0?"":"none"),this.nextBtn.classList.toggle("end",this.index<this.items.length-1?"":"none")}handleGalleryOpen(e){let t=this.imageWrapper?e.target.closest(this.imageWrapper):e.target.closest("img"),i=this.imageWrapper?"id":"small",s=t.dataset[i],n=this.findIndex(i,s);this.openGallery(n)}findIndex(e,t){return this.items.findIndex((i=>i[e]===t))}openGallery(e){this.initListeners(),this.index=e,this.modal.handleOpen(),this.updateDisplay(),this.preloadImages(),this.a11y.announce(`Image ${this.index+1} of ${this.items.length}. Use arrow keys to navigate.`)}closeGallery(e=!0){this.destroyListeners(),e&&this.modal.handleClose()}};
\ No newline at end of file
+(()=>{class e{constructor(e,t={}){this.container=t.container?document.querySelector(t.container):document.querySelector("main"),this.gallerySelector=t.gallerySelector||"img[data-small]",this.modal=new window.jvbModal(e,{onOpen:!1,onSave:!1,onClose:()=>this.closeGallery(!1)}),this.modalElement="string"==typeof e?document.querySelector(e):e,this.modalElement&&(this.a11y=window.jvbA11y,this.index=0,this.items=[],this.swipe={touchStart:null,touchEnd:null,minSwipe:50},this.initElements(),this.initListeners())}initElements(){this.prevBtn=this.modalElement.querySelector(".prev"),this.nextBtn=this.modalElement.querySelector(".next"),this.image=this.modalElement.querySelector(".image"),this.counter=this.modalElement.querySelector(".counter")}initListeners(){this.container.addEventListener("click",this.handleClick.bind(this)),this.boundKeyHandler=this.handleKeys.bind(this),this.boundTouchStart=this.handleTouchStart.bind(this),this.boundTouchMove=this.handleTouchMove.bind(this),this.boundTouchEnd=this.handleTouchEnd.bind(this)}handleClick(e){const t=e.target.closest(this.gallerySelector);if(t&&!this.modal.isOpen)return e.preventDefault(),void this.openGallery(t);this.modal.isOpen&&(e.target.closest(".next")?this.navigate(1):e.target.closest(".prev")&&this.navigate(-1))}handleKeys(e){if(this.modal.isOpen)switch(e.key){case"ArrowLeft":e.preventDefault(),this.navigate(-1);break;case"ArrowRight":e.preventDefault(),this.navigate(1)}}handleTouchStart(e){this.modal.isOpen&&(this.swipe.touchStart=e.touches[0].clientX)}handleTouchMove(e){this.modal.isOpen&&(this.swipe.touchEnd=e.touches[0].clientX)}handleTouchEnd(e){if(!this.modal.isOpen||!this.swipe.touchStart||!this.swipe.touchEnd)return;const t=this.swipe.touchStart-this.swipe.touchEnd;Math.abs(t)>this.swipe.minSwipe&&this.navigate(t>0?1:-1),this.swipe.touchStart=null,this.swipe.touchEnd=null}buildGalleryItems(){return Array.from(this.container.querySelectorAll(this.gallerySelector)).map(((e,t)=>({id:e.dataset.id||t,small:e.dataset.small||e.src,medium:e.dataset.medium||e.src,full:e.dataset.full||e.src,alt:e.alt||"",element:e})))}openGallery(e){this.items=this.buildGalleryItems(),this.index=this.items.findIndex((t=>t.element===e)),-1===this.index&&(this.index=0),document.addEventListener("keydown",this.boundKeyHandler),document.addEventListener("touchstart",this.boundTouchStart,{passive:!0}),document.addEventListener("touchmove",this.boundTouchMove,{passive:!0}),document.addEventListener("touchend",this.boundTouchEnd,{passive:!0}),this.modal.handleOpen(),this.updateDisplay(),this.preloadAdjacent(),this.a11y.announce(`Gallery opened. Image ${this.index+1} of ${this.items.length}. Use arrow keys to navigate.`)}closeGallery(e=!0){document.removeEventListener("keydown",this.boundKeyHandler),document.removeEventListener("touchstart",this.boundTouchStart),document.removeEventListener("touchmove",this.boundTouchMove),document.removeEventListener("touchend",this.boundTouchEnd),e&&this.modal.handleClose()}navigate(e){let t=this.index+e;t<0?t=this.items.length-1:t>=this.items.length&&(t=0),this.index=t,this.updateDisplay(),this.preloadAdjacent(),this.a11y.announce(`Image ${this.index+1} of ${this.items.length}`)}updateDisplay(){const e=this.items[this.index];if(!e)return;const t=window.innerWidth<1e3?e.medium||e.src:e.full||e.src;this.image.src=t,this.image.alt=e.alt,this.counter.textContent=`${this.index+1} / ${this.items.length}`,this.prevBtn.classList.toggle("disabled",this.items.length<=1),this.nextBtn.classList.toggle("disabled",this.items.length<=1)}preloadAdjacent(){[-1,1].forEach((e=>{const t=this.index+e;if(t>=0&&t<this.items.length){const e=this.items[t];(new Image).src=window.innerWidth<1e3?e.medium||e.src:e.full||e.src}}))}cleanup(){this.container.removeEventListener("click",this.handleClick),this.closeGallery(!1)}}document.addEventListener("DOMContentLoaded",(function(){let t=document.querySelectorAll("dialog.gallery");t.length>0&&(window.galleries=new Map,t.forEach((t=>{let i=t.id;window.galleries.set(i,new e(t))})))}))})();
\ No newline at end of file
diff --git a/assets/js/min/media.min.js b/assets/js/min/media.min.js
index e69de29..84dab73 100644
--- a/assets/js/min/media.min.js
+++ b/assets/js/min/media.min.js
@@ -0,0 +1 @@
+(()=>{class e{constructor(){this.currentWidth=window.innerWidth,this.images=document.querySelectorAll(".wp-site-blocks img[data-small]"),0!==this.images.length&&(this.loadVisibleImages(),this.initListeners())}loadVisibleImages(){this.images.forEach(((e,t)=>{const i=e.getBoundingClientRect(),s=i.top<window.innerHeight&&i.bottom>0;(0===t||s)&&(this.loadAppropriateImage(e),e.dataset.loaded="true")}))}initListeners(){this.resizeHandler=this.handleResize.bind(this),window.addEventListener("resize",this.resizeHandler),this.observer=new IntersectionObserver((e=>{e.forEach((e=>{e.isIntersecting&&(this.loadAppropriateImage(e.target),this.observer.unobserve(e.target))}))}),{rootMargin:"50px",threshold:.1}),this.images.forEach((e=>{e.dataset.loaded||this.observer.observe(e)}))}handleResize(){window.debouncer.schedule("image-resize",(()=>{const e=window.innerWidth;Math.abs(e-this.currentWidth)>100&&(this.currentWidth=e,this.updateVisibleImages())}),150)}updateVisibleImages(){this.images.forEach((e=>{const t=e.getBoundingClientRect();t.top<window.innerHeight&&t.bottom>0&&this.loadAppropriateImage(e,!0)}))}loadAppropriateImage(e,t=!1){const i=this.getTargetSize(),s=e.dataset[i];s&&(t||s!==e.currentSrc)&&(e.src=s)}getTargetSize(){return this.currentWidth<768?"small":this.currentWidth<1200?"medium":"full"}cleanup(){this.observer?.disconnect(),window.removeEventListener("resize",this.resizeHandler)}}window.isLoaded=!1,document.addEventListener("readystatechange",(()=>{!window.isLoaded&&document.querySelector(".wp-site-blocks img")&&(window.jvbMedia=new e,window.isLoaded=!0)}))})();
\ No newline at end of file
diff --git a/assets/js/min/news.min.js b/assets/js/min/news.min.js
index db1b065..272456b 100644
--- a/assets/js/min/news.min.js
+++ b/assets/js/min/news.min.js
@@ -1 +1 @@
-window.newsManager=class{constructor(){this.queue=window.jvbQueue,this.loading=window.jvbLoading,this.cache=window.jvbCache,this.a11y=window.jvbA11y,this.error=window.jvbError,this.activeTab="all",this.tabs=new window.jvbTabs(document.querySelector(".replace"),{news:()=>{this.activeTab="all",this.resetFilters(),this.loadItems(!0).then((()=>{}))},mine:()=>{console.log("switching to mine tab"),this.activeTab="own",this.resetFilters(),this.filters.artist=jvbSettings.currentUser,this.loadItems(!0).then((()=>{}))},watching:()=>{this.activeTab="watching",this.resetFilters(),this.filters.watched=!0,this.loadItems(!0).then((()=>{}))}}),this.isLoading=!1,this.alreadyHandling=!1,this.template=new Map,this.endpoints={news:"news",vote:"news/vote"},this.resetFilters(),this.state={hasMore:!0,pages:1,items:0},this.initElements(),this.initEvents(),this.loadItems()}resetFilters(){this.filters={page:1,order:"DESC",orderby:"date",shop:null,type:null,artist:null,watched:!1}}initElements(){this.container=document.querySelector(".replace"),this.grid=this.container.querySelector(".item-grid"),this.addButton=this.container.querySelector(".add-item-btn"),this.addModal=new window.jvbModal(this.container.querySelector(".create-modal"),{render:this.renderModal.bind(this),open:this.addButton,content:"news",openMessage:"Opened modal to create a news post.",onSave:this.saveModal.bind(this)}),this.filterForm=this.container.querySelector("form"),this.dateRangeFilter=new window.jvbModal(this.container.querySelector("dialog.date-range"),{open:!1}),this.clearFilters=this.container.querySelector(".clear-filters"),this.replyModal=new window.jvbModal(this.container.querySelector(".create-response"),{open:!1,content:"response",openMessage:"Opened Response modal",onSave:this.saveCreatedResponse.bind(this)})}initEvents(){this.filterForm.addEventListener("change",(e=>{let t=e.target.value;if(!e.target.closest(".date-range"))if("custom"===t)this.handleCustomDateRange();else{let s=e.target.name;s?this.filters[s]=t:this.resetFilters(),this.loadItems(!0)}})),document.addEventListener("click",(e=>{if(e.target===this.clearFilters&&(this.filterForm.reset(),this.resetFilters(),this.loadItems(!0)),e.target.closest("button.reply")){let t=e.target.closest("button"),s=t.closest(".item").dataset.id,n="";"news"===t.dataset.type?n=t.closest(".item").querySelector(".item-info").innerHTML:(n=t.closest(".response").querySelector(".content").innerHTML,this.replyModal.modal.dataset.parent_id=t.id.replace("reply-to","")),this.replyModal.modal.dataset.id=s,this.replyModal.modal.dataset.type=t.dataset.type,this.replyModal.modal.querySelector(".original").innerHTML="<h5>Replying to:</h5>"+n,this.replyModal.handleOpen()}}))}renderModal(){}handleCustomDateRange(){this.dateRangeFilter.handleOpen();let e=this.dateRangeFilter.modal.querySelector("input.date-start"),t=this.dateRangeFilter.modal.querySelector("input.date-end"),s=this.dateRangeFilter.modal.querySelector("select");this.dateRangeFilter.modal.querySelectorAll("input, select").forEach((n=>{n.addEventListener("change",(i=>{n===e&&""!==t.value||n===t&&""!==e.value?(this.filters.dateFrom=e.value,this.filters.dateTo=t.value,this.dateRangeFilter.handleClose(),this.loadItems(!0)):n===s&&(this.filters.customDate=s.value,this.dateRangeFilter.handleClose(),this.loadItems(!0))}))}))}async saveModal(e){const t=new FormData(this.addModal.modal.querySelector("form"));t.append("user",jvbSettings.currentUser),this.queue.addToQueue({type:"new_news",data:t})}async loadItems(e=!0){if(!this.isLoading)try{this.isLoading=!0,this.loading.show(),e&&(this.filters.page=1,removeChildren(this.grid),this.grid.classList.remove("empty"));const t=this.buildFilters(),s=await this.cache.fetchWithCache(`${jvbSettings.api}${this.endpoints.news}?${t.toString()}`,{method:"GET",headers:{"X-WP-Nonce":jvbSettings.nonce,action_nonce:jvbSettings.dash}},{context:"news",forceRefresh:!0});return this.renderItems(s.items||[],this.filters.page>1),s.pagination&&(this.state={hasMore:s.has_more,items:s.items,pages:s.pages}),s}catch(e){throw this.handleError(e,"loading news"),e}finally{this.isLoading=!1,this.loading.hide()}}buildFilters(){const e=JSON.parse(JSON.stringify(this.filters));let t={};for(var[s,n]of Object.entries(e))!1!==n&&null!==n&&(t[s]=n);return new URLSearchParams(t)}renderItems(e,t=!1){if(t||removeChildren(this.grid),0===e.length)return this.a11y.announceItems(0,t),void this.showEmptyState();const s=document.createDocumentFragment(),n=i=>{const a=Math.min(i+10,e.length);for(let t=i;t<a;t++){const n=e[t],i=this.createItemElement(n);s.appendChild(i)}a<e.length?requestAnimationFrame((()=>{n(a)})):(this.grid.appendChild(s),this.a11y.makeNavigable(this.grid.querySelectorAll(".item:not([data-keyboard-nav])")),this.a11y.announceItems(e.length,t,this.state.hasMore))};e.length>0?n(0):this.a11y.announceItems(0,t)}createItemElement(e){const t=window.getTemplate(`template-${this.activeTab}`);t.id=`news-${e.id}`,t.dataset.id=e.id;const[s]=t.getElementsByTagName("h3"),[n]=t.getElementsByClassName("published"),[i]=t.getElementsByClassName("artist"),[a]=t.getElementsByClassName("shop"),[r]=t.getElementsByClassName("tldr"),[o]=t.getElementsByClassName("item-info"),[l]=t.getElementsByClassName("image");[s.textContent,n.textContent,i.href,i.textContent,r.textContent,o.innerHTML]=[e.title,formatTimeAgo(e.date),e.artist.url,e.artist.name,e.tldr,e.post_content],e.shop?[a.href,a.innerHTML]=[e.shop.url,jvbSettings.icons.shop+e.shop.name]:a.hidden=!0;const[d]=t.getElementsByClassName("favourite-button");if("own"!==this.activeTab)[d.dataset.id,d.dataset.artist]=[e.id,e.artist.id],window.userFavourites.news?.includes(parseInt(e.id))?(removeChildren(d),d.append(getIcon("star-filled"))):(removeChildren(d),d.append(getIcon("star")));else{d.hidden=!0;const[s]=t.getElementsByClassName("select-checkbox"),[n]=t.getElementsByTagName("label");[s.id,s.value,n.for]=[`item-${e.id}`,e.id,`item-${e.id}`]}let h="";window.userVotes?.news?.has(e.id)&&(h=window.userVotes.news.get(e.id)),console.log(e),t.querySelector(".summary").appendChild(formatVote(e,h));let c=window.getTemplate("commentsButton");c.href=`#responses-to-${e.id}`,c.querySelector(".count").textContent=e.comments.items.length;let m=window.getTemplate("responses");m.id=`responses-to-${e.id}`,m.querySelector("summary").textContent+=" { "+e.comments.items.length+" }";let p=window.getTemplate("replyButton");return p.id="reply-to-"+e.id,p.dataset.type="news",p.dataset.action="reply",t.appendChild(p),e.comments.items.length>0&&e.comments.items.forEach((e=>{m.appendChild(this.formatComment(e))})),t.appendChild(m),t.querySelector(".vote").prepend(c,t.querySelector(".vote button")),e.image&&e.image.replace(/src="([^"]+)"/,'data-src="$1"'),t}formatComment(e,t=null){let s=window.getTemplate("response");s.id="response-"+e.id;let n=s.querySelector("summary");n.querySelector(".content").innerHTML=e.response,n.querySelector(".created").textContent=formatTimeAgo(e.created_at);let i=checkVoteStatus("response",e.id);e.content="response",s.querySelector(".footer").appendChild(formatVote(e,i)),console.log(e);let a=window.getTemplate("replyButton");a.id="reply-to-"+e.id,t&&(a.dataset.parent_id=t),a.dataset.action="reply",a.dataset.type=e.content,n.querySelector(".vote").prepend(a,n.querySelector(".vote").firstElementChild);let r=n.querySelector(".artist"),o=n.querySelector(".shop");if(console.log(e),e.artist?(e.artist.shop||o.remove(),[r.href,r.textContent,o.href,o.textContent]=[e.artist.url,e.artist.name,e.artist.shop.url,e.artist.shop.name]):(r.remove(),o.remove()),e.children.items.length>0){let t=window.getTemplate("responses");t.id="replies-to-"+e.id,t.querySelector("summary").textContent="See Responses {"+e.children.items.length+"}",e.children.items.forEach((s=>{t.appendChild(this.formatComment(s,e.id))})),s.appendChild(t)}return s}renderResponseCreate(){}saveCreatedResponse(){console.log("Saving create response"),console.log(this.replyModal.modal.id);const e=this.replyModal.modal;let t={user:jvbSettings.currentUser,item_id:e.dataset.id,response:e.querySelector(".ql-editor").innerHTML,content:e.dataset.type,action:"create"};e.dataset.parent_id&&(t.parent_id=e.dataset.parent_id),console.log(t),this.queue.addToQueue({type:"new_response",data:t})}showEmptyState(){const e=document.createElement("div");e.className="no-news",e.innerHTML="\n            <h3>Nothing here</h3>\n            <p>No updates here.</p>\n            <p>Add some gap fillers from the main favourites tab.</p>\n        ",this.grid.appendChild(e),this.grid.classList.add("empty"),this.a11y.announce("No favourites to show!")}hideEmptyState(){let e=this.grid.querySelector(".no-news");e&&e.remove()}handleError(e,t){console.error(`News error (${t}):`,e),window.jvbError&&window.jvbError.log(e,{component:"NewsManager",action:t}),window.jvbA11y&&window.jvbA11y.announce(`Error ${t}. ${e.message||"Please try again."}`)}};
\ No newline at end of file
+window.newsManager=class{constructor(){this.queue=window.jvbQueue,this.loading=window.jvbLoading,this.cache=window.jvbCache,this.a11y=window.jvbA11y,this.error=window.jvbError,this.activeTab="all",this.tabs=new window.jvbTabs(document.querySelector(".replace"),{news:()=>{this.activeTab="all",this.resetFilters(),this.loadItems(!0).then((()=>{}))},mine:()=>{console.log("switching to mine tab"),this.activeTab="own",this.resetFilters(),this.filters.artist=jvbSettings.currentUser,this.loadItems(!0).then((()=>{}))},watching:()=>{this.activeTab="watching",this.resetFilters(),this.filters.watched=!0,this.loadItems(!0).then((()=>{}))}}),this.isLoading=!1,this.alreadyHandling=!1,this.template=new Map,this.endpoints={news:"news",vote:"news/vote"},this.resetFilters(),this.state={hasMore:!0,pages:1,items:0},this.initElements(),this.initEvents(),this.loadItems()}resetFilters(){this.filters={page:1,order:"DESC",orderby:"date",shop:null,type:null,artist:null,watched:!1}}initElements(){this.container=document.querySelector(".replace"),this.grid=this.container.querySelector(".item-grid"),this.addButton=this.container.querySelector(".add-item-btn"),this.addModal=new window.jvbModal(this.container.querySelector(".create-modal"),{render:this.renderModal.bind(this),open:this.addButton,content:"news",openMessage:"Opened modal to create a news post.",onSave:this.saveModal.bind(this)}),this.filterForm=this.container.querySelector("form"),this.dateRangeFilter=new window.jvbModal(this.container.querySelector("dialog.date-range"),{open:!1}),this.clearFilters=this.container.querySelector(".clear-filters"),this.replyModal=new window.jvbModal(this.container.querySelector(".create-response"),{open:!1,content:"response",openMessage:"Opened Response modal",onSave:this.saveCreatedResponse.bind(this)})}initEvents(){this.filterForm.addEventListener("change",(e=>{let t=e.target.value;if(!e.target.closest(".date-range"))if("custom"===t)this.handleCustomDateRange();else{let s=e.target.name;s?this.filters[s]=t:this.resetFilters(),this.loadItems(!0)}})),document.addEventListener("click",(e=>{if(e.target===this.clearFilters&&(this.filterForm.reset(),this.resetFilters(),this.loadItems(!0)),e.target.closest("button.reply")){let t=e.target.closest("button"),s=t.closest(".item").dataset.id,n="";"news"===t.dataset.type?n=t.closest(".item").querySelector(".item-info").innerHTML:(n=t.closest(".response").querySelector(".content").innerHTML,this.replyModal.modal.dataset.parent_id=t.id.replace("reply-to","")),this.replyModal.modal.dataset.id=s,this.replyModal.modal.dataset.type=t.dataset.type,this.replyModal.modal.querySelector(".original").innerHTML="<h5>Replying to:</h5>"+n,this.replyModal.handleOpen()}}))}renderModal(){}handleCustomDateRange(){this.dateRangeFilter.handleOpen();let e=this.dateRangeFilter.modal.querySelector("input.date-start"),t=this.dateRangeFilter.modal.querySelector("input.date-end"),s=this.dateRangeFilter.modal.querySelector("select");this.dateRangeFilter.modal.querySelectorAll("input, select").forEach((n=>{n.addEventListener("change",(i=>{n===e&&""!==t.value||n===t&&""!==e.value?(this.filters.dateFrom=e.value,this.filters.dateTo=t.value,this.dateRangeFilter.handleClose(),this.loadItems(!0)):n===s&&(this.filters.customDate=s.value,this.dateRangeFilter.handleClose(),this.loadItems(!0))}))}))}async saveModal(e){const t=new FormData(this.addModal.modal.querySelector("form"));t.append("user",jvbSettings.currentUser),this.queue.addToQueue({type:"new_news",data:t})}async loadItems(e=!0){if(!this.isLoading)try{this.isLoading=!0,this.loading.show(),e&&(this.filters.page=1,removeChildren(this.grid),this.grid.classList.remove("empty"));const t=this.buildFilters(),s=await this.cache.fetchWithCache(`${jvbSettings.api}${this.endpoints.news}?${t.toString()}`,{method:"GET",headers:{"X-WP-Nonce":jvbSettings.nonce,action_nonce:jvbSettings.dash}},{context:"news",forceRefresh:!0});return this.renderItems(s.items||[],this.filters.page>1),s.pagination&&(this.state={hasMore:s.has_more,items:s.items,pages:s.pages}),s}catch(e){throw this.handleError(e,"loading news"),e}finally{this.isLoading=!1,this.loading.hide()}}buildFilters(){const e=JSON.parse(JSON.stringify(this.filters));let t={};for(var[s,n]of Object.entries(e))!1!==n&&null!==n&&(t[s]=n);return new URLSearchParams(t)}renderItems(e,t=!1){if(t||removeChildren(this.grid),0===e.length)return this.a11y.announceItems(0,t),void this.showEmptyState();const s=document.createDocumentFragment(),n=i=>{const a=Math.min(i+10,e.length);for(let t=i;t<a;t++){const n=e[t],i=this.createItemElement(n);s.appendChild(i)}a<e.length?requestAnimationFrame((()=>{n(a)})):(this.grid.appendChild(s),this.a11y.makeNavigable(this.grid.querySelectorAll(".item:not([data-keyboard-nav])")),this.a11y.announceItems(e.length,t,this.state.hasMore))};e.length>0?n(0):this.a11y.announceItems(0,t)}createItemElement(e){const t=window.getTemplate(`template-${this.activeTab}`);t.id=`news-${e.id}`,t.dataset.id=e.id;const[s]=t.getElementsByTagName("h3"),[n]=t.getElementsByClassName("published"),[i]=t.getElementsByClassName("artist"),[a]=t.getElementsByClassName("shop"),[r]=t.getElementsByClassName("tldr"),[o]=t.getElementsByClassName("item-info"),[l]=t.getElementsByClassName("image");[s.textContent,n.textContent,i.href,i.textContent,r.textContent,o.innerHTML]=[e.title,formatTimeAgo(e.date),e.artist.url,e.artist.name,e.tldr,e.post_content],e.shop?[a.href,a.innerHTML]=[e.shop.url,jvbSettings.icons.shop+e.shop.name]:a.hidden=!0;const[d]=t.getElementsByClassName("favourite-button");if("own"!==this.activeTab)[d.dataset.id,d.dataset.artist]=[e.id,e.artist.id],window.userFavourites.news?.includes(parseInt(e.id))?(removeChildren(d),d.append(getIcon("star-fi"))):(removeChildren(d),d.append(getIcon("star")));else{d.hidden=!0;const[s]=t.getElementsByClassName("select-checkbox"),[n]=t.getElementsByTagName("label");[s.id,s.value,n.for]=[`item-${e.id}`,e.id,`item-${e.id}`]}let h="";window.userVotes?.news?.has(e.id)&&(h=window.userVotes.news.get(e.id)),console.log(e),t.querySelector(".summary").appendChild(formatVote(e,h));let c=window.getTemplate("commentsButton");c.href=`#responses-to-${e.id}`,c.querySelector(".count").textContent=e.comments.items.length;let m=window.getTemplate("responses");m.id=`responses-to-${e.id}`,m.querySelector("summary").textContent+=" { "+e.comments.items.length+" }";let p=window.getTemplate("replyButton");return p.id="reply-to-"+e.id,p.dataset.type="news",p.dataset.action="reply",t.appendChild(p),e.comments.items.length>0&&e.comments.items.forEach((e=>{m.appendChild(this.formatComment(e))})),t.appendChild(m),t.querySelector(".vote").prepend(c,t.querySelector(".vote button")),e.image&&e.image.replace(/src="([^"]+)"/,'data-src="$1"'),t}formatComment(e,t=null){let s=window.getTemplate("response");s.id="response-"+e.id;let n=s.querySelector("summary");n.querySelector(".content").innerHTML=e.response,n.querySelector(".created").textContent=formatTimeAgo(e.created_at);let i=checkVoteStatus("response",e.id);e.content="response",s.querySelector(".footer").appendChild(formatVote(e,i)),console.log(e);let a=window.getTemplate("replyButton");a.id="reply-to-"+e.id,t&&(a.dataset.parent_id=t),a.dataset.action="reply",a.dataset.type=e.content,n.querySelector(".vote").prepend(a,n.querySelector(".vote").firstElementChild);let r=n.querySelector(".artist"),o=n.querySelector(".shop");if(console.log(e),e.artist?(e.artist.shop||o.remove(),[r.href,r.textContent,o.href,o.textContent]=[e.artist.url,e.artist.name,e.artist.shop.url,e.artist.shop.name]):(r.remove(),o.remove()),e.children.items.length>0){let t=window.getTemplate("responses");t.id="replies-to-"+e.id,t.querySelector("summary").textContent="See Responses {"+e.children.items.length+"}",e.children.items.forEach((s=>{t.appendChild(this.formatComment(s,e.id))})),s.appendChild(t)}return s}renderResponseCreate(){}saveCreatedResponse(){console.log("Saving create response"),console.log(this.replyModal.modal.id);const e=this.replyModal.modal;let t={user:jvbSettings.currentUser,item_id:e.dataset.id,response:e.querySelector(".ql-editor").innerHTML,content:e.dataset.type,action:"create"};e.dataset.parent_id&&(t.parent_id=e.dataset.parent_id),console.log(t),this.queue.addToQueue({type:"new_response",data:t})}showEmptyState(){const e=document.createElement("div");e.className="no-news",e.innerHTML="\n            <h3>Nothing here</h3>\n            <p>No updates here.</p>\n            <p>Add some gap fillers from the main favourites tab.</p>\n        ",this.grid.appendChild(e),this.grid.classList.add("empty"),this.a11y.announce("No favourites to show!")}hideEmptyState(){let e=this.grid.querySelector(".no-news");e&&e.remove()}handleError(e,t){console.error(`News error (${t}):`,e),window.jvbError&&window.jvbError.log(e,{component:"NewsManager",action:t}),window.jvbA11y&&window.jvbA11y.announce(`Error ${t}. ${e.message||"Please try again."}`)}};
\ No newline at end of file
diff --git a/assets/js/min/populate.min.js b/assets/js/min/populate.min.js
index f5d813a..eaaad5a 100644
--- a/assets/js/min/populate.min.js
+++ b/assets/js/min/populate.min.js
@@ -1 +1 @@
-window.jvbPopulate=class{constructor(e,t={},a={},l={}){console.log("Populating field... ",e),console.log("fieldData: ",t),console.log("imageData: ",a),console.log("options: ",l);for(let[r,o]of Object.entries(t)){let t=e.querySelector(`[data-field="${r}"]`);t&&this.populateField(t,r,o,a,l)}}populateField(e,t,a,l={},r={}){if(e&&null!=a)switch(this.getFieldType(e)){case"image":this.populateImageField(e,t,a,l);break;case"gallery":this.populateGalleryField(e,t,a,l);break;case"repeater":this.populateRepeaterField(e,t,a,r);break;case"taxonomy":this.populateTaxonomyField(e,t,a);break;case"user":this.populateUserField(e,t,a);break;case"location":this.populateLocationField(e,t,a);break;case"set":case"checkbox":this.populateSetField(e,t,a);break;case"select":case"radio":this.populateSelectField(e,t,a);break;case"true_false":this.populateBooleanField(e,t,a);break;case"date":case"time":case"datetime":this.populateDateField(e,t,a);break;case"number":this.populateNumberField(e,t,a);break;case"textarea":e.querySelector(".editor-container")?this.populateEditorField(e,t,a):this.populateTextareaField(e,t,a);break;default:this.populateTextField(e,t,a)}}getFieldType(e){const t=["image","gallery","repeater","taxonomy","user","location","set","checkbox","select","radio","true_false","date","time","datetime","editor","number","text","textarea","email","url","tel","phone"];for(const a of t)if(e.classList.contains(a))return a;if(e.dataset.type)return e.dataset.type;const a=e.querySelector("input, select, textarea");if(a){if("TEXTAREA"===a.tagName)return"true"===a.dataset.editor?"editor":"textarea";if(a.type)return"checkbox"!==a.type||e.classList.contains("true_false")?a.type:"set"}return"text"}populateTextField(e,t,a){const l=e.querySelector(`[name="${t}"], input, textarea`);if(l&&(l.value=String(a||""),l.dataset.limit)){const t=e.querySelector(".char-count .current");t&&(t.textContent=l.value.length)}}populateTextareaField(e,t,a){const l=e.querySelector(`textarea[name="${t}"]`)||e.querySelector('textarea:not([data-editor="true"])');if(l){if(l.value,l.value=String(a||""),l.dispatchEvent(new Event("change",{bubbles:!0})),l.dataset.limit){const t=e.querySelector(".char-count .current");if(t){t.textContent=l.value.length;const a=parseInt(l.dataset.limit,10);l.value.length>=a?e.classList.add("reached"):e.classList.remove("reached")}}}else console.warn(`No textarea found for field ${t} in wrapper:`,e)}populateNumberField(e,t,a){const l=e.querySelector(`[name="${t}"], input[type="number"]`);l&&(l.value=Number(a)||0)}populateBooleanField(e,t,a){const l=e.querySelector(`[name="${t}"], input[type="checkbox"]`);l&&(l.checked=Boolean(a))}populateSelectField(e,t,a){const l=String(a||""),r=e.querySelector(`select[name="${t}"]`);if(r)return void(r.value=l);const o=e.querySelector(`input[type="radio"][name="${t}"][value="${l}"]`);o&&(o.checked=!0)}populateSetField(e,t,a){let l=a;if("string"==typeof a)try{l=JSON.parse(a)}catch(e){l=a.split(",").map((e=>e.trim()))}Array.isArray(l)||(l=[String(l)]),e.querySelectorAll(`input[type="checkbox"][name*="${t}"]`).forEach((e=>{e.checked=l.includes(e.value)}))}populateDateField(e,t,a){const l=e.querySelector(`[name="${t}"], input`);if(l&&a){let e=a;"object"==typeof a&&a.date&&(e=a.date);try{const t=new Date(e);if(!isNaN(t.getTime()))switch(l.type){case"date":l.value=t.toISOString().split("T")[0];break;case"time":l.value=t.toTimeString().slice(0,5);break;case"datetime-local":l.value=t.toISOString().slice(0,16);break;default:l.value=e}}catch(t){l.value=e}}}populateEditorField(e,t,a){const l=e.querySelector(`textarea[name="${t}"]`)||e.querySelector('textarea[data-editor="true"]')||e.querySelector("textarea");if(!l)return void console.warn(`Editor field ${t}: textarea not found`);const r=String(a||"");l.value=r;const o=e.querySelector(".editor");if(o){let e=null;if(o.__quill)e=o.__quill;else if(o.quill)e=o.quill;else if(window.Quill&&window.Quill.find)e=window.Quill.find(o);else if(window.Quill&&window.Quill.instances)for(let t of window.Quill.instances)if(t.container===o){e=t;break}e?(e.root.innerHTML=r,o.__quill=e):(console.warn(`Quill instance not found for ${t}, setting HTML directly`),o.innerHTML=r)}else console.warn(`Editor container not found for ${t}`);l.dispatchEvent(new Event("change",{bubbles:!0}))}populateLocationField(e,t,a){a&&"object"==typeof a&&["address","lat","lng","street","city","province","postal_code","country"].forEach((l=>{if(void 0!==a[l]){const r=e.querySelector(`[name="${t}_${l}"], [name="${l}"]`);r&&(r.value=String(a[l]||""))}}))}populateTaxonomyField(e,t,a){let l=[];if(Array.isArray(a))l=a.map((e=>String(e)));else if("string"==typeof a)try{const e=JSON.parse(a);l=Array.isArray(e)?e.map((e=>String(e))):[String(e)]}catch(e){l=a.split(",").map((e=>e.trim()))}else a&&(l=[String(a)]);if(0===l.length)return;const r=e.querySelector(`input[type="hidden"][name="${t}"]`);r&&(r.value=l.join(","))}populateUserField(e,t,a){this.populateTaxonomyField(e,t,a)}populateImageField(e,t,a,l={}){if(!a)return;const r=String(a).split(",").filter((e=>parseInt(e.trim())));if(0===r.length)return;const o=e.querySelector(`input[type="hidden"][name="${t}"]`);o&&(o.value=r.join(","));const i=e.querySelector(".item-grid"),n=e.querySelector(".file-upload-container");e.querySelector(".progress")?.remove(),i&&(window.removeChildren(i),r.forEach((e=>{let t=window.getTemplate("uploadItem"),a=t.querySelector("img"),r=t.querySelector("details"),o=window.getTemplate("uploadMeta");r.append(o),[a.src,a.alt,t.querySelector('[name="image-title"]').value,t.querySelector('[name="image-alt-text"]').value,t.querySelector('[name="image-caption"]').value]=[l[e].medium,l[e].alt,l[e].title,l[e].alt,l[e].caption],r.querySelector(".upload-meta > .hint")?.remove(),i.append(t)})),r.length>0&&n&&(n.hidden=!0))}populateGalleryField(e,t,a,l={}){this.populateImageField(e,t,a,l)}populateRepeaterField(e,t,a,l={}){if(console.log("fieldWrapper",e),console.log("fieldName",t),console.log("fieldValue",a),console.log("options",l),!a||!Array.isArray(a))return;const r=e.querySelector(".repeater-items"),o=e.querySelector("template");r&&o?(window.removeChildren(r),a.forEach(((a,l)=>{if(!a||"object"!=typeof a)return;const i=window.getTemplate(o.className);if(!i)return void console.warn(`Repeater field ${t}: template not found`);i.id=`${e.closest("form").id}-${t}-row-${l}`,i.dataset.index=l;const n=i.querySelector(".row-number");n&&(n.textContent=`#${l+1}`),i.querySelectorAll("input, select, textarea").forEach((e=>{const r=e.name,o=`${t}:${l}:${r}`,i=`${t}-${l}-${r}-${e.value}`;e.name=o,e.id=i;const n=e.nextElementSibling;n&&"LABEL"===n.tagName&&(n.htmlFor=i),void 0!==a[r]&&this.populateRepeaterFieldValue(e,r,a[r])})),r.appendChild(i)}))):console.warn(`Repeater field ${t}: missing container or template`)}populateRepeaterFieldValue(e,t,a){switch(e.type){case"checkbox":e.checked=Boolean(a);break;case"radio":e.checked=e.value===String(a);break;default:e.value=String(a||"")}}};
\ No newline at end of file
+window.jvbPopulate=class{constructor(e,t={},a={},r={}){for(let[l,i]of Object.entries(t)){let t=e.querySelector(`[data-field="${l}"]`);t&&this.populateField(t,l,i,a,r)}}populateField(e,t,a,r={},l={}){if(e&&null!=a)switch(this.getFieldType(e)){case"image":this.populateImageField(e,t,a,r);break;case"gallery":this.populateGalleryField(e,t,a,r);break;case"repeater":this.populateRepeaterField(e,t,a,l);break;case"taxonomy":this.populateTaxonomyField(e,t,a);break;case"user":this.populateUserField(e,t,a);break;case"location":this.populateLocationField(e,t,a);break;case"set":case"checkbox":this.populateSetField(e,t,a);break;case"select":case"radio":this.populateSelectField(e,t,a);break;case"true_false":this.populateBooleanField(e,t,a);break;case"date":case"time":case"datetime":this.populateDateField(e,t,a);break;case"number":this.populateNumberField(e,t,a);break;case"textarea":e.querySelector(".editor-container")?this.populateEditorField(e,t,a):this.populateTextareaField(e,t,a);break;default:this.populateTextField(e,t,a)}}getFieldType(e){const t=["image","gallery","repeater","taxonomy","user","location","set","checkbox","select","radio","true_false","date","time","datetime","editor","number","text","textarea","email","url","tel","phone"];for(const a of t)if(e.classList.contains(a))return a;if(e.dataset.type)return e.dataset.type;const a=e.querySelector("input, select, textarea");if(a){if("TEXTAREA"===a.tagName)return"true"===a.dataset.editor?"editor":"textarea";if(a.type)return"checkbox"!==a.type||e.classList.contains("true_false")?a.type:"set"}return"text"}populateTextField(e,t,a){const r=e.querySelector(`[name="${t}"], input, textarea`);if(r&&(r.value=String(a||""),r.dataset.limit)){const t=e.querySelector(".char-count .current");t&&(t.textContent=r.value.length)}}populateTextareaField(e,t,a){const r=e.querySelector(`textarea[name="${t}"]`)||e.querySelector('textarea:not([data-editor="true"])');if(r){if(r.value,r.value=String(a||""),r.dispatchEvent(new Event("change",{bubbles:!0})),r.dataset.limit){const t=e.querySelector(".char-count .current");if(t){t.textContent=r.value.length;const a=parseInt(r.dataset.limit,10);r.value.length>=a?e.classList.add("reached"):e.classList.remove("reached")}}}else console.warn(`No textarea found for field ${t} in wrapper:`,e)}populateNumberField(e,t,a){const r=e.querySelector(`[name="${t}"], input[type="number"]`);r&&(r.value=Number(a)||0)}populateBooleanField(e,t,a){const r=e.querySelector(`[name="${t}"], input[type="checkbox"]`);r&&(r.checked=Boolean(a))}populateSelectField(e,t,a){const r=String(a||""),l=e.querySelector(`select[name="${t}"]`);if(l)return void(l.value=r);const i=e.querySelector(`input[type="radio"][name="${t}"][value="${r}"]`);i&&(i.checked=!0)}populateSetField(e,t,a){let r=a;if("string"==typeof a)try{r=JSON.parse(a)}catch(e){r=a.split(",").map((e=>e.trim()))}Array.isArray(r)||(r=[String(r)]),e.querySelectorAll(`input[type="checkbox"][name*="${t}"]`).forEach((e=>{e.checked=r.includes(e.value)}))}populateDateField(e,t,a){const r=e.querySelector(`[name="${t}"], input`);if(r&&a){let e=a;"object"==typeof a&&a.date&&(e=a.date);try{const t=new Date(e);if(!isNaN(t.getTime()))switch(r.type){case"date":r.value=t.toISOString().split("T")[0];break;case"time":r.value=t.toTimeString().slice(0,5);break;case"datetime-local":r.value=t.toISOString().slice(0,16);break;default:r.value=e}}catch(t){r.value=e}}}populateEditorField(e,t,a){const r=e.querySelector(`textarea[name="${t}"]`)||e.querySelector('textarea[data-editor="true"]')||e.querySelector("textarea");if(!r)return void console.warn(`Editor field ${t}: textarea not found`);const l=String(a||"");r.value=l;const i=e.querySelector(".editor");if(i){let e=null;if(i.__quill)e=i.__quill;else if(i.quill)e=i.quill;else if(window.Quill&&window.Quill.find)e=window.Quill.find(i);else if(window.Quill&&window.Quill.instances)for(let t of window.Quill.instances)if(t.container===i){e=t;break}e?(e.root.innerHTML=l,i.__quill=e):(console.warn(`Quill instance not found for ${t}, setting HTML directly`),i.innerHTML=l)}else console.warn(`Editor container not found for ${t}`);r.dispatchEvent(new Event("change",{bubbles:!0}))}populateLocationField(e,t,a){a&&"object"==typeof a&&["address","lat","lng","street","city","province","postal_code","country"].forEach((r=>{if(void 0!==a[r]){const l=e.querySelector(`[name="${t}_${r}"], [name="${r}"]`);l&&(l.value=String(a[r]||""))}}))}populateTaxonomyField(e,t,a){let r=[];if(Array.isArray(a))r=a.map((e=>String(e)));else if("string"==typeof a)try{const e=JSON.parse(a);r=Array.isArray(e)?e.map((e=>String(e))):[String(e)]}catch(e){r=a.split(",").map((e=>e.trim()))}else a&&(r=[String(a)]);if(0===r.length)return;const l=e.querySelector(`input[type="hidden"][name="${t}"]`);l&&(l.value=r.join(","))}populateUserField(e,t,a){this.populateTaxonomyField(e,t,a)}populateImageField(e,t,a,r={}){if(!a)return;const l=String(a).split(",").filter((e=>parseInt(e.trim())));if(0===l.length)return;const i=e.querySelector(`input[type="hidden"][name="${t}"]`);i&&(i.value=l.join(","));const o=e.querySelector(".item-grid"),n=e.querySelector(".file-upload-container");e.querySelector(".progress")?.remove(),o&&(window.removeChildren(o),l.forEach((e=>{let t=window.getTemplate("uploadItem"),a=t.querySelector("img"),l=t.querySelector("details"),i=window.getTemplate("uploadMeta");l.append(i),[a.src,a.alt,t.querySelector('[name="image-title"]').value,t.querySelector('[name="image-alt-text"]').value,t.querySelector('[name="image-caption"]').value]=[r[e].medium,r[e].alt,r[e].title,r[e].alt,r[e].caption],l.querySelector(".upload-meta > .hint")?.remove(),o.append(t)})),l.length>0&&n&&(n.hidden=!0))}populateGalleryField(e,t,a,r={}){"timeline"===t?this.populateTimelineGallery(e,t,a,r):this.populateImageField(e,t,a,r)}populateTimelineGallery(e,t,a,r){if(!a)return;const l=Object.keys(a);if(0===l.length)return;const i=e.querySelector(".item-grid");e.querySelector(".file-upload-container"),e.querySelector(".progress")?.remove(),i&&(window.removeChildren(i),l.forEach((e=>{let t=window.getTemplate("timelineItem"),l=t.querySelector("img");t.querySelector("video")?.remove(),t.querySelector(".select-item span")?.remove();let o=a[e],n=r[e];l.src=n.medium,o={...o,...n},t.querySelectorAll(".field").forEach((e=>{if(e.classList.contains("group"))return;let t=e.querySelector("input, textarea"),a=e.querySelector("label"),l=t.name.replace("upload_data::",""),i=o[l];this.populateField(e,l,i,r);let n=`[${o.id}]${l}`;[t.name,t.id,a.htmlFor]=[n,n,n]})),i.append(t)})))}populateRepeaterField(e,t,a,r={}){if(!a||!Array.isArray(a))return;const l=e.querySelector(".repeater-items"),i=e.querySelector("template");l&&i?(window.removeChildren(l),a.forEach(((a,r)=>{if(!a||"object"!=typeof a)return;const o=window.getTemplate(i.className);if(!o)return void console.warn(`Repeater field ${t}: template not found`);o.id=`${e.closest("form").id}-${t}-row-${r}`,o.dataset.index=r;const n=o.querySelector(".row-number");n&&(n.textContent=`#${r+1}`),o.querySelectorAll("input, select, textarea").forEach((e=>{const l=e.name,i=`${t}:${r}:${l}`,o=`${t}-${r}-${l}-${e.value}`;e.name=i,e.id=o;const n=e.nextElementSibling;n&&"LABEL"===n.tagName&&(n.htmlFor=o),void 0!==a[l]&&this.populateRepeaterFieldValue(e,l,a[l])})),l.appendChild(o)}))):console.warn(`Repeater field ${t}: missing container or template`)}populateRepeaterFieldValue(e,t,a){switch(e.type){case"checkbox":e.checked=Boolean(a);break;case"radio":e.checked=e.value===String(a);break;default:e.value=String(a||"")}}};
\ No newline at end of file
diff --git a/assets/js/min/queue.min.js b/assets/js/min/queue.min.js
index 2e4f0da..2139ecc 100644
--- a/assets/js/min/queue.min.js
+++ b/assets/js/min/queue.min.js
@@ -1 +1 @@
-(()=>{class t{constructor(t={}){this.canUpdateUI=!0,console.log("jvbSettings",jvbSettings),this.config={apiBase:jvbSettings.api,maxRetries:3,pollInterval:5e3,activityDelay:2e3,autosync:!0,endpoint:"queue",...t},this.user=jvbSettings.currentUser,console.log(this.user),this.headers={"X-WP-Nonce":jvbSettings.nonce,...t.headers},this.a11y=window.jvbA11y,this.errors=window.jvbError,this.store=new window.jvbStore({name:"queue",storeName:"operations",keyPath:"id",endpoint:this.config.endpoint,TTL:1/0,indexes:[{name:"status",keyPath:"status"},{name:"type",keyPath:"type"}],showLoading:!1,getBlobs:async t=>{if(window.jvbUploadBlobs){Array.isArray(t)||"string"!=typeof t||(t=[t]);return(await Promise.all(t.map((t=>window.jvbUploadBlobs.getBlob(t))))).filter(Boolean)}return null}}),this.classes=["offline","synced","pending"],this.isProcessing=!1,this.isPolling=!1,this.subscribers=new Set,this.statuses=["queued","localProcessing","uploading","pending","processing","completed","failed","failed_permanent"],this.initUI(),this.initListeners(),console.log(this.ui),this.ui.panel&&(this.popup=new window.jvbPopup({popup:this.ui.panel,toggle:this.ui.toggle,name:"Queue Panel"})),this.initQueue(),this.user&&(this.ui.toggle.hidden=!1,this.ui.panel.hidden=!1)}async initQueue(){const t=this.getOperationsByStatus(["completed","failed_permanent"],!1);t.length>0?this.startPolling():this.updateStatusPanel("synced"),this.store.subscribe(((t,e)=>{switch(t){case"data-loaded":this.getOperationsByStatus(["completed","failed_permanent"],!1).length>0&&this.startPolling(),this.updateUI();break;case"item-saved":this.hasQueuedOperations()&&this.startPolling();default:this.updateUI()}})),this.notify("queue-initialized",{operations:t})}addToQueue(t){const e={id:`u${this.user}_${Date.now()}_${Math.random().toString(36).substring(2,9)}`,endpoint:null,method:"POST",headers:{},data:{},canMerge:!0,popup:"Saving changes...",title:"Operation",status:"queued",timestamp:Date.now(),retries:0,user:this.user,...t};if(e.headers={...this.headers,...e.headers},!e.endpoint||!e.data)return console.error("Invalid operation queued: missing endpoint or data"),null;const s=Array.from(this.store.data.values()).filter((t=>"queued"===t.status&&t.endpoint===e.endpoint&&t.canMerge));if(s.length>0){const t=s[0];return t.data=window.deepMerge(t.data,e.data),t.timestamp=Date.now(),this.updateOperationStatus(t.id,t.status),this.updateUI(),this.startActivityTracking(),t.id}return console.log("Added to Queue: ",e),this.setQueue(e),this.updateOperationStatus(e.id,e.status),this.updateUI(),this.startActivityTracking(),e.id}setQueue(t){this.store.save(t)}updateOperationStatus(t,e){let s=this.store.get(t);s&&(s.status=e,this.notify("operation-status",s),this.updateOperationUI(s))}getQueue(t){return this.store.get(t)}clearQueue(t){this.store.delete(t)}startActivityTracking(){if(!this.activityListeners){const t=["mousedown","mousemove","keypress","scroll","touchstart"];this.activityListeners=t.map((t=>{const e=()=>this.resetActivityTimer();return document.addEventListener(t,e,{passive:!0}),{event:t,handler:e}}))}this.resetActivityTimer()}resetActivityTimer(){this.lastActivity=Date.now(),this.activityTimer&&clearTimeout(this.activityTimer),this.activityTimer=setTimeout((()=>{this.processQueue()}),this.config.activityDelay)}stopActivityTracking(){this.activityTimer&&(clearTimeout(this.activityTimer),this.activityTimer=null),this.activityListeners&&(this.activityListeners.forEach((({event:t,handler:e})=>{document.removeEventListener(t,e)})),this.activityListeners=null)}setProcessing(t){this.isProcessing=t,this.ui.toggle.classList.toggle("saving",t)}async processQueue(){if(this.isProcessing)return;const t=this.getOperationsByStatus("queued");if(0===t.length)return void this.stopActivityTracking();this.setProcessing(!0);for(const e of t)await this.processOperation(e);this.setProcessing(!1),this.stopActivityTracking();this.getOperationsByStatus(["queued","completed","failed_permanent"],!1).length>0&&this.startPolling()}async processOperation(t){try{this.updateOperationStatus(t.id,"uploading"),t=this.getQueue(t.id);const i=`${this.config.apiBase}${t.endpoint}`;let a;if(console.log(t.data),t.data instanceof FormData)for(var[e,s]of(t.data.append("id",t.id),t.data.append("user",this.user),a=t.data,console.log("Sending to server:"),a.entries()))console.log(e,s);else a=JSON.stringify({...t.data,id:t.id,user:this.user}),t.headers["Content-Type"]="application/json";const n=await fetch(i,{method:t.method,headers:t.headers,body:a}),r=await n.json();if(!n.ok||!1===r.success)throw new Error(r.message||`HTTP ${n.status}`);if(r.id&&t.id!==r.id){const e=this.getQueue(r.id);e?(e.data=window.deepMerge(e.data,t.data),e.status="pending",e.serverData=r,this.updateOperationStatus(e.id,e.status),this.setQueue(e),this.removeOperationFromUI(t.id),t=e):(this.clearQueue(t.id),t.id=r.id,t.status="pending",t.serverData=r,this.updateOperationStatus(t.id,t.status),this.setQueue(t))}else t.status="pending",t.serverData=r,this.updateOperationStatus(t.id,"pending"),this.setQueue(t);this.a11y.announce(`${t.title} sent to server for processing.`)}catch(e){console.error("Operation failed:",e),t.retries++,t.lastError=e.message,t.retries>=this.config.maxRetries?t.status="failed_permanent":(t.status="failed",t.nextRetry=Date.now()+1e3*Math.pow(2,t.retries)),this.updateOperationStatus(t.id,t.status),this.setQueue(t)}}startPolling(){this.isPolling||(this.isPolling=!0,this.updateStatusPanel("pending"),this.pollTimer=setInterval((async()=>{try{await this.store.fetch();0===this.getOperationsByStatus(["completed","failed_permanent"],!1).length&&(this.stopPolling(),this.updateStatusPanel("synced"))}catch(t){console.error("Polling error:",t)}}),this.config.pollInterval))}stopPolling(){this.isPolling&&(this.isPolling=!1,this.pollTimer&&(clearInterval(this.pollTimer),this.pollTimer=null),this.countdownTimer&&(clearInterval(this.countdownTimer),this.countdownTimer=null))}async updateServerOperations(t,e){if(0!==(t=(t=Array.isArray(t)?t:t.includes(",")?t.split(","):[t]).filter((t=>{let s=this.getQueue(t);return this.getAllowedActions(s.status).includes(e)}))).length){["cancel","dismiss"].includes(e)&&t.forEach((t=>{this.removeOperationFromUI(t)}));try{const s=`${this.config.apiBase}${this.config.endpoint}`,i=await fetch(s,{method:"POST",headers:{"Content-Type":"application/json",...this.headers},body:JSON.stringify({ids:t,action:e})});if(!i.ok){const t=await i.json().catch((()=>{}));throw new Error(t.message||`${e} failed: ${i.status}`)}const a=await i.json();if(!a.success)throw new Error(a.message||`${e} operation failed`);return["cancel","dismiss"].includes(e)?t.forEach((t=>{let s=this.getQueue(t);this.notify(`${e}-operation`,s),this.clearQueue(t)})):(t.forEach((t=>{let s=this.getQueue(t);this.notify(`${e}-operation`,s),s.status="queued",s.retries=0,this.setQueue(s),this.updateOperationStatus(s.id,s.status)})),this.startActivityTracking()),this.updateUI(),a}catch(s){const i=await window.jvbError.log(s,{component:"QueueManager",operation:"performQueueAction",action:e,operationIds:t,itemCount:t.length},(()=>this.updateServerOperations(t,e)));if(i.retried)return i;throw s}}}getAllowedActions(t){return{queued:["cancel"],localProcessing:["cancel"],pending:["cancel"],processing:[],completed:["dismiss"],failed:["retry","dismiss"],failed_permanent:["dismiss"]}[t]||[]}initListeners(){this.clickHandler=this.handleClick.bind(this),this.changeHandler=this.handleChange.bind(this),document.addEventListener("click",this.clickHandler),this.ui.panel?.addEventListener("change",this.changeHandler),this.handleOnline=()=>{this.updateStatusPanel(),this.hasQueuedOperations()&&this.processQueue()},this.handleOffline=()=>this.updateStatusPanel("offline"),this.handleBeforeUnload=t=>{if(this.getOperationsByStatus(["queued","uploading"]).length>0)return t.preventDefault(),"You have unsaved changes in the queue."},window.addEventListener("online",this.handleOnline),window.addEventListener("offline",this.handleOffline),window.addEventListener("beforeunload",this.handleBeforeUnload)}handleClick(t){if(t.target.closest(this.selectors.panel,this.selectors.toggle))if(t.target.closest(this.selectors.refreshButton))this.store.fetch();else if(t.target.closest(this.selectors.clearButton)){const t=this.getOperationsByStatus("completed");if(t.length>0){const e=t.map((t=>t.id));this.updateServerOperations(e,"dismiss")}}else if(t.target.closest(this.selectors.retryButton)){const t=this.getOperationsByStatus("failed");if(t.length>0){const e=t.map((t=>t.id));this.updateServerOperations(e,"retry")}}else if(t.target.closest("[data-action]")){const e=t.target.closest("[data-action]"),s=e.closest("[data-id]")?.dataset.id;s&&this.updateServerOperations(s,e.dataset.action)}else if(t.target.closest(".filters [data-filter]")){const e=t.target.closest("[data-filter]").dataset.filter;this.setFilter(e)}}handleChange(t){}initUI(){if(this.icons={queued:"refresh",localProcessing:"refresh",uploading:"syncing",pending:"cloud",processing:"syncing",completed:"synced",failed:"error",failed_permanent:"error"},this.selectors={panel:"aside#queue",toggle:"button.qtoggle",refreshButton:"button.refreshNow",countdown:".countdown",indicator:".qtoggle .indicator",count:".qtoggle .count",popup:".popup",itemsContainer:".qitems",clearButton:".dismiss-all",retryButton:".retry-all",filters:{all:'.filters [data-filter="all"]',received:'.filters [data-filter="queued"]',localProcessing:'.filters [data-filter="localProcessing"]',uploading:'.filters [data-filter="uploading"]',pending:'.filters [data-filter="pending"]',processing:'.filters [data-filter="processing"]',completed:'.filters [data-filter="completed"]',failed:'.filters [data-filter="failed"]'}},this.ui={panel:document.querySelector(this.selectors.panel),toggle:document.querySelector(this.selectors.toggle),count:document.querySelector(this.selectors.count),indicator:document.querySelector(this.selectors.indicator)},this.ui.panel){for(let[t,e]of Object.entries(this.selectors))if(!["panel","toggle","count","indicator"].includes(t))if("object"==typeof e){this.ui[t]={};for(let[s,i]of Object.entries(e))this.ui[t][s]=this.ui.panel.querySelector(i)}else this.ui[t]=this.ui.panel.querySelector(e)}else this.canUpdateUI=!1}updateUI(){if(!this.canUpdateUI)return;const t=this.getQueueStats();if(this.ui.count){const e=t.total-t.completed;this.ui.count.textContent=e>0?e:"",this.ui.count.style.display=e>0?"":"none"}if(this.ui.indicator){const e=t.queued>0||t.uploading>0||t.pending>0||t.processing>0;this.ui.indicator.classList.toggle("active",e)}let e=this.getOperationsByStatus("failed"),s=this.getOperationsByStatus("completed");this.ui.clearButton.disabled=0===s.length,this.ui.retryButton.disabled=0===e.length,Object.entries(this.ui.filters).forEach((([e,s])=>{const i="all"===e?t.total:t[e]||0,a=s.querySelector(".count");a&&(a.textContent=i>0?i:""),s.setAttribute("data-count",i)})),this.renderOperations()}getStatusLabel(t){return{queued:"Queued",localProcessing:"Processing locally",uploading:"Uploading",pending:"Waiting on server",processing:"Processing",completed:"Completed",failed:"Failed (will retry)",failed_permanent:"Failed permanently"}[t]||t}getItemMessage(t){if(t.message)return t.message;if(t.error_message)return t.error_message;switch(t.status){case"queued":return"Waiting to send...";case"uploading":return"Sending to server...";case"pending":return t.position?`Position ${t.position} in queue`:"In server queue";case"processing":return t.progress?`${t.progress}% complete`:"Processing...";case"completed":return"Successfully completed";case"failed":return`Failed: ${t.lastError||"Unknown error"} (Retry ${t.retries}/${this.config.maxRetries})`;case"failed_permanent":return`Failed: ${t.lastError||"Unknown error"}`;default:return""}}calculateProgress(t){if(t.progress)return t.progress;return{queued:10,uploading:25,pending:40,processing:70,completed:100,failed:0,failed_permanent:0}[t.status]||0}getQueueStats(){const t={};return this.statuses.forEach((e=>{t[e]=0})),Array.from(this.store.data.values()).forEach((e=>{t.hasOwnProperty(e.status)&&t[e.status]++})),t.total=Array.from(this.store.data.values()).length,t}renderOperations(){if(!this.ui.itemsContainer)return;const t=this.getActiveFilter(),e=this.getFilteredOperations(t);if(window.removeChildren(this.ui.itemsContainer),0===e.length){let t=window.getTemplate("emptyQueue");this.ui.itemsContainer.append(t),this.a11y.announce("Nothing queued.")}else{let t=this.ui.itemsContainer.querySelector(".emptyQueue");t&&t.remove(),e.forEach((t=>{const e=this.createOperationUI(t);this.ui.itemsContainer.appendChild(e)}))}}createOperationUI(t){const e=window.getTemplate("queueItem");return e.dataset.id=t.id,this.updateOperationUI(t,e),e}updateOperationUI(t,e=null){e||(e=this.ui.itemsContainer?.querySelector(`[data-id="${t.id}"]`)),e||(e=this.createOperationUI(t)),this.statuses.forEach((t=>e.classList.remove(t))),e.classList.add(t.status);let s="";t.updated_at?s=window.formatTimeAgo(new Date(t.updated_at)):t.created_at&&(s=window.formatTimeAgo(new Date(t.created_at)));const i=this.calculateProgress(t),a=e.querySelector(".type"),n=e.querySelector(".status"),r=e.querySelector(".info .details"),o=e.querySelector(".info .time"),l=e.querySelector(".progress .fill");if(a&&(a.textContent=t.title),n){n.querySelector(".icon")?.remove();let e=this.getStatusLabel(t.status);n.title=e,n.prepend(window.getIcon(this.icons[t.status])),n.querySelector("span").textContent=e}r&&(r.textContent=this.getItemMessage(t)),o&&(o.textContent=s),l&&(l.style.width=`${i}%`);const u=e.querySelector(".actions");u&&this.updateActionButtons(t,u)}updateActionButtons(t,e){switch(window.removeChildren(e),t.status){case"queued":case"localProcessing":case"pending":const s=window.getTemplate("button");s.classList.add("cancel"),s.dataset.action="cancel",s.textContent="Cancel",e.appendChild(s);break;case"failed":case"failed_permanent":const i=window.getTemplate("button"),a=window.getTemplate("button");i.classList.add("retry"),i.textContent="Retry",i.disabled=t.retries>=this.maxRetries,i.dataset.action="retry",a.classList.add("dismiss"),a.textContent="Dismiss",a.dataset.action="dismiss",e.appendChild(i),e.appendChild(a);break;case"completed":const n=window.getTemplate("button");n.dataset.action="dismiss",n.classList.add("dismiss"),n.textContent="Dismiss",e.appendChild(n)}}removeOperationFromUI(t){const e=this.ui.itemsContainer?.querySelector(`[data-id="${t}"]`);e&&(e.style.opacity="0",e.style.transform="scale(0.9)",setTimeout((()=>e.remove()),300))}updateCountdown(){if(!this.ui.countdown||!this.isPolling)return;let t=this.config.pollInterval/1e3;this.countdownTimer=setInterval((()=>{t--,this.ui.countdown.textContent=t,t<=0&&(clearInterval(this.countdownTimer),this.isPolling&&setTimeout((()=>this.updateCountdown()),100))}),1e3)}updateStatusPanel(t){this.ui.panel?.classList.remove(...this.classes),this.classes.includes(t)&&this.ui.panel?.classList.add(t)}setFilter(t){Object.values(this.ui.filters).forEach((e=>{e&&e.classList.toggle("active",e.dataset.filter===t)})),this.activeFilter=t,this.renderOperations()}getActiveFilter(){const t=this.ui.panel?.querySelector(".filter.active");return t?.dataset.filter||"all"}getFilteredOperations(t){const e=Array.from(this.store.data.values());return"all"===t?e:e.filter((e=>e.status===t))}showPopup(t,e="success"){if(!this.ui.popup)return;const s=this.ui.popup.querySelector("span");s&&(s.textContent=t),this.ui.popup.className=`popup ${e} show`,setTimeout((()=>{this.ui.popup.classList.remove("show")}),3e3)}getOperationsByStatus(t,e=!0){return Array.isArray(t)||"string"!=typeof t||(t=[t]),e?Array.from(this.store.data.values()).filter((e=>t.includes(e.status))):Array.from(this.store.data.values()).filter((e=>!t.includes(e.status)))}async hasQueuedOperations(){return(await this.store.query("status","queued")).length>0}subscribe(t){return this.subscribers.add(t),()=>this.subscribers.delete(t)}notify(t,e){this.subscribers.forEach((s=>s(t,e)))}destroy(){this.stopPolling(),this.stopActivityTracking(),this.clickHandler&&document.removeEventListener("click",this.clickHandler),this.keyHandler&&document.removeEventListener("keydown",this.keyHandler),this.subscribers.clear()}}document.addEventListener("DOMContentLoaded",(function(){window.jvbQueue=new t}))})();
\ No newline at end of file
+(()=>{class t{constructor(t={}){this.canUpdateUI=!0,console.log("jvbSettings",jvbSettings),this.config={apiBase:jvbSettings.api,maxRetries:3,pollInterval:5e3,activityDelay:2e3,autosync:!0,endpoint:"queue",...t},this.user=jvbSettings.currentUser,console.log(this.user),this.headers={"X-WP-Nonce":jvbSettings.nonce,...t.headers},this.a11y=window.jvbA11y,this.errors=window.jvbError,this.store=new window.jvbStore({name:"queue",storeName:"operations",keyPath:"id",endpoint:this.config.endpoint,TTL:1/0,indexes:[{name:"status",keyPath:"status"},{name:"type",keyPath:"type"}],showLoading:!1,getBlobs:async t=>{if(window.jvbUploadBlobs){Array.isArray(t)||"string"!=typeof t||(t=[t]);return(await Promise.all(t.map((t=>window.jvbUploadBlobs.getBlob(t))))).filter(Boolean)}return null}}),this.classes=["offline","synced","pending"],this.isProcessing=!1,this.isPolling=!1,this.subscribers=new Set,this.statuses=["queued","localProcessing","uploading","pending","processing","completed","failed","failed_permanent"],this.initUI(),this.initListeners(),console.log(this.ui),this.ui.panel&&(this.popup=new window.jvbPopup({popup:this.ui.panel,toggle:this.ui.toggle,name:"Queue Panel"})),this.initQueue(),this.user&&(this.ui.toggle.hidden=!1,this.ui.panel.hidden=!1)}async initQueue(){const t=this.getOperationsByStatus(["completed","failed_permanent"],!1);t.length>0?this.startPolling():this.updateStatusPanel("synced"),this.store.subscribe(((t,e)=>{switch(t){case"data-loaded":this.getOperationsByStatus(["completed","failed_permanent"],!1).length>0&&this.startPolling(),this.updateUI();break;case"item-saved":this.hasQueuedOperations()&&this.startPolling();default:this.updateUI()}})),this.notify("queue-initialized",{operations:t})}addToQueue(t){const e={id:`u${this.user}_${Date.now()}_${Math.random().toString(36).substring(2,9)}`,endpoint:null,method:"POST",headers:{},data:{},canMerge:!0,popup:"Saving changes...",title:"Operation",status:"queued",timestamp:Date.now(),retries:0,user:this.user,...t};if(e.headers={...this.headers,...e.headers},!e.endpoint||!e.data)return console.error("Invalid operation queued: missing endpoint or data"),null;const s=Array.from(this.store.data.values()).filter((t=>"queued"===t.status&&t.endpoint===e.endpoint&&t.canMerge));if(s.length>0){const t=s[0];return t.data=window.deepMerge(t.data,e.data),t.timestamp=Date.now(),this.updateOperationStatus(t.id,t.status),this.updateUI(),this.startActivityTracking(),t.id}return console.log("Added to Queue: ",e),this.setQueue(e),this.updateOperationStatus(e.id,e.status),this.updateUI(),this.startActivityTracking(),e.id}setQueue(t){this.store.save(t)}updateOperationStatus(t,e){let s=this.store.get(t);s&&(s.status=e,this.notify("operation-status",s),this.updateOperationUI(s))}getQueue(t){return this.store.get(t)}clearQueue(t){this.store.delete(t)}startActivityTracking(){if(!this.activityListeners){const t=["mousedown","mousemove","keypress","scroll","touchstart"];this.activityListeners=t.map((t=>{const e=()=>this.resetActivityTimer();return document.addEventListener(t,e,{passive:!0}),{event:t,handler:e}}))}this.resetActivityTimer()}resetActivityTimer(){this.lastActivity=Date.now(),this.activityTimer&&clearTimeout(this.activityTimer),this.activityTimer=setTimeout((()=>{this.processQueue()}),this.config.activityDelay)}stopActivityTracking(){this.activityTimer&&(clearTimeout(this.activityTimer),this.activityTimer=null),this.activityListeners&&(this.activityListeners.forEach((({event:t,handler:e})=>{document.removeEventListener(t,e)})),this.activityListeners=null)}setProcessing(t){this.isProcessing=t,this.ui.toggle.classList.toggle("saving",t)}async processQueue(){if(this.isProcessing)return;const t=this.getOperationsByStatus("queued");if(0===t.length)return void this.stopActivityTracking();this.setProcessing(!0);for(const e of t)await this.processOperation(e);this.setProcessing(!1),this.stopActivityTracking();this.getOperationsByStatus(["queued","completed","failed_permanent"],!1).length>0&&this.startPolling()}async processOperation(t){try{this.updateOperationStatus(t.id,"uploading"),t=this.getQueue(t.id);const i=`${this.config.apiBase}${t.endpoint}`;let a;if(console.log(t.data),t.data instanceof FormData)for(var[e,s]of(t.data.append("id",t.id),t.data.append("user",this.user),a=t.data,console.log("Sending to server:"),a.entries()))console.log(e,s);else a=JSON.stringify({...t.data,id:t.id,user:this.user}),t.headers["Content-Type"]="application/json";const n=await fetch(i,{method:t.method,headers:t.headers,body:a}),r=await n.json();if(!n.ok||!1===r.success)throw new Error(r.message||`HTTP ${n.status}`);if(r.id&&t.id!==r.id){const e=this.getQueue(r.id);e?(e.data=window.deepMerge(e.data,t.data),e.status="pending",e.serverData=r,this.updateOperationStatus(e.id,e.status),this.setQueue(e),this.removeOperationFromUI(t.id),t=e):(this.clearQueue(t.id),t.id=r.id,t.status="pending",t.serverData=r,this.updateOperationStatus(t.id,t.status),this.setQueue(t))}else t.status="pending",t.serverData=r,this.updateOperationStatus(t.id,"pending"),this.setQueue(t);this.a11y.announce(`${t.title} sent to server for processing.`)}catch(e){console.error("Operation failed:",e),t.retries++,t.lastError=e.message,t.retries>=this.config.maxRetries?t.status="failed_permanent":(t.status="failed",t.nextRetry=Date.now()+1e3*Math.pow(2,t.retries)),this.updateOperationStatus(t.id,t.status),this.setQueue(t)}}startPolling(){this.isPolling||(this.isPolling=!0,this.updateStatusPanel("pending"),this.pollTimer=setInterval((async()=>{try{await this.store.fetch();0===this.getOperationsByStatus(["completed","failed_permanent"],!1).length&&(this.stopPolling(),this.updateStatusPanel("synced"))}catch(t){console.error("Polling error:",t)}}),this.config.pollInterval))}stopPolling(){this.isPolling&&(this.isPolling=!1,this.pollTimer&&(clearInterval(this.pollTimer),this.pollTimer=null),this.countdownTimer&&(clearInterval(this.countdownTimer),this.countdownTimer=null))}async updateServerOperations(t,e){if(0!==(t=(t=Array.isArray(t)?t:t.includes(",")?t.split(","):[t]).filter((t=>{let s=this.getQueue(t);return this.getAllowedActions(s.status).includes(e)}))).length){["cancel","dismiss"].includes(e)&&t.forEach((t=>{this.removeOperationFromUI(t)}));try{const s=`${this.config.apiBase}${this.config.endpoint}`,i=await fetch(s,{method:"POST",headers:{"Content-Type":"application/json",...this.headers},body:JSON.stringify({ids:t,action:e})});if(!i.ok){const t=await i.json().catch((()=>{}));throw new Error(t.message||`${e} failed: ${i.status}`)}const a=await i.json();if(!a.success)throw new Error(a.message||`${e} operation failed`);return["cancel","dismiss"].includes(e)?t.forEach((t=>{let s=this.getQueue(t);this.notify(`${e}-operation`,s),this.clearQueue(t)})):(t.forEach((t=>{let s=this.getQueue(t);this.notify(`${e}-operation`,s),s.status="queued",s.retries=0,this.setQueue(s),this.updateOperationStatus(s.id,s.status)})),this.startActivityTracking()),this.updateUI(),a}catch(s){const i=await window.jvbError.log(s,{component:"QueueManager",operation:"performQueueAction",action:e,operationIds:t,itemCount:t.length},(()=>this.updateServerOperations(t,e)));if(i.retried)return i;throw s}}}getAllowedActions(t){return{queued:["cancel"],localProcessing:["cancel"],pending:["cancel"],processing:[],completed:["dismiss"],failed:["retry","dismiss"],failed_permanent:["dismiss"]}[t]||[]}initListeners(){this.clickHandler=this.handleClick.bind(this),this.changeHandler=this.handleChange.bind(this),document.addEventListener("click",this.clickHandler),this.ui.panel?.addEventListener("change",this.changeHandler),this.handleOnline=()=>{this.updateStatusPanel(),this.hasQueuedOperations()&&this.processQueue()},this.handleOffline=()=>this.updateStatusPanel("offline"),this.handleBeforeUnload=t=>{if(this.getOperationsByStatus(["queued","uploading"]).length>0)return t.preventDefault(),"You have unsaved changes in the queue."},window.addEventListener("online",this.handleOnline),window.addEventListener("offline",this.handleOffline),window.addEventListener("beforeunload",this.handleBeforeUnload)}handleClick(t){if(t.target.closest(this.selectors.panel,this.selectors.toggle))if(t.target.closest(this.selectors.refreshButton))this.store.fetch();else if(t.target.closest(this.selectors.clearButton)){const t=this.getOperationsByStatus("completed");if(t.length>0){const e=t.map((t=>t.id));this.updateServerOperations(e,"dismiss")}}else if(t.target.closest(this.selectors.retryButton)){const t=this.getOperationsByStatus("failed");if(t.length>0){const e=t.map((t=>t.id));this.updateServerOperations(e,"retry")}}else if(t.target.closest("[data-action]")){const e=t.target.closest("[data-action]"),s=e.closest("[data-id]")?.dataset.id;s&&this.updateServerOperations(s,e.dataset.action)}else if(t.target.closest(".filters [data-filter]")){const e=t.target.closest("[data-filter]").dataset.filter;this.setFilter(e)}}handleChange(t){}initUI(){if(this.icons={queued:"arrows-clockwise",localProcessing:"arrows-clockwise",uploading:"syncing",pending:"cloud",processing:"syncing",completed:"cloud-check",failed:"cloud-warning",failed_permanent:"cloud-warning"},this.selectors={panel:"aside#queue",toggle:"button.qtoggle",refreshButton:"button.refreshNow",countdown:".countdown",indicator:".qtoggle .indicator",count:".qtoggle .count",popup:".popup",itemsContainer:".qitems",clearButton:".dismiss-all",retryButton:".retry-all",filters:{all:'.filters [data-filter="all"]',received:'.filters [data-filter="queued"]',localProcessing:'.filters [data-filter="localProcessing"]',uploading:'.filters [data-filter="uploading"]',pending:'.filters [data-filter="pending"]',processing:'.filters [data-filter="processing"]',completed:'.filters [data-filter="completed"]',failed:'.filters [data-filter="failed"]'}},this.ui={panel:document.querySelector(this.selectors.panel),toggle:document.querySelector(this.selectors.toggle),count:document.querySelector(this.selectors.count),indicator:document.querySelector(this.selectors.indicator)},this.ui.panel){for(let[t,e]of Object.entries(this.selectors))if(!["panel","toggle","count","indicator"].includes(t))if("object"==typeof e){this.ui[t]={};for(let[s,i]of Object.entries(e))this.ui[t][s]=this.ui.panel.querySelector(i)}else this.ui[t]=this.ui.panel.querySelector(e)}else this.canUpdateUI=!1}updateUI(){if(!this.canUpdateUI)return;const t=this.getQueueStats();if(this.ui.count){const e=t.total-t.completed;this.ui.count.textContent=e>0?e:"",this.ui.count.style.display=e>0?"":"none"}if(this.ui.indicator){const e=t.queued>0||t.uploading>0||t.pending>0||t.processing>0;this.ui.indicator.classList.toggle("active",e)}let e=this.getOperationsByStatus("failed"),s=this.getOperationsByStatus("completed");this.ui.clearButton.disabled=0===s.length,this.ui.retryButton.disabled=0===e.length,Object.entries(this.ui.filters).forEach((([e,s])=>{const i="all"===e?t.total:t[e]||0,a=s.querySelector(".count");a&&(a.textContent=i>0?i:""),s.setAttribute("data-count",i)})),this.renderOperations()}getStatusLabel(t){return{queued:"Queued",localProcessing:"Processing locally",uploading:"Uploading",pending:"Waiting on server",processing:"Processing",completed:"Completed",failed:"Failed (will retry)",failed_permanent:"Failed permanently"}[t]||t}getItemMessage(t){if(t.message)return t.message;if(t.error_message)return t.error_message;switch(t.status){case"queued":return"Waiting to send...";case"uploading":return"Sending to server...";case"pending":return t.position?`Position ${t.position} in queue`:"In server queue";case"processing":return t.progress?`${t.progress}% complete`:"Processing...";case"completed":return"Successfully completed";case"failed":return`Failed: ${t.lastError||"Unknown error"} (Retry ${t.retries}/${this.config.maxRetries})`;case"failed_permanent":return`Failed: ${t.lastError||"Unknown error"}`;default:return""}}calculateProgress(t){if(t.progress)return t.progress;return{queued:10,uploading:25,pending:40,processing:70,completed:100,failed:0,failed_permanent:0}[t.status]||0}getQueueStats(){const t={};return this.statuses.forEach((e=>{t[e]=0})),Array.from(this.store.data.values()).forEach((e=>{t.hasOwnProperty(e.status)&&t[e.status]++})),t.total=Array.from(this.store.data.values()).length,t}renderOperations(){if(!this.ui.itemsContainer)return;const t=this.getActiveFilter(),e=this.getFilteredOperations(t);if(window.removeChildren(this.ui.itemsContainer),0===e.length){let t=window.getTemplate("emptyQueue");this.ui.itemsContainer.append(t),this.a11y.announce("Nothing queued.")}else{let t=this.ui.itemsContainer.querySelector(".emptyQueue");t&&t.remove(),e.forEach((t=>{const e=this.createOperationUI(t);this.ui.itemsContainer.appendChild(e)}))}}createOperationUI(t){const e=window.getTemplate("queueItem");return e.dataset.id=t.id,this.updateOperationUI(t,e),e}updateOperationUI(t,e=null){e||(e=this.ui.itemsContainer?.querySelector(`[data-id="${t.id}"]`)),e||(e=this.createOperationUI(t)),this.statuses.forEach((t=>e.classList.remove(t))),e.classList.add(t.status);let s="";t.updated_at?s=window.formatTimeAgo(new Date(t.updated_at)):t.created_at&&(s=window.formatTimeAgo(new Date(t.created_at)));const i=this.calculateProgress(t),a=e.querySelector(".type"),n=e.querySelector(".status"),r=e.querySelector(".info .details"),o=e.querySelector(".info .time"),l=e.querySelector(".progress .fill");if(a&&(a.textContent=t.title),n){n.querySelector(".icon")?.remove();let e=this.getStatusLabel(t.status);n.title=e,n.prepend(window.getIcon(this.icons[t.status])),n.querySelector("span").textContent=e}r&&(r.textContent=this.getItemMessage(t)),o&&(o.textContent=s),l&&(l.style.width=`${i}%`);const u=e.querySelector(".actions");u&&this.updateActionButtons(t,u)}updateActionButtons(t,e){switch(window.removeChildren(e),t.status){case"queued":case"localProcessing":case"pending":const s=window.getTemplate("button");s.classList.add("cancel"),s.dataset.action="cancel",s.textContent="Cancel",e.appendChild(s);break;case"failed":case"failed_permanent":const i=window.getTemplate("button"),a=window.getTemplate("button");i.classList.add("retry"),i.textContent="Retry",i.disabled=t.retries>=this.maxRetries,i.dataset.action="retry",a.classList.add("dismiss"),a.textContent="Dismiss",a.dataset.action="dismiss",e.appendChild(i),e.appendChild(a);break;case"completed":const n=window.getTemplate("button");n.dataset.action="dismiss",n.classList.add("dismiss"),n.textContent="Dismiss",e.appendChild(n)}}removeOperationFromUI(t){const e=this.ui.itemsContainer?.querySelector(`[data-id="${t}"]`);e&&(e.style.opacity="0",e.style.transform="scale(0.9)",setTimeout((()=>e.remove()),300))}updateCountdown(){if(!this.ui.countdown||!this.isPolling)return;let t=this.config.pollInterval/1e3;this.countdownTimer=setInterval((()=>{t--,this.ui.countdown.textContent=t,t<=0&&(clearInterval(this.countdownTimer),this.isPolling&&setTimeout((()=>this.updateCountdown()),100))}),1e3)}updateStatusPanel(t){this.ui.panel?.classList.remove(...this.classes),this.classes.includes(t)&&this.ui.panel?.classList.add(t)}setFilter(t){Object.values(this.ui.filters).forEach((e=>{e&&e.classList.toggle("active",e.dataset.filter===t)})),this.activeFilter=t,this.renderOperations()}getActiveFilter(){const t=this.ui.panel?.querySelector(".filter.active");return t?.dataset.filter||"all"}getFilteredOperations(t){const e=Array.from(this.store.data.values());return"all"===t?e:e.filter((e=>e.status===t))}showPopup(t,e="success"){if(!this.ui.popup)return;const s=this.ui.popup.querySelector("span");s&&(s.textContent=t),this.ui.popup.className=`popup ${e} show`,setTimeout((()=>{this.ui.popup.classList.remove("show")}),3e3)}getOperationsByStatus(t,e=!0){return Array.isArray(t)||"string"!=typeof t||(t=[t]),e?Array.from(this.store.data.values()).filter((e=>t.includes(e.status))):Array.from(this.store.data.values()).filter((e=>!t.includes(e.status)))}async hasQueuedOperations(){return(await this.store.query("status","queued")).length>0}subscribe(t){return this.subscribers.add(t),()=>this.subscribers.delete(t)}notify(t,e){this.subscribers.forEach((s=>s(t,e)))}destroy(){this.stopPolling(),this.stopActivityTracking(),this.clickHandler&&document.removeEventListener("click",this.clickHandler),this.keyHandler&&document.removeEventListener("keydown",this.keyHandler),this.subscribers.clear()}}document.addEventListener("DOMContentLoaded",(function(){window.jvbQueue=new t}))})();
\ No newline at end of file
diff --git a/assets/js/min/quill.min.js b/assets/js/min/quill.min.js
index 0876418..e1b1aca 100644
--- a/assets/js/min/quill.min.js
+++ b/assets/js/min/quill.min.js
@@ -1 +1 @@
-window.jvbQuill=function(t){t.querySelectorAll("textarea[data-editor=true]").forEach((t=>{let n,e,i;if(t.parentNode.querySelector(".editor-container"))n=t.parentNode.querySelector(".editor-container"),e=n.querySelector(".editor"),i=n.querySelector(".toolbar");else{n=document.createElement("div"),n.className="editor-container",e=document.createElement("div"),e.className="editor",i=document.createElement("div"),i.className="toolbar";const l=!0===t.dataset.allowimage?`<button type="button" class="ql-jvb_image">\n                    ${dashboardSettings.icons.image}\n                </button>`:"";i.id=`toolbar-${t.id}`,i.innerHTML=`\n                <span class="ql-formats">\n                    <button type="button" class="ql-p">\n                        ${jvbSettings.icons.paragraph}\n                    </button>\n                    <button type="button" class="ql-h1">\n                        ${jvbSettings.icons.h1}\n                    </button>\n                    <button type="button" class="ql-h2">\n                        ${jvbSettings.icons.h2}\n                    </button>\n                    <button type="button" class="ql-h3">\n                        ${jvbSettings.icons.h3}\n                    </button>\n                </span>\n                <span class="ql-formats">\n                    <button type="button" class="ql-jvb_bold">\n                        ${jvbSettings.icons.bold}\n                    </button>\n                    <button type="button" class="ql-jvb_italic">\n                        ${jvbSettings.icons.italic}\n                    </button>\n                    <button type="button" class="ql-jvb_underline">\n                        ${jvbSettings.icons.underline}\n                    </button>\n                    <button type="button" class="ql-jvb_strike">\n                        ${jvbSettings.icons.strike}\n                    </button>\n                </span>\n                <span class="ql-formats">\n                     <button type="button" class="ql-jvb_list" value="bullet">\n                        ${jvbSettings.icons["list-bullets"]}\n                    </button>\n                    <button type="button" class="ql-jvb_list" value="ordered">\n                        ${jvbSettings.icons["list-numbers"]}\n                    </button>\n                </span>\n                <span class="ql-formats">\n                     <button type="button" class="ql-jvb_align" value="left">\n                        ${jvbSettings.icons["align-left"]}\n                    </button>\n                     <button type="button" class="ql-jvb_align" value="center">\n                        ${jvbSettings.icons["align-center"]}\n                    </button>\n                     <button type="button" class="ql-jvb_align" value="right">\n                        ${jvbSettings.icons["align-right"]}\n                    </button>\n                </span>\n                <span class="ql-formats">\n                     <button type="button" class="ql-jvb_link">\n                        ${jvbSettings.icons.link}\n                    </button>\n                    ${l}\n                </span>\n            `,n.appendChild(i),n.appendChild(e),t.parentNode.insertBefore(n,t),t.style.display="none",e.innerHTML=t.value}const l=new Quill(e,{theme:"snow",modules:{toolbar:{container:i,handlers:{p:function(){this.quill.format("header",!1)},h1:function(){this.quill.format("header",1)},h2:function(){this.quill.format("header",2)},h3:function(){this.quill.format("header",3)},jvb_bold:function(){this.quill.format("bold",!0)},jvb_italic:function(){this.quill.format("italic",!0)},jvb_strike:function(){this.quill.format("strike",!0)},jvb_underline:function(){this.quill.format("underline",!0)},jvb_align:function(t){this.quill.format("align",t!==this.quill.getFormat().list&&t)},jvb_list:function(t){this.quill.format("list",t!==this.quill.getFormat().list&&t)},jvb_link:function(t){if(t){const t=this.quill.getSelection();if(null==t||0===t.length)return;this.quill.getText(t.index,t.length);const n=this.quill.getFormat(t).link,e=document.createElement("dialog");e.className="quill-link-modal",e.innerHTML=`\n                                    <div class="quill-link-modal-content ">\n                                        <label for="link">Enter URL</label>\n                                        <input type="url" id="link" placeholder="Enter URL" value="${n||""}" />\n                                        <div class="buttons">\n                                            <button type="button" class="save">Save</button>\n                                            ${n?'<button type="button" class="remove">Remove</button>':""}\n                                            <button type="button" class="cancel">Cancel</button>\n                                        </div>\n                                    </div>\n                                `,document.body.appendChild(e),e.showModal();const i=e.querySelector("input");i.focus(),e.querySelector(".save").addEventListener("click",(()=>{const t=i.value;t&&this.quill.format("link",t),e.remove()}));const l=e.querySelector(".remove");l&&l.addEventListener("click",(()=>{this.quill.format("link",!1),e.remove()})),e.querySelector(".cancel").addEventListener("click",(()=>{e.remove()})),i.addEventListener("keyup",(t=>{if("Enter"===t.key){const t=i.value;t&&this.quill.format("link",t),e.remove()}}))}},jvb_image:function(){const t=document.createElement("input");t.setAttribute("type","file"),t.setAttribute("accept","image/jpeg,image/png,image/gif,image/webp"),t.style.display="none",document.body.appendChild(t),t.onchange=async n=>{const e=n.target.files?.[0];if(!e)return;if(e.size>5242880)return this.quill.insertText(i.index,"File too large. Maximum size is 5MB",{color:"#f00",italic:!0},!0),void t.remove();const i=this.quill.getSelection(!0),l=new FormData;l.append("image",e),objectID&&l.append("post_id",objectID),window.jvbLoading&&window.jvbLoading.showLoading("Uploading image...","Processing Upload");try{const t=await fetch(`${jvbSettings.api}uploads/`,{method:"POST",headers:{"X-WP-Nonce":jvbSettings.nonce},body:l});if(!t.ok)throw new Error("Upload failed");const n=await t.json();this.quill.insertEmbed(i.index,"image",n.url)}catch(t){this.handleError("Upload error:",t),this.quill.insertText(i.index,"Failed to upload image. Please try again.",{color:"#f00",italic:!0},!0)}finally{window.jvbLoading&&window.jvbLoading.hide(),t.remove()}},t.click()}}},history:{delay:2e3,maxStack:500},clipboard:{matchVisual:!1}}});l.on("selection-change",(function(t){const n=i.querySelector(".ql-align");if(n){if(t&&0===t.length){const[e]=this.quill.getLeaf(t.index);if(e&&e.domNode&&"IMG"===e.domNode.tagName)return void(n.style.display="inline-block")}n.style.display="none"}})),l.on("text-change",(()=>{t.value=l.root.innerHTML,t.dispatchEvent(new Event("change",{bubbles:!0}))}))}))};
\ No newline at end of file
+window.jvbQuill=function(t){t.querySelectorAll("textarea[data-editor=true]").forEach((t=>{let n,e,i;if(t.parentNode.querySelector(".editor-container"))n=t.parentNode.querySelector(".editor-container"),e=n.querySelector(".editor"),i=n.querySelector(".toolbar");else{n=document.createElement("div"),n.className="editor-container",e=document.createElement("div"),e.className="editor",i=document.createElement("div"),i.className="toolbar";const o=!0===t.dataset.allowimage?`<button type="button" class="ql-jvb_image">\n                    ${dashboardSettings.icons.image}\n                </button>`:"";i.id=`toolbar-${t.id}`,i.innerHTML=`\n                <span class="ql-formats">\n                    <button type="button" class="ql-p">\n                        <i class="icon icon-paragraph"></i>\n                    </button>\n                    <button type="button" class="ql-h1">\n                        <i class="icon icon-text-h-one"></i>\n                    </button>\n                    <button type="button" class="ql-h2">\n                        <i class="icon icon-text-h-two"></i>\n                    </button>\n                    <button type="button" class="ql-h3">\n                        <i class="icon icon-text-h-three"></i>\n                    </button>\n                </span>\n                <span class="ql-formats">\n                    <button type="button" class="ql-jvb_bold">\n                        <i class="icon icon-text-b-fi"></i>\n                    </button>\n                    <button type="button" class="ql-jvb_italic">\n                        <i class="icon icon-text-italic"></i>\n                    </button>\n                    <button type="button" class="ql-jvb_underline">\n                        <i class="icon icon-text-underline"></i>\n                    </button>\n                    <button type="button" class="ql-jvb_strike">\n                        <i class="icon icon-text-strikethrough"></i>\n                    </button>\n                </span>\n                <span class="ql-formats">\n                     <button type="button" class="ql-jvb_list" value="bullet">\n                        <i class="icon icon-list-dashes"></i>\n                    </button>\n                    <button type="button" class="ql-jvb_list" value="ordered">\n                        <i class="icon icon-list-numbers"></i>\n                    </button>\n                </span>\n                <span class="ql-formats">\n                     <button type="button" class="ql-jvb_align" value="left">\n                        <i class="icon icon-text-align-left"></i>\n                    </button>\n                     <button type="button" class="ql-jvb_align" value="center">\n                        <i class="icon icon-text-align-center"></i>\n                    </button>\n                     <button type="button" class="ql-jvb_align" value="right">\n                        <i class="icon icon-text-align-right"></i>\n                    </button>\n                </span>\n                <span class="ql-formats">\n                     <button type="button" class="ql-jvb_link">\n                        <i class="icon icon-link"></i>\n                    </button>\n                    ${o}\n                </span>\n            `,n.appendChild(i),n.appendChild(e),t.parentNode.insertBefore(n,t),t.style.display="none",e.innerHTML=t.value}const o=new Quill(e,{theme:"snow",modules:{toolbar:{container:i,handlers:{p:function(){this.quill.format("header",!1)},h1:function(){this.quill.format("header",1)},h2:function(){this.quill.format("header",2)},h3:function(){this.quill.format("header",3)},jvb_bold:function(){this.quill.format("bold",!0)},jvb_italic:function(){this.quill.format("italic",!0)},jvb_strike:function(){this.quill.format("strike",!0)},jvb_underline:function(){this.quill.format("underline",!0)},jvb_align:function(t){this.quill.format("align",t!==this.quill.getFormat().list&&t)},jvb_list:function(t){this.quill.format("list",t!==this.quill.getFormat().list&&t)},jvb_link:function(t){if(t){const t=this.quill.getSelection();if(null==t||0===t.length)return;this.quill.getText(t.index,t.length);const n=this.quill.getFormat(t).link,e=document.createElement("dialog");e.className="quill-link-modal",e.innerHTML=`\n                                    <div class="quill-link-modal-content ">\n                                        <label for="link">Enter URL</label>\n                                        <input type="url" id="link" placeholder="Enter URL" value="${n||""}" />\n                                        <div class="buttons">\n                                            <button type="button" class="save">Save</button>\n                                            ${n?'<button type="button" class="remove">Remove</button>':""}\n                                            <button type="button" class="cancel">Cancel</button>\n                                        </div>\n                                    </div>\n                                `,document.body.appendChild(e),e.showModal();const i=e.querySelector("input");i.focus(),e.querySelector(".save").addEventListener("click",(()=>{const t=i.value;t&&this.quill.format("link",t),e.remove()}));const o=e.querySelector(".remove");o&&o.addEventListener("click",(()=>{this.quill.format("link",!1),e.remove()})),e.querySelector(".cancel").addEventListener("click",(()=>{e.remove()})),i.addEventListener("keyup",(t=>{if("Enter"===t.key){const t=i.value;t&&this.quill.format("link",t),e.remove()}}))}},jvb_image:function(){const t=document.createElement("input");t.setAttribute("type","file"),t.setAttribute("accept","image/jpeg,image/png,image/gif,image/webp"),t.style.display="none",document.body.appendChild(t),t.onchange=async n=>{const e=n.target.files?.[0];if(!e)return;if(e.size>5242880)return this.quill.insertText(i.index,"File too large. Maximum size is 5MB",{color:"#f00",italic:!0},!0),void t.remove();const i=this.quill.getSelection(!0),o=new FormData;o.append("image",e),objectID&&o.append("post_id",objectID),window.jvbLoading&&window.jvbLoading.showLoading("Uploading image...","Processing Upload");try{const t=await fetch(`${jvbSettings.api}uploads/`,{method:"POST",headers:{"X-WP-Nonce":jvbSettings.nonce},body:o});if(!t.ok)throw new Error("Upload failed");const n=await t.json();this.quill.insertEmbed(i.index,"image",n.url)}catch(t){this.handleError("Upload error:",t),this.quill.insertText(i.index,"Failed to upload image. Please try again.",{color:"#f00",italic:!0},!0)}finally{window.jvbLoading&&window.jvbLoading.hide(),t.remove()}},t.click()}}},history:{delay:2e3,maxStack:500},clipboard:{matchVisual:!1}}});o.on("selection-change",(function(t){const n=i.querySelector(".ql-align");if(n){if(t&&0===t.length){const[e]=this.quill.getLeaf(t.index);if(e&&e.domNode&&"IMG"===e.domNode.tagName)return void(n.style.display="inline-block")}n.style.display="none"}})),o.on("text-change",(()=>{t.value=o.root.innerHTML,t.dispatchEvent(new Event("change",{bubbles:!0}))}))}))};
\ No newline at end of file
diff --git a/assets/js/min/referral.min.js b/assets/js/min/referral.min.js
index 68cd4e7..d4800d3 100644
--- a/assets/js/min/referral.min.js
+++ b/assets/js/min/referral.min.js
@@ -1 +1 @@
-(()=>{class e{constructor(){this.container=document.querySelector(".jvb-referral"),this.container&&(this.a11y=window.jvbA11y,this.toggle=document.querySelector('button[data-action="toggle-referral"]'),this.initElements(),this.initListeners(),this.checkForReferral())}initElements(){this.selectors={copy:"button.copy",login:".login",submit:"[type=submit]"},this.forms=this.container.querySelectorAll("form"),console.log(this.forms),this.popup=new window.jvbPopup({toggle:this.toggle,popup:this.container,name:"Referral Box",onOpen:()=>{this.forms.forEach((e=>{e.addEventListener("submit",this.submitHandler)})),this.container.addEventListener("click",this.clickHandler),this.container.addEventListener("input",this.inputHandler)},onClose:()=>{this.forms.forEach((e=>{e.removeEventListener("submit",this.submitHandler)})),this.container.removeEventListener("click",this.clickHandler),this.container.removeEventListener("input",this.inputHandler)}}),this.tabs=null,this.container.querySelector("nav.tabs")&&(this.tabs=new window.jvbTabs(this.container,{updateURL:!1})),this.ui=window.uiFromSelectors(this.selectors,this.container)}initListeners(){this.clickHandler=this.handleClick.bind(this),this.inputHandler=this.handleInput.bind(this),this.submitHandler=this.handleFormSubmit.bind(this),this.changeHandler=this.handleChange.bind(this)}handleClick(e){if(e.target.classList.contains(".copy")){let t=e.target.dataset.target,r=this.container.querySelector(`#${t}`);r=!!r&&r.textContent,r&&this.handleCopy(e.target,r)}}handleChange(e){"referral-code"===e.target.id&&window.debouncer.schedule("check-referral",(()=>this.makeRequest("referrals/check-code",{code:e.target.value})))}handleInput(e){"referral-code"===e.target.id&&(e.target.value=e.target.value.toUpperCase())}initShareWidget(){this.initCopyButton(),this.loadStats()}async checkForReferral(){const e=this.getUrlParameter("seeReferral"),t=this.getUrlParameter("ref");if(!e&&!t)return;if(!t)return void this.popup.openPopup();const r=this.container.querySelector("#referral-code-input");if(!r)return;const n=t.toUpperCase();r.value=n,r.readOnly=!0,this.popup.togglePopup();try{const e=await this.validateCodeOnly(n);if(e.success){this.showReferrerBanner(e.referrer_name,n);const t=this.container.querySelector("#referral-name");t&&t.focus()}else r.readOnly=!1,this.showMessage("This referral link is invalid. Please enter a valid code.","error")}catch(e){console.error("Error validating code:",e),r.readOnly=!1}this.removeUrlParameter("ref")}getUrlParameter(e){return new URLSearchParams(window.location.search).get(e)}removeUrlParameter(e){const t=new URL(window.location);t.searchParams.delete(e),window.history.replaceState({},document.title,t.toString())}async validateCodeOnly(e){const t=await fetch(`${jvbSettings.api}/referrals/check-code`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({code:e})});return await t.json()}showReferrerBanner(e,t){const r=this.container.querySelector(".referral-header");if(!r)return;const n=document.createElement("div");n.className="referrer-banner",n.innerHTML=`\n            <div class="banner-icon">🎉</div>\n            <div class="banner-content">\n                <strong>${window.escapeHtml(e)}</strong> referred you!\n                <div class="banner-code">Code: <code>${window.escapeHtml(t)}</code></div>\n            </div>\n        `,r.parentNode.insertBefore(n,r.nextSibling);const a=r.querySelector("h3");a&&(a.textContent="Complete Your Registration");const o=r.querySelector("p");o&&(o.textContent="Enter your details below to claim your welcome reward!")}handleCopy(e,t=""){if(""===t||"string"!=typeof t)return;let r=e.textContent;(navigator.clipboard||navigator.clipboard.writeText)&&navigator.clipboard.writeText(t).then((()=>{e.textContent="Copied!",e.style.background="#00a32a",setTimeout((()=>{e.textContent=r,e.style.background=""}),2e3)}))}async loadStats(){if(this.container.querySelector(".referral-stats"))try{const e=await fetch(`${jvbSettings.api}/referrals/stats`,{headers:{"X-WP-Nonce":jvbSettings.nonce}}),t=await e.json();t.success&&t.stats&&this.updateStats(t.stats)}catch(e){console.error("Error loading stats:",e)}}updateStats(e){const t={total:this.container.querySelector('[data-stat="total"]'),treated:this.container.querySelector('[data-stat="treated"]'),pending:this.container.querySelector('[data-stat="pending"]'),rewards:this.container.querySelector('[data-stat="rewards"]')};t.total&&(t.total.textContent=e.total_referrals||0),t.treated&&(t.treated.textContent=e.treated_count||0),t.pending&&(t.pending.textContent=e.pending_count||0),t.rewards&&(t.rewards.textContent="$"+parseFloat(e.available_rewards||0).toFixed(2))}async handleFormSubmit(e){console.log("Form Submission!"),window.debouncer.cancel("check-referral"),e.preventDefault(),console.log("Still working?");const t=e.target,r=new FormData(t);let n={};this.setFormLoading(!0,t);try{let e={success:!1,message:""};console.log(t),console.log(t.id),"referral-code-form"===t.id?(r.get("name")||(e.message+="We need your name to know who you are."),r.get("email")||(e.message+="We need your email to confirm you have access to it."),r.get("referral_code")||(e.message+="We need the referral code to know who sent you."),r.get("name")&&r.get("email")&&r.get("referral_code")&&(n.name=r.get("name"),n.email=r.get("email"),n.code=r.get("referral_code"),e=await this.makeRequest("referrals/register",n))):"login-form"===t.id&&r.get("login-email")&&(n.type="login",n.email=r.get("login-email"),n.context={},n.context.redirect_to=window.location.href+"?seeReferral=1",console.log("Making Request with: ",n),e=await this.makeRequest("magic-link",n)),e.success?this.handleSuccess(e):(this.showMessage(e.message||"Something went wrong. Please try again.","error"),this.setFormLoading(!1,t))}catch(e){console.error("Error registering:",e),this.showMessage("Something went wrong. Please try again.","error"),this.setFormLoading(!1,t)}finally{this.setFormLoading(!1,t)}}async makeRequest(e,t){if(!["magic-link","referrals/register","referrals/check-code"].includes(e))return{success:!1,message:"Something went wrong (Invalid endpoint)."};console.log("Endpoint: ",e),console.log("Data: ",t);const r=await fetch(`${jvbSettings.api}${e}`,{method:"POST",headers:{"Content-Type":"application/json","X-WP-Nonce":jvbSettings.nonce},body:JSON.stringify(t)});return await r.json()}handleSuccess(e){this.container.querySelectorAll("form").forEach((e=>{window.fade(e,!1)}));const t=this.container.querySelector(".success-message");t&&(t.hidden=!1,t.scrollIntoView({behavior:"smooth",block:"center"}),this.dispatchEvent("emailSent",{email:e.email}))}setFormLoading(e,t){t.querySelectorAll("input").forEach((t=>t.disabled=e));let r=t.querySelector(".status"),n=r.querySelector(".message");r.hidden=e,r.classList.toggle("loading",e),n.textContent=e?"Checking with server...":""}showMessage(e,t="success"){const r=this.container.querySelector("#referral-message");r&&(r.textContent=e,r.className="message "+t,r.style.display="block","error"===t&&setTimeout((()=>{r.style.display="none"}),5e3))}dispatchEvent(e,t){const r=new CustomEvent("referralWidget:"+e,{detail:t,bubbles:!0});this.container.dispatchEvent(r)}}document.addEventListener("DOMContentLoaded",(()=>{window.jvbReferral=new e}))})();
\ No newline at end of file
+(()=>{class e{constructor(){this.container=document.querySelector(".jvb-referral"),this.container&&(this.a11y=window.jvbA11y,this.toggle=document.querySelector('button[data-action="toggle-referral"]'),this.initElements(),this.initListeners(),this.checkForReferral())}initElements(){this.selectors={copy:"button.copy",login:".login",submit:"[type=submit]"},this.forms=this.container.querySelectorAll("form"),console.log(this.forms),this.popup=new window.jvbPopup({toggle:this.toggle,popup:this.container,name:"Referral Box",onOpen:()=>{this.forms.forEach((e=>{e.addEventListener("submit",this.submitHandler)})),this.container.addEventListener("click",this.clickHandler),this.container.addEventListener("input",this.inputHandler)},onClose:()=>{this.forms.forEach((e=>{e.removeEventListener("submit",this.submitHandler)})),this.container.removeEventListener("click",this.clickHandler),this.container.removeEventListener("input",this.inputHandler)}}),this.tabs=null,this.container.querySelector("nav.tabs")&&(this.tabs=new window.jvbTabs(this.container,{updateURL:!1})),this.ui=window.uiFromSelectors(this.selectors,this.container)}initListeners(){this.clickHandler=this.handleClick.bind(this),this.inputHandler=this.handleInput.bind(this),this.submitHandler=this.handleFormSubmit.bind(this),this.changeHandler=this.handleChange.bind(this)}handleClick(e){if(e.target.classList.contains(".copy")){let t=e.target.dataset.target,r=this.container.querySelector(`#${t}`);r=!!r&&r.textContent,r&&this.handleCopy(e.target,r)}}handleChange(e){"referral-code"===e.target.id&&window.debouncer.schedule("check-referral",(()=>this.makeRequest("referrals/check-code",{code:e.target.value})))}handleInput(e){"referral-code"===e.target.id&&(e.target.value=e.target.value.toUpperCase())}initShareWidget(){this.initCopyButton(),this.loadStats()}async checkForReferral(){const e=this.getUrlParameter("seeReferral"),t=this.getUrlParameter("ref");if(!e&&!t)return;if(!t)return void this.popup.openPopup();const r=this.container.querySelector("#referral-code-input");if(!r)return;const n=t.toUpperCase();r.value=n,r.readOnly=!0,this.popup.togglePopup();try{const e=await this.validateCodeOnly(n);if(e.success){this.showReferrerBanner(e.referrer_name,n);const t=this.container.querySelector("#referral-name");t&&t.focus()}else r.readOnly=!1,this.showMessage("This referral link is invalid. Please enter a valid code.","error")}catch(e){console.error("Error validating code:",e),r.readOnly=!1}this.removeUrlParameter("ref")}getUrlParameter(e){return new URLSearchParams(window.location.search).get(e)}removeUrlParameter(e){const t=new URL(window.location);t.searchParams.delete(e),window.history.replaceState({},document.title,t.toString())}async validateCodeOnly(e){const t=await fetch(`${jvbSettings.api}/referrals/check-code`,{method:"POST",headers:{"Content-Type":"application/json"},body:JSON.stringify({code:e})});return await t.json()}showReferrerBanner(e,t){const r=this.container.querySelector(".referral-header");if(!r)return;const n=document.createElement("div");n.className="referrer-banner",n.innerHTML=`\n            <div class="banner-icon">🎉</div>\n            <div class="banner-content">\n                <strong>${window.escapeHtml(e)}</strong> referred you!\n                <div class="banner-code">Code: <code>${window.escapeHtml(t)}</code></div>\n            </div>\n        `,r.parentNode.insertBefore(n,r.nextSibling);const a=r.querySelector("h3");a&&(a.textContent="Complete Your Registration");const o=r.querySelector("p");o&&(o.textContent="Enter your details below to claim your welcome reward!")}handleCopy(e,t=""){if(""===t||"string"!=typeof t)return;let r=e.textContent;(navigator.clipboard||navigator.clipboard.writeText)&&navigator.clipboard.writeText(t).then((()=>{e.textContent="Copied!",e.style.background="#00a32a",setTimeout((()=>{e.textContent=r,e.style.background=""}),2e3)}))}async loadStats(){if(this.container.querySelector(".referral-stats"))try{const e=await fetch(`${jvbSettings.api}/referrals/stats`,{headers:{"X-WP-Nonce":jvbSettings.nonce}}),t=await e.json();t.success&&t.stats&&this.updateStats(t.stats)}catch(e){console.error("Error loading stats:",e)}}updateStats(e){const t={total:this.container.querySelector('[data-stat="total"]'),treated:this.container.querySelector('[data-stat="treated"]'),pending:this.container.querySelector('[data-stat="pending"]'),rewards:this.container.querySelector('[data-stat="rewards"]')};t.total&&(t.total.textContent=e.total_referrals||0),t.treated&&(t.treated.textContent=e.treated_count||0),t.pending&&(t.pending.textContent=e.pending_count||0),t.rewards&&(t.rewards.textContent="$"+parseFloat(e.available_rewards||0).toFixed(2))}async handleFormSubmit(e){console.log("Form Submission!"),window.debouncer.cancel("check-referral"),e.preventDefault(),console.log("Still working?");const t=e.target,r=new FormData(t);let n={};this.setFormLoading(!0,t);try{let e={success:!1,message:""};console.log(t),console.log(t.id),"referral-code-form"===t.id?(r.get("name")||(e.message+="We need your name to know who you are."),r.get("email")||(e.message+="We need your email to confirm you have access to it."),r.get("referral_code")||(e.message+="We need the referral code to know who sent you."),r.get("name")&&r.get("email")&&r.get("referral_code")&&(n.name=r.get("name"),n.email=r.get("email"),n.code=r.get("referral_code"),e=await this.makeRequest("referrals/register",n))):"login-form"===t.id&&r.get("login-email")&&(n.type="login",n.email=r.get("login-email"),n.context={},n.context.redirect_to=window.location.href+"?seeReferral=1",console.log("Making Request with: ",n),e=await this.makeRequest("magic",n)),e.success?this.handleSuccess(e):(this.showMessage(e.message||"Something went wrong. Please try again.","error"),this.setFormLoading(!1,t))}catch(e){console.error("Error registering:",e),this.showMessage("Something went wrong. Please try again.","error"),this.setFormLoading(!1,t)}finally{this.setFormLoading(!1,t)}}async makeRequest(e,t){if(!["magic","referrals/register","referrals/check-code"].includes(e))return{success:!1,message:"Something went wrong (Invalid endpoint)."};console.log("Endpoint: ",e),console.log("Data: ",t);const r=await fetch(`${jvbSettings.api}${e}`,{method:"POST",headers:{"Content-Type":"application/json","X-WP-Nonce":jvbSettings.nonce},body:JSON.stringify(t)});return await r.json()}handleSuccess(e){this.container.querySelectorAll("form").forEach((e=>{window.fade(e,!1)}));const t=this.container.querySelector(".success-message");t&&(t.hidden=!1,t.scrollIntoView({behavior:"smooth",block:"center"}),this.dispatchEvent("emailSent",{email:e.email}))}setFormLoading(e,t){t.querySelectorAll("input").forEach((t=>t.disabled=e));let r=t.querySelector(".status"),n=r.querySelector(".message");r.hidden=e,r.classList.toggle("loading",e),n.textContent=e?"Checking with server...":""}showMessage(e,t="success"){const r=this.container.querySelector("#referral-message");r&&(r.textContent=e,r.className="message "+t,r.style.display="block","error"===t&&setTimeout((()=>{r.style.display="none"}),5e3))}dispatchEvent(e,t){const r=new CustomEvent("referralWidget:"+e,{detail:t,bubbles:!0});this.container.dispatchEvent(r)}}document.addEventListener("DOMContentLoaded",(()=>{window.jvbReferral=new e}))})();
\ No newline at end of file
diff --git a/assets/js/min/selector.min.js b/assets/js/min/selector.min.js
index e1e2b59..e7339bb 100644
--- a/assets/js/min/selector.min.js
+++ b/assets/js/min/selector.min.js
@@ -1 +1 @@
-(()=>{class e{constructor(){this.a11y=window.jvbA11y,this.error=window.jvbError,this.index=-1,this.hasAutocomplete=!1,this.isInitializing=!0,this.taxonomiesToFetch=new Set,this.store=new window.jvbStore({name:"taxonomies",storeName:"terms",keyPath:"id",showLoading:!1,indexes:[{name:"taxonomy",keyPath:"taxonomy"},{name:"parent",keyPath:"parent"},{name:"slug",keyPath:"slug",unique:!0},{name:"count",keyPath:"count"}],endpoint:"terms",TTL:72e5,filters:{taxonomy:"",page:1,search:"",parent:0},required:"taxonomy"}),this.fields=new Map,this.selectedTerms=new Map,this.activeField=null,this.currentConfig=null,this.currentSingular=null,this.currentPlural=null,this.activeStore=null,this.disabled=!1,this.searchHandler=null,this.autocompleteHandler=null,this.isAutocompleteActive=!1,this.init()}init(){this.initModal(),this.scanExistingFields(),this.initGlobalListeners(),this.store.subscribe(this.handleStoreEvent.bind(this)),this.isInitializing=!1,this.batchFetchTaxonomies()}handleStoreEvent(e,t){switch(e){case"data-loaded":if(this.modal?.open&&this.handleTermsLoaded(t),this.isAutocompleteActive&&this.activeField){const e=this.fields.get(this.activeField),i=t.data?.items||[],s=t.filters?.search||"";this.showAutocompleteResults(e,i,s),this.isAutocompleteActive=!1}break;case"filters-changed":this.modal?.open&&this.showLoading();break;case"fetch-error":this.isAutocompleteActive&&this.activeField&&(this.showAutocompleteError(this.activeField),this.isAutocompleteActive=!1),this.handleFetchError(t.error)}}handleTermsLoaded(e){this.hideLoading();const t=e.data?.items||[],i=e.data?.pagination||{},s=e.filters?.search&&e.filters.search.length>0,o=e.filters?.page>1;0===t.length?(o||this.showEmptyState(s?"No results found.":"No items available."),this.observer.unobserve(this.ui.sentinel)):(this.renderTerms(t,o,s),this.currentTerms=t,i.has_more?this.observer.observe(this.ui.sentinel):this.observer.unobserve(this.ui.sentinel)),this.a11y?.announce(t.length,o)}handleFetchError(e){console.error("Taxonomy fetch error:",e),this.hideLoading(),this.error?.log?this.error.log(e,{component:"TaxonomySelector",action:"fetchTerms"},(()=>this.fetchCurrentTerms())):this.showEmptyState("Error loading terms. Please try again.")}updateFieldsForTaxonomy(e,t){this.fields.forEach((i=>{i.taxonomy===e&&i.selectedTerms.size>0&&i.selectedTerms.forEach((e=>{const s=t.find((t=>t.id===e));if(s){const t=i.selectedContainer.querySelector(`[data-id="${e}"]`);t&&(t.dataset.path=s.path,t.querySelector("span").textContent=s.path)}}))}))}scanExistingFields(e=null){e||(e=document.body);e.querySelectorAll(".field.taxonomy, .field.post").forEach((e=>{try{this.registerField(e)}catch(t){this.error.log(t,{component:"TaxonomySelector",action:"scanExistingFields",container:e.dataset.name})}}))}registerField(e,t={}){let i=e.querySelector("input[type=hidden]");if(!i)return;"fieldId"in e.dataset||(e.dataset.fieldId=this.createFieldId(e));let s=e.dataset.fieldId,o=e.querySelector("button.taxonomy-toggle"),n={id:s,input:i,container:e,taxonomy:o.dataset.taxonomy,name:e.dataset.field,maxSelection:parseInt(o.dataset.max)||0,canSearch:"search"in o.dataset,hasAutocomplete:"autocomplete"in o.dataset,autocompleteDropdown:e.querySelector(".autocomplete-dropdown")??!1,canCreate:"creatable"in o.dataset,isRequired:"required"in o.dataset,selectedTerms:new Set,toggle:o,selectedContainer:e.querySelector(".selected-items"),...t};!this.hasAutocomplete&&n.hasAutocomplete&&(this.hasAutocomplete=!0,this.initAutocomplete());const a=i.value.trim();if(""!==a){a.split(",").map((e=>parseInt(e.trim()))).filter((e=>!isNaN(e))).forEach((e=>n.selectedTerms.add(e)))}return this.fields.set(s,n),this.isInitializing?this.taxonomiesToFetch.add(n.taxonomy):this.store.setFilter("taxonomy",n.taxonomy),n.selectedTerms.size>0&&this.initFieldDisplay(s),s}async batchFetchTaxonomies(){if(0===this.taxonomiesToFetch.size)return;const e=Array.from(this.taxonomiesToFetch);this.taxonomiesToFetch.clear(),console.log(`Batch fetching ${e.length} unique taxonomies:`,e);for(const t of e)await this.store.setFilters({taxonomy:t,page:1,search:"",parent:0});this.fields.forEach(((e,t)=>{e.selectedTerms.size>0&&this.initFieldDisplay(t)}))}createFieldId(e){return this.index++,"selector-"+this.index}async initFieldDisplay(e){const t=this.fields.get(e);if(!t||0===t.selectedTerms.size)return;const i=Array.from(t.selectedTerms),s=[];i.forEach((e=>{const t=this.store.data.get(e);t&&s.push(t)})),s.forEach((t=>{this.addTermToDisplay(e,t.id,t.name,t.path)}))}initModal(){this.modalID="dialog#jvb-selector",this.modal=document.querySelector(this.modalID),this.modal?(this.initModalElements(),this.modalInstance=new window.jvbModal(this.modal,{handleForm:!1,save:null,open:null}),this.modalInstance.subscribe(((e,t)=>{switch(e){case"modal-open":console.log(t),this.openModal(t);break;case"modal-close":this.closeModal(t)}}))):console.warn("Taxonomy selector modal not found")}initModalElements(){this.selectors={search:{input:"[type=search]",clear:".clear-search",container:".search-wrapper"},termsList:".items-container",termsWrap:".items-wrap",breadcrumbs:{nav:"nav.term-navigation",back:".back-to-parent"},loading:{loading:".loading",text:".loading span"},selectedTerms:".selected-items",sentinel:".scroll-sentinel",modal:{title:"#modal-title",content:".modal-content"},create:{details:".create-new-term",parent:"#select_parent",summary:".create-new-term summary",name:"#term_name",button:".submit-term",label:{name:"[for=term_name]",parent:"[for=select_parent]"}},favouriteTerms:".favourite-terms"},this.ui=window.uiFromSelectors(this.selectors),this.observer=new IntersectionObserver((e=>{e.forEach((e=>{e.isIntersecting&&this.activeStore&&this.loadMoreTerms()}))}),{root:this.ui.termsWrap,threshold:.5})}initGlobalListeners(){document.addEventListener("click",this.handleClick.bind(this)),document.addEventListener("change",this.handleChange.bind(this)),this.hasAutocomplete&&this.initAutocomplete()}initAutocomplete(){console.log("Autocomplete init"),this.autocompleteHandler=window.debounce((e=>this.handleAutocomplete(e)),300),document.addEventListener("input",this.autocompleteHandler),document.addEventListener("blur",this.cleanupAutocomplete.bind(this))}handleClick(e){const t=window.targetCheck(e,".taxonomy-toggle");if(t)return e.preventDefault(),void this.handleToggleClick(t);const i=window.targetCheck(e,"button.remove-item");if(i&&e.target.closest(".jvb-selector")){const e=this.getFieldId(i),t=i.closest(".selected-item").dataset.id;this.removeSelectedTerm(e,t)}else e.target.matches(".modal-close")?this.modalInstance&&this.modalInstance.handleClose():this.modal&&this.modal.contains(e.target)&&this.handleModalClick(e)}handleChange(e){if(window.targetCheck(e,".taxonomy.field, .post.field")&&"hidden"===e.target.type){const t=this.getFieldId(e.target);this.updateFieldFromInput(t)}else this.modal&&this.modal.contains(e.target)&&this.handleModalChange(e)}handleToggleClick(e){try{const t=this.getFieldId(e);if(!this.fields.get(t))return void console.error("Field not found for toggle:",t);this.setActiveField(t),this.modalInstance.handleOpen()}catch(e){console.error("Error handling toggle click:",e),this.error?.handleError(e,{component:"TaxonomySelector",action:"handleToggleClick"})}}setActiveField(e){if(this.activeField=e,this.currentConfig=this.fields.get(e),this.currentSingular=jvbSettings.labels[this.currentConfig.taxonomy].single,this.currentPlural=jvbSettings.labels[this.currentConfig.taxonomy].plural,this.store.setFilter("taxonomy",this.currentConfig.taxonomy),this.selectedTerms.clear(),this.currentConfig.selectedTerms){let e=[];if(this.currentConfig.selectedTerms.forEach((t=>{const i=this.store.getItem(t);i?this.selectedTerms.set(t,{id:t,name:i.name,path:i.path}):e.push(t)})),e.length>0){this.fetchSpecificTerms(e).forEach((e=>{this.selectedTerms.set(e.id,{id:e.id,name:e.name,path:e.path})}))}}}fetchSpecificTerms(e){return[]}handleModalClick(e){if(window.targetCheck(e,".remove-item")){let t=window.targetCheck(e,".selected-item");t&&this.removeSelectedTermFromModal(t.dataset.id)}else if(window.targetCheck(e,".back-to-parent"))this.navigateToParent();else if(window.targetCheck(e,".toggle-children")){let t=e.target.closest("li");this.navigateToChild(parseInt(t.dataset.id),t.querySelector(".term-name").textContent)}else if(window.targetCheck(e,".path-level")){let t=window.targetCheck(e,".path-level");this.navigateToPath(t)}}handleModalChange(e){if(window.targetCheck(e,this.modalID)&&"checkbox"===e.target.type){e.preventDefault(),e.stopPropagation();const t=parseInt(e.target.closest("li").dataset.id),i=e.target.closest("li").querySelector("label");e.target.checked?this.addSelectedTermToModal(t,i.title,i.dataset.path):this.removeSelectedTermFromModal(t)}}openForFilter(e,t,i=[]){const s=`filter-${e}-${Date.now()}`;this.fields.set(s,{id:s,input:null,container:null,taxonomy:e,name:`filter_${e}`,maxSelection:0,canSearch:!0,hasAutocomplete:!1,autocompleteDropdown:document.querySelector(".autocomplete-dropdown")??!1,canCreate:!1,isRequired:!1,selectedTerms:new Set(i),toggle:null,selectedContainer:null,isFilterMode:!0,filterCallback:t}),this.setActiveField(s),this.modalInstance.handleOpen()}openModal(e){this.activeField=e.fieldId,this.currentConfig=e,e.canCreate&&"jvbTaxCreator"in window?this.creator=new window.jvbTaxCreator(this):this.creator&&delete this.creator,this.selectedTerms=new Set(e.selectedTerms);this.store.filters.taxonomy!==e.taxonomy&&this.store.setFilters({taxonomy:e.taxonomy,page:1,search:"",parent:0}),window.removeChildren(this.ui.termsList),this.ui.search.value="",this.updateSelectionCount(),this.modalInstance.open()}closeModal(){if(this.observer.unobserve(this.ui.sentinel),window.removeChildren(this.ui.termsList),this.currentConfig?.isFilterMode){if(this.currentConfig.filterCallback){const e=Array.from(this.selectedTerms.keys());this.currentConfig.filterCallback(e,this.currentConfig.taxonomy)}this.fields.delete(this.activeField)}else this.activeField&&this.saveSelectionsToField(this.activeField);this.currentConfig?.canSearch&&this.searchHandler&&this.ui.search.input.removeEventListener("input",this.searchHandler),this.creator&&delete this.creator,this.activeField=null,this.currentConfig=null}resetModalState(){this.disabled=!1,window.removeChildren(this.ui.termsList),window.removeChildren(this.ui.selectedTerms),this.ui.search.input.value="",window.removeChildren(this.ui.breadcrumbs.nav),this.ui.breadcrumbs.nav.appendChild(this.ui.breadcrumbs.back),this.ui.breadcrumbs.back.hidden=!0}updateModalForTaxonomy(){if(!this.currentConfig)return;this.ui.modal.title.textContent=`Select ${this.currentPlural}`,this.ui.search.container&&(this.ui.search.container.style.display=this.currentConfig.canSearch?"block":"none"),this.ui.create.details&&(this.ui.create.details.style.display=this.currentConfig.canCreate?"block":"none",this.ui.create.details.hidden=!this.currentConfig.canCreate,this.ui.create.summary&&(this.ui.create.summary.textContent=`Add new ${this.currentSingular}`),this.ui.create.label.name&&(this.ui.create.label.name.textContent=`Name this ${this.currentSingular}`),this.ui.create.label.parent&&(this.ui.create.label.parent.textContent="Nest it under"),this.ui.create.parent);const e=`Opened ${this.currentSingular} selection. Choose from checkboxes or search to filter results.`;this.a11y?.announce(e)}updateModalSelections(){window.removeChildren(this.ui.selectedTerms),this.selectedTerms.forEach(((e,t)=>{this.addTermToModalDisplay(t,e.name,e.path)})),this.checkSelectionLimits()}addSelectedTermToModal(e,t,i){this.selectedTerms.set(e,{id:e,name:t,path:i}),this.addTermToModalDisplay(e,t,i),this.checkSelectionLimits();const s=this.ui.termsList.querySelector(`input[value="${e}"]`);s&&(s.checked=!0)}removeSelectedTermFromModal(e){this.selectedTerms.delete(parseInt(e));const t=this.ui.selectedTerms.querySelector(`[data-id="${e}"]`);t&&t.remove();const i=this.ui.termsList.querySelector(`input[value="${e}"]`);i&&(i.checked=!1),this.checkSelectionLimits()}addTermToModalDisplay(e,t,i){const s=window.getTemplate("selectedTerm").cloneNode(!0);s.dataset.id=e,s.dataset.path=i,s.dataset.name=t,s.dataset.taxonomy=this.currentConfig.taxonomy,s.querySelector("span").textContent=i,s.querySelector("button").title=`Remove ${t}`,this.ui.selectedTerms.appendChild(s)}checkSelectionLimits(){this.currentConfig&&0!==this.currentConfig.maxSelection&&(this.disabled=this.selectedTerms.size>=this.currentConfig.maxSelection,this.setCheckboxes(this.disabled))}setCheckboxes(e){this.ui.termsList.querySelectorAll('input[type="checkbox"]').forEach((t=>{t.checked||(t.disabled=e)}))}saveSelectionsToField(e){const t=this.fields.get(e);if(!t)return;t.selectedTerms.clear(),window.removeChildren(t.selectedContainer),this.selectedTerms.forEach(((i,s)=>{t.selectedTerms.add(s),this.addTermToDisplay(e,s,i.name,i.path)}));const i=Array.from(t.selectedTerms);t.input.value=i.join(","),t.input.dispatchEvent(new Event("change",{bubbles:!0}))}removeSelectedTerm(e,t){const i=this.fields.get(e);if(!i)return;const s=parseInt(t);i.selectedTerms.delete(s);const o=i.selectedContainer.querySelector(`[data-id="${s}"]`);o&&o.remove();const n=Array.from(i.selectedTerms);i.input.value=n.join(","),i.input.dispatchEvent(new Event("change",{bubbles:!0}))}addTermToDisplay(e,t,i,s){const o=this.fields.get(e);if(!o||o.selectedContainer.querySelector(`[data-id="${t}"]`))return;const n=window.getTemplate("selectedTerm").cloneNode(!0);n.dataset.id=t,n.dataset.path=s,n.dataset.name=i,n.dataset.taxonomy=o.taxonomy,n.querySelector("span").textContent=s,n.querySelector("button").title=`Remove ${i}`,o.selectedContainer.appendChild(n)}updateFieldFromInput(e){const t=this.fields.get(e);if(!t)return;const i=t.input.value.trim();if(t.selectedTerms.clear(),window.removeChildren(t.selectedContainer),""!==i){i.split(",").map((e=>parseInt(e.trim()))).filter((e=>!isNaN(e))).forEach((e=>t.selectedTerms.add(e))),this.initFieldDisplay(e)}}handleSearch(e){const t=e.target.value.trim();this.searchHandler&&clearTimeout(this.searchHandler),this.searchHandler=setTimeout((()=>{this.store.setFilters({search:t,page:1,parent:t?0:this.store.filters.parent||0}),window.removeChildren(this.ui.termsList)}),300)}async handleAutocomplete(e){if(!("autocomplete"in e.target.dataset))return;const t=this.getFieldId(e.target),i=this.fields.get(t);if(!i)return;const s=e.target.value.trim();if(s.length<2)return i.autocompleteDropdown&&(i.autocompleteDropdown.hidden=!0),void(this.isAutocompleteActive=!1);this.activeField=t,this.currentConfig=i,i.canCreate&&!this.creator&&(this.creator=new window.jvbTaxCreator(this)),this.isAutocompleteActive=!0,i.autocompleteDropdown&&(i.autocompleteDropdown.hidden=!1),this.store.setFilters({taxonomy:i.taxonomy,search:s,page:1})}cleanupAutocomplete(e){if(!("autocomplete"in e.target.dataset))return;const t=this.getFieldId(e.target);this.fields.get(t)&&this.creator&&delete this.creator}showAutocompleteError(e){const t=this.fields.get(e);if(!t)return;t.config.autocompleteDropdown||(t.config.autocompleteDropdown=t.element.querySelector(".autocomplete-dropdown"));const i=t.config.autocompleteDropdown;i&&(window.removeChildren(i),this.showEmptyState("Hmmm... something went wrong",i))}showAutocompleteResults(e,t,i){if(!e||!e.autocompleteDropdown)return;const s=e.autocompleteDropdown;if(window.removeChildren(s),0===t.length?this.showEmptyState("No items found.",s):t.forEach((t=>{const i=this.createAutocompleteTermElement(e,t);i&&s.appendChild(i)})),this.creator){const t=this.creator.createAutocompleteOption(i,e);s.appendChild(t)}s.hidden=!1}createAutocompleteTermElement(e,t){const i=document.createElement("button");return i.type="button",i.className="autocomplete-item",i.dataset.id=t.id,i.dataset.name=t.name,i.dataset.path=t.path||t.name,i.textContent=t.path||t.name,i.addEventListener("click",(()=>{e.selectedTerms.add(parseInt(t.id)),this.addTermToDisplay(e.id,t.id,t.name,t.path),e.input.value=Array.from(e.selectedTerms).join(","),e.input.dispatchEvent(new Event("change",{bubbles:!0})),e.autocompleteDropdown.hidden=!0;const i=e.container.querySelector("input[data-autocomplete]");i&&(i.value="")})),i}navigateToParent(){this.store.setFilters({parent:0,page:1}),window.removeChildren(this.ui.termsList),this.ui.breadcrumbs.back.hidden=!0}navigateToChild(e,t){this.store.setFilters({parent:e,page:1}),window.removeChildren(this.ui.termsList),this.updateBreadcrumbs(e,t),this.ui.breadcrumbs.back.hidden=!1}navigateToPath(e){const t=parseInt(e.dataset.id)||0;this.store.setFilters({parent:t,page:1}),window.removeChildren(this.ui.termsList),this.ui.breadcrumbs.back.hidden=0===t}loadMoreTerms(){if(!this.activeStore)return;const e=this.activeStore.filters.page||1;this.store.setFilter("page",e+1)}renderTerms(e,t=!1,i=!1){if(t||window.removeChildren(this.ui.termsList),0===e.length)return void(t||this.showEmptyState());const s=this.store.filters.parent||0;this.ui.breadcrumbs.back.hidden=0===s,e.forEach((e=>{const t=this.createTermElement({id:parseInt(e.id),name:e.name,hasChildren:e.hasChildren,path:e.path||null,show:i});t&&this.ui.termsList.appendChild(t)}))}createTermElement(e){if(!e||!e.name)return null;const t=window.getTemplate("termListItem").cloneNode(!0);t.dataset.id=e.id;const i=this.selectedTerms.has(e.id),s=t.querySelector("input"),o=t.querySelector("label"),n=t.querySelector("span, .term-name");if(s&&o&&n&&(s.id=`${this.currentConfig.container.id}${e.id}`,s.name=`${this.currentConfig.container.id}${this.currentConfig.taxonomy}-select`,s.value=e.id,s.disabled=!i&&this.disabled,s.checked=i,o.htmlFor=s.id,o.title=e.path||e.name,o.dataset.path=e.path,n.textContent=e.show?e.path:e.name),e.hasChildren){const i=window.getTemplate?window.getTemplate("termChildrenToggle"):this.createChildrenToggle();i&&(i.ariaLabel=`View sub-terms of ${e.name}`,t.appendChild(i))}return t}createChildrenToggle(){const e=document.createElement("button");return e.type="button",e.className="toggle-children",e.innerHTML="→",e}updateBreadcrumbs(e,t){const i=window.getTemplate("termBreadcrumb").cloneNode(!0);i.dataset.id=e,i.textContent=t,i.title=t;const s=this.ui.breadcrumbs.nav.querySelector(`[data-id="${e}"]`);if(s)for(;s.nextElementSibling;)s.nextElementSibling.remove();else this.ui.breadcrumbs.nav.appendChild(i)}showLoading(){this.ui.loading.loading.hidden=!1,this.modal.classList.add("loading");const e=this.store?.filters?.search||"",t=this.store?.filters?.parent||0;let i=""!==e?`searching for "${e}" items`:0===t?"loading items":"loading child items";window.typeLoop?this.stopTyping=window.typeLoop(this.ui.loading.text,i):this.ui.loading.text.textContent=i}hideLoading(){this.ui.loading.loading.hidden=!0,this.modal.classList.remove("loading"),this.stopTyping&&this.stopTyping()}showEmptyState(e="No items found.",t=null){t||(t=this.ui.termsList);const i=window.getTemplate("noResults").cloneNode(!0);e&&i.querySelector("span")&&(i.querySelector("span").textContent=e),t.appendChild(i)}getFieldId(e){if(e.dataset.fieldId)return e.dataset.fieldId;const t=e.closest("[data-field-id]");return t?t.dataset.fieldId:null}destroy(){document.removeEventListener("click",this.handleClick),document.removeEventListener("change",this.handleChange),this.observer?.disconnect(),this.store.destroy(),this.fields.clear(),this.selectedTerms.clear()}}document.addEventListener("DOMContentLoaded",(function(){window.jvbSelector=new e}))})();
\ No newline at end of file
+(()=>{class e{constructor(){this.a11y=window.jvbA11y,this.error=window.jvbError,this.index=-1,this.hasAutocomplete=!1,this.isInitializing=!0,this.taxonomiesToFetch=new Set,this.store=new window.jvbStore({name:"taxonomies",storeName:"terms",keyPath:"id",showLoading:!1,indexes:[{name:"taxonomy",keyPath:"taxonomy"},{name:"parent",keyPath:"parent"},{name:"slug",keyPath:"slug",unique:!0},{name:"count",keyPath:"count"}],endpoint:"terms",TTL:72e5,filters:{taxonomy:"",page:1,search:"",parent:0},required:"taxonomy"}),this.fields=new Map,this.selectedTerms=new Map,this.activeField=null,this.currentConfig=null,this.currentSingular=null,this.currentPlural=null,this.activeStore=null,this.disabled=!1,this.searchHandler=null,this.autocompleteHandler=null,this.isAutocompleteActive=!1,this.init()}init(){this.initModal(),this.scanExistingFields(),this.initGlobalListeners(),this.store.subscribe(this.handleStoreEvent.bind(this)),this.isInitializing=!1,this.batchFetchTaxonomies()}handleStoreEvent(e,t){switch(e){case"data-loaded":if(this.modal?.open&&this.handleTermsLoaded(t),this.isAutocompleteActive&&this.activeField){const e=this.fields.get(this.activeField),i=t.data?.items||[],s=t.filters?.search||"";this.showAutocompleteResults(e,i,s),this.isAutocompleteActive=!1}break;case"filters-changed":this.modal?.open&&this.showLoading();break;case"fetch-error":this.isAutocompleteActive&&this.activeField&&(this.showAutocompleteError(this.activeField),this.isAutocompleteActive=!1),this.handleFetchError(t.error)}}handleTermsLoaded(e){this.hideLoading();const t=e.data?.items||[],i=e.data?.pagination||{},s=e.filters?.search&&e.filters.search.length>0,o=e.filters?.page>1;0===t.length?(o||this.showEmptyState(s?"No results found.":"No items available."),this.observer.unobserve(this.ui.sentinel)):(this.renderTerms(t,o,s),this.currentTerms=t,i.has_more?this.observer.observe(this.ui.sentinel):this.observer.unobserve(this.ui.sentinel)),this.a11y?.announce(t.length,o)}handleFetchError(e){console.error("Taxonomy fetch error:",e),this.hideLoading(),this.error?.log?this.error.log(e,{component:"TaxonomySelector",action:"fetchTerms"},(()=>this.fetchCurrentTerms())):this.showEmptyState("Error loading terms. Please try again.")}updateFieldsForTaxonomy(e,t){this.fields.forEach((i=>{i.taxonomy===e&&i.selectedTerms.size>0&&i.selectedTerms.forEach((e=>{const s=t.find((t=>t.id===e));if(s){const t=i.selectedContainer.querySelector(`[data-id="${e}"]`);t&&(t.dataset.path=s.path,t.querySelector("span").textContent=s.path)}}))}))}scanExistingFields(e=null){e||(e=document.body);e.querySelectorAll(".field.taxonomy, .field.post").forEach((e=>{try{this.registerField(e)}catch(t){this.error.log(t,{component:"TaxonomySelector",action:"scanExistingFields",container:e.dataset.name})}}))}registerField(e,t={}){let i=e.querySelector("input[type=hidden]");if(!i)return;"fieldId"in e.dataset||(e.dataset.fieldId=this.createFieldId(e));let s=e.dataset.fieldId,o=e.querySelector("button.taxonomy-toggle"),n={id:s,input:i,container:e,taxonomy:o.dataset.taxonomy,name:e.dataset.field,maxSelection:parseInt(o.dataset.max)||0,canSearch:"search"in o.dataset,hasAutocomplete:"autocomplete"in o.dataset,autocompleteDropdown:e.querySelector(".autocomplete-dropdown")??!1,canCreate:"creatable"in o.dataset,isRequired:"required"in o.dataset,selectedTerms:new Set,toggle:o,selectedContainer:e.querySelector(".selected-items"),...t};!this.hasAutocomplete&&n.hasAutocomplete&&(this.hasAutocomplete=!0,this.initAutocomplete());const a=i.value.trim();if(""!==a){a.split(",").map((e=>parseInt(e.trim()))).filter((e=>!isNaN(e))).forEach((e=>n.selectedTerms.add(e)))}return this.fields.set(s,n),this.isInitializing?this.taxonomiesToFetch.add(n.taxonomy):this.store.setFilter("taxonomy",n.taxonomy),n.selectedTerms.size>0&&this.initFieldDisplay(s),s}async batchFetchTaxonomies(){if(0===this.taxonomiesToFetch.size)return;const e=Array.from(this.taxonomiesToFetch);this.taxonomiesToFetch.clear(),console.log(`Batch fetching ${e.length} unique taxonomies:`,e);for(const t of e)await this.store.setFilters({taxonomy:t,page:1,search:"",parent:0});this.fields.forEach(((e,t)=>{e.selectedTerms.size>0&&this.initFieldDisplay(t)}))}createFieldId(e){return this.index++,"selector-"+this.index}async initFieldDisplay(e){const t=this.fields.get(e);if(!t||0===t.selectedTerms.size)return;const i=Array.from(t.selectedTerms),s=[];i.forEach((e=>{const t=this.store.data.get(e);t&&s.push(t)})),s.forEach((t=>{this.addTermToDisplay(e,t.id,t.name,t.path)}))}initModal(){this.modalID="dialog#jvb-selector",this.modal=document.querySelector(this.modalID),this.modal?(this.initModalElements(),this.modalInstance=new window.jvbModal(this.modal,{handleForm:!1,save:null,open:null}),this.modalInstance.subscribe(((e,t)=>{switch(e){case"modal-open":console.log(t),this.openModal(t);break;case"modal-close":this.closeModal(t)}}))):console.warn("Taxonomy selector modal not found")}initModalElements(){this.selectors={search:{input:"[type=search]",clear:".clear-search",container:".search-wrapper"},termsList:".items-container",termsWrap:".items-wrap",breadcrumbs:{nav:"nav.term-navigation",back:".back-to-parent"},loading:{loading:".loading",text:".loading span"},selectedTerms:".selected-items",sentinel:".scroll-sentinel",modal:{title:"#modal-title",content:".modal-content"},create:{details:".create-new-term",parent:"#select_parent",summary:".create-new-term summary",name:"#term_name",button:".submit-term",label:{name:"[for=term_name]",parent:"[for=select_parent]"}},favouriteTerms:".favourite-terms"},this.ui=window.uiFromSelectors(this.selectors),this.observer=new IntersectionObserver((e=>{e.forEach((e=>{e.isIntersecting&&this.activeStore&&this.loadMoreTerms()}))}),{root:this.ui.termsWrap,threshold:.5})}initGlobalListeners(){document.addEventListener("click",this.handleClick.bind(this)),document.addEventListener("change",this.handleChange.bind(this)),this.hasAutocomplete&&this.initAutocomplete()}initAutocomplete(){console.log("Autocomplete init"),this.autocompleteHandler=window.debounce((e=>this.handleAutocomplete(e)),300),document.addEventListener("input",this.autocompleteHandler),document.addEventListener("blur",this.cleanupAutocomplete.bind(this))}handleClick(e){const t=window.targetCheck(e,".taxonomy-toggle");if(t)return e.preventDefault(),void this.handleToggleClick(t);const i=window.targetCheck(e,"button.remove-item");if(i&&e.target.closest(".jvb-selector")){const e=this.getFieldId(i),t=i.closest(".selected-item").dataset.id;this.removeSelectedTerm(e,t)}else e.target.matches(".modal-close")?this.modalInstance&&this.modalInstance.handleClose():this.modal&&this.modal.contains(e.target)&&this.handleModalClick(e)}handleChange(e){if(window.targetCheck(e,".taxonomy.field, .post.field")&&"hidden"===e.target.type){const t=this.getFieldId(e.target);this.updateFieldFromInput(t)}else this.modal&&this.modal.contains(e.target)&&this.handleModalChange(e)}handleToggleClick(e){try{const t=this.getFieldId(e);if(!this.fields.get(t))return void console.error("Field not found for toggle:",t);this.setActiveField(t),this.modalInstance.handleOpen()}catch(e){console.error("Error handling toggle click:",e),this.error?.handleError(e,{component:"TaxonomySelector",action:"handleToggleClick"})}}setActiveField(e){if(this.activeField=e,this.currentConfig=this.fields.get(e),this.currentSingular=jvbSettings.labels[this.currentConfig.taxonomy].single,this.currentPlural=jvbSettings.labels[this.currentConfig.taxonomy].plural,this.store.setFilter("taxonomy",this.currentConfig.taxonomy),this.selectedTerms.clear(),this.currentConfig.selectedTerms){let e=[];if(this.currentConfig.selectedTerms.forEach((t=>{const i=this.store.getItem(t);i?this.selectedTerms.set(t,{id:t,name:i.name,path:i.path}):e.push(t)})),e.length>0){this.fetchSpecificTerms(e).forEach((e=>{this.selectedTerms.set(e.id,{id:e.id,name:e.name,path:e.path})}))}}}fetchSpecificTerms(e){return[]}handleModalClick(e){if(window.targetCheck(e,".remove-item")){let t=window.targetCheck(e,".selected-item");t&&this.removeSelectedTermFromModal(t.dataset.id)}else if(window.targetCheck(e,".back-to-parent"))this.navigateToParent();else if(window.targetCheck(e,".toggle-children")){let t=e.target.closest("li");this.navigateToChild(parseInt(t.dataset.id),t.querySelector(".term-name").textContent)}else if(window.targetCheck(e,".path-level")){let t=window.targetCheck(e,".path-level");this.navigateToPath(t)}}handleModalChange(e){if(window.targetCheck(e,this.modalID)&&"checkbox"===e.target.type){e.preventDefault(),e.stopPropagation();const t=parseInt(e.target.closest("li").dataset.id),i=e.target.closest("li").querySelector("label");e.target.checked?this.addSelectedTermToModal(t,i.title,i.dataset.path):this.removeSelectedTermFromModal(t)}}openForFilter(e,t,i=[]){const s=`filter-${e}-${Date.now()}`;this.fields.set(s,{id:s,input:null,container:null,taxonomy:e,name:`filter_${e}`,maxSelection:0,canSearch:!0,hasAutocomplete:!1,autocompleteDropdown:document.querySelector(".autocomplete-dropdown")??!1,canCreate:!1,isRequired:!1,selectedTerms:new Set(i),toggle:null,selectedContainer:null,isFilterMode:!0,filterCallback:t}),this.setActiveField(s),this.modalInstance.handleOpen()}openModal(e){this.activeField=e.fieldId,this.currentConfig=e,e.canCreate&&"jvbTaxCreator"in window?this.creator=new window.jvbTaxCreator(this):this.creator&&delete this.creator,this.selectedTerms=new Set(e.selectedTerms);this.store.filters.taxonomy!==e.taxonomy&&this.store.setFilters({taxonomy:e.taxonomy,page:1,search:"",parent:0}),window.removeChildren(this.ui.termsList),this.ui.search.value="",this.updateSelectionCount(),this.modalInstance.open()}closeModal(){if(this.observer.unobserve(this.ui.sentinel),window.removeChildren(this.ui.termsList),this.currentConfig?.isFilterMode){if(this.currentConfig.filterCallback){const e=Array.from(this.selectedTerms.keys());this.currentConfig.filterCallback(e,this.currentConfig.taxonomy)}this.fields.delete(this.activeField)}else this.activeField&&this.saveSelectionsToField(this.activeField);this.currentConfig?.canSearch&&this.searchHandler&&this.ui.search.input.removeEventListener("input",this.searchHandler),this.creator&&delete this.creator,this.activeField=null,this.currentConfig=null}resetModalState(){this.disabled=!1,window.removeChildren(this.ui.termsList),window.removeChildren(this.ui.selectedTerms),this.ui.search.input.value="",window.removeChildren(this.ui.breadcrumbs.nav),this.ui.breadcrumbs.nav.appendChild(this.ui.breadcrumbs.back),this.ui.breadcrumbs.back.hidden=!0}updateModalForTaxonomy(){if(!this.currentConfig)return;this.ui.modal.title.textContent=`Select ${this.currentPlural}`,this.ui.search.container&&(this.ui.search.container.style.display=this.currentConfig.canSearch?"block":"none"),this.ui.create.details&&(this.ui.create.details.style.display=this.currentConfig.canCreate?"block":"none",this.ui.create.details.hidden=!this.currentConfig.canCreate,this.ui.create.summary&&(this.ui.create.summary.textContent=`Add new ${this.currentSingular}`),this.ui.create.label.name&&(this.ui.create.label.name.textContent=`Name this ${this.currentSingular}`),this.ui.create.label.parent&&(this.ui.create.label.parent.textContent="Nest it under"),this.ui.create.parent);const e=`Opened ${this.currentSingular} selection. Choose from checkboxes or search to filter results.`;this.a11y?.announce(e)}updateModalSelections(){window.removeChildren(this.ui.selectedTerms),this.selectedTerms.forEach(((e,t)=>{this.addTermToModalDisplay(t,e.name,e.path)})),this.checkSelectionLimits()}addSelectedTermToModal(e,t,i){this.selectedTerms.set(e,{id:e,name:t,path:i}),this.addTermToModalDisplay(e,t,i),this.checkSelectionLimits();const s=this.ui.termsList.querySelector(`input[value="${e}"]`);s&&(s.checked=!0)}removeSelectedTermFromModal(e){this.selectedTerms.delete(parseInt(e));const t=this.ui.selectedTerms.querySelector(`[data-id="${e}"]`);t&&t.remove();const i=this.ui.termsList.querySelector(`input[value="${e}"]`);i&&(i.checked=!1),this.checkSelectionLimits()}addTermToModalDisplay(e,t,i){const s=window.getTemplate("selectedTerm").cloneNode(!0);s.dataset.id=e,s.dataset.path=i,s.dataset.name=t,s.dataset.taxonomy=this.currentConfig.taxonomy,s.querySelector("span").textContent=i,s.querySelector("button").title=`Remove ${t}`,this.ui.selectedTerms.appendChild(s)}checkSelectionLimits(){this.currentConfig&&0!==this.currentConfig.maxSelection&&(this.disabled=this.selectedTerms.size>=this.currentConfig.maxSelection,this.setCheckboxes(this.disabled))}setCheckboxes(e){this.ui.termsList.querySelectorAll('input[type="checkbox"]').forEach((t=>{t.checked||(t.disabled=e)}))}saveSelectionsToField(e){const t=this.fields.get(e);if(!t)return;t.selectedTerms.clear(),window.removeChildren(t.selectedContainer),this.selectedTerms.forEach(((i,s)=>{t.selectedTerms.add(s),this.addTermToDisplay(e,s,i.name,i.path)}));const i=Array.from(t.selectedTerms);t.input.value=i.join(","),t.input.dispatchEvent(new Event("change",{bubbles:!0}))}removeSelectedTerm(e,t){const i=this.fields.get(e);if(!i)return;const s=parseInt(t);i.selectedTerms.delete(s);const o=i.selectedContainer.querySelector(`[data-id="${s}"]`);o&&o.remove();const n=Array.from(i.selectedTerms);i.input.value=n.join(","),i.input.dispatchEvent(new Event("change",{bubbles:!0}))}addTermToDisplay(e,t,i,s){const o=this.fields.get(e);if(!o||o.selectedContainer.querySelector(`[data-id="${t}"]`))return;const n=window.getTemplate("selectedTerm").cloneNode(!0);n.dataset.id=t,n.dataset.path=s,n.dataset.name=i,n.dataset.taxonomy=o.taxonomy,n.querySelector("span").textContent=s,n.querySelector("button").title=`Remove ${i}`,o.selectedContainer.appendChild(n)}updateFieldFromInput(e){const t=this.fields.get(e);if(!t)return;const i=t.input.value.trim();if(t.selectedTerms.clear(),window.removeChildren(t.selectedContainer),""!==i){i.split(",").map((e=>parseInt(e.trim()))).filter((e=>!isNaN(e))).forEach((e=>t.selectedTerms.add(e))),this.initFieldDisplay(e)}}handleSearch(e){const t=e.target.value.trim();this.searchHandler&&clearTimeout(this.searchHandler),this.searchHandler=setTimeout((()=>{this.store.setFilters({search:t,page:1,parent:t?0:this.store.filters.parent||0}),window.removeChildren(this.ui.termsList)}),300)}async handleAutocomplete(e){if(!("autocomplete"in e.target.dataset))return;const t=this.getFieldId(e.target),i=this.fields.get(t);if(!i)return;const s=e.target.value.trim();if(i.currentAutocompleteQuery=s,s.length<2)return i.autocompleteDropdown&&(i.autocompleteDropdown.hidden=!0),void(this.isAutocompleteActive=!1);this.activeField=t,this.isAutocompleteActive=!0,i.autocompleteDropdown&&(i.autocompleteDropdown.hidden=!1),this.store.setFilters({taxonomy:i.taxonomy,search:s,page:1})}cleanupAutocomplete(e){if(!("autocomplete"in e.target.dataset))return;const t=this.getFieldId(e.target);this.fields.get(t)&&this.creator&&delete this.creator}showAutocompleteError(e){const t=this.fields.get(e);if(!t)return;t.config.autocompleteDropdown||(t.config.autocompleteDropdown=t.element.querySelector(".autocomplete-dropdown"));const i=t.config.autocompleteDropdown;i&&(window.removeChildren(i),this.showEmptyState("Hmmm... something went wrong",i))}showAutocompleteResults(e,t,i){if(!e||!e.autocompleteDropdown)return;const s=e.autocompleteDropdown;window.removeChildren(s),0===t.length?this.showEmptyState("No items found.",s):t.forEach((t=>{const i=this.createAutocompleteTermElement(e,t);i&&s.appendChild(i)}));const o=e.currentAutocompleteQuery||i;if(e.canCreate&&o&&window.jvbTaxCreator){const e=this.createNewTermOption(o);s.appendChild(e)}s.hidden=!1}createNewTermOption(e){const t=document.createElement("button");return t.type="button",t.className="autocomplete-item create-term",t.dataset.query=e,t.innerHTML=`<strong>Create:</strong> "${e}"`,t}createAutocompleteTermElement(e,t){const i=document.createElement("button");return i.type="button",i.className="autocomplete-item",i.dataset.id=t.id,i.dataset.name=t.name,i.dataset.path=t.path||t.name,i.textContent=t.path||t.name,i.addEventListener("click",(()=>{e.selectedTerms.add(parseInt(t.id)),this.addTermToDisplay(e.id,t.id,t.name,t.path),e.input.value=Array.from(e.selectedTerms).join(","),e.input.dispatchEvent(new Event("change",{bubbles:!0})),e.autocompleteDropdown.hidden=!0;const i=e.container.querySelector("input[data-autocomplete]");i&&(i.value="")})),i}navigateToParent(){this.store.setFilters({parent:0,page:1}),window.removeChildren(this.ui.termsList),this.ui.breadcrumbs.back.hidden=!0}navigateToChild(e,t){this.store.setFilters({parent:e,page:1}),window.removeChildren(this.ui.termsList),this.updateBreadcrumbs(e,t),this.ui.breadcrumbs.back.hidden=!1}navigateToPath(e){const t=parseInt(e.dataset.id)||0;this.store.setFilters({parent:t,page:1}),window.removeChildren(this.ui.termsList),this.ui.breadcrumbs.back.hidden=0===t}loadMoreTerms(){if(!this.activeStore)return;const e=this.activeStore.filters.page||1;this.store.setFilter("page",e+1)}renderTerms(e,t=!1,i=!1){if(t||window.removeChildren(this.ui.termsList),0===e.length)return void(t||this.showEmptyState());const s=this.store.filters.parent||0;this.ui.breadcrumbs.back.hidden=0===s,e.forEach((e=>{const t=this.createTermElement({id:parseInt(e.id),name:e.name,hasChildren:e.hasChildren,path:e.path||null,show:i});t&&this.ui.termsList.appendChild(t)}))}createTermElement(e){if(!e||!e.name)return null;const t=window.getTemplate("termListItem").cloneNode(!0);t.dataset.id=e.id;const i=this.selectedTerms.has(e.id),s=t.querySelector("input"),o=t.querySelector("label"),n=t.querySelector("span, .term-name");if(s&&o&&n&&(s.id=`${this.currentConfig.container.id}${e.id}`,s.name=`${this.currentConfig.container.id}${this.currentConfig.taxonomy}-select`,s.value=e.id,s.disabled=!i&&this.disabled,s.checked=i,o.htmlFor=s.id,o.title=e.path||e.name,o.dataset.path=e.path,n.textContent=e.show?e.path:e.name),e.hasChildren){const i=window.getTemplate?window.getTemplate("termChildrenToggle"):this.createChildrenToggle();i&&(i.ariaLabel=`View sub-terms of ${e.name}`,t.appendChild(i))}return t}createChildrenToggle(){const e=document.createElement("button");return e.type="button",e.className="toggle-children",e.innerHTML="→",e}updateBreadcrumbs(e,t){const i=window.getTemplate("termBreadcrumb").cloneNode(!0);i.dataset.id=e,i.textContent=t,i.title=t;const s=this.ui.breadcrumbs.nav.querySelector(`[data-id="${e}"]`);if(s)for(;s.nextElementSibling;)s.nextElementSibling.remove();else this.ui.breadcrumbs.nav.appendChild(i)}showLoading(){this.ui.loading.loading.hidden=!1,this.modal.classList.add("loading");const e=this.store?.filters?.search||"",t=this.store?.filters?.parent||0;let i=""!==e?`searching for "${e}" items`:0===t?"loading items":"loading child items";window.typeLoop?this.stopTyping=window.typeLoop(this.ui.loading.text,i):this.ui.loading.text.textContent=i}hideLoading(){this.ui.loading.loading.hidden=!0,this.modal.classList.remove("loading"),this.stopTyping&&this.stopTyping()}showEmptyState(e="No items found.",t=null){t||(t=this.ui.termsList);const i=window.getTemplate("noResults").cloneNode(!0);e&&i.querySelector("span")&&(i.querySelector("span").textContent=e),t.appendChild(i)}getFieldId(e){if(e.dataset.fieldId)return e.dataset.fieldId;const t=e.closest("[data-field-id]");return t?t.dataset.fieldId:null}destroy(){document.removeEventListener("click",this.handleClick),document.removeEventListener("change",this.handleChange),this.observer?.disconnect(),this.store.destroy(),this.fields.clear(),this.selectedTerms.clear()}}document.addEventListener("DOMContentLoaded",(function(){window.jvbSelector=new e}))})();
\ No newline at end of file
diff --git a/assets/js/min/uploader.min.js b/assets/js/min/uploader.min.js
index 7c51b23..8d83df7 100644
--- a/assets/js/min/uploader.min.js
+++ b/assets/js/min/uploader.min.js
@@ -1 +1 @@
-(()=>{class e{constructor(){this.queue=window.jvbQueue,this.a11y=window.jvbA11y,this.error=window.jvbError,this.fieldStore=new window.jvbStore({name:"upload_fields",storeName:"fieldStates",keyPath:"id",version:2,indexes:[{name:"fieldId",keyPath:"fieldId"},{name:"timestamp",keyPath:"timestamp"},{name:"content",keyPath:"content"},{name:"itemId",keyPath:"itemId"},{name:"status",keyPath:"status"}],stripDOMReferences:!0,TTL:6048e5}),this.uploadStore=new window.jvbStore({name:"uploads",storeName:"uploads",keyPath:"id",storeBlobs:!0,indexes:[{name:"fieldId",keyPath:"fieldId"},{name:"status",keyPath:"status"},{name:"groupId",keyPath:"groupId"},{name:"attachmentId",keyPath:"attachmentId"}]}),window.jvbUploadBlobs=this.uploadStore,this.fieldStore.subscribe(this.handleFieldStoreEvent.bind(this)),this.uploadStore.subscribe(this.handleUploadStoreEvent.bind(this)),this.initWorker(),this.fields=new Map,this.uploads=new Map,this.groups=new Map,this.selected=new Map,this.selectionHandlers=new Map,this.previewUrls=new Set,this.subscribers=new Set,this.dragController=null,this.selectors={field:{field:"[data-upload-field]",input:'input[type="file"]',hiddenValue:'input[type="hidden"]',dropZone:".file-upload-container",preview:".item-grid.preview",progress:".image-progress"},groups:{container:".upload-group",grid:".item-grid.group",header:".group-header",selectAll:'[name="select-all-group"]',actions:".group-actions",count:".selection-controls .info"},items:{item:"[data-upload-id]",checkbox:'[name*="select-item"]',featured:'[name="featured"]',details:"details"}},this.statusMapping={received:"Image Received",local_processing:"Processing Image...",queued:"Waiting to upload...",uploading:"Uploading to Server",pending:"Successfully sent to server. In line for further processing.",processing:"Processing on server...",completed:"Upload complete!",failed:"Upload failed (will retry)",failed_permanent:"Upload failed permanently"},this.init()}async init(){await this.loadFields(),await this.loadUploads(),this.initializeFields(),this.initListeners(),this.queue.subscribe(((e,t)=>{if("uploads"!==t.endpoint&&"uploads/meta"!==t.endpoint)return;const s=t.data instanceof FormData?t.data.get("fieldId"):t.data.fieldId;switch(e){case"cancel-operation":s&&this.clearField(s);break;case"operation-status":s&&this.updateFieldStatus(s,t.status);break;case"operation-complete":(t.result?.data||[]).forEach((e=>{const t=this.uploads.get(e.upload_id);t&&(t.attachmentId=e.attachment_id,t.status="completed",this.uploads.set(t.id,t))})),s&&this.cleanField(s)}})),window.addEventListener("beforeunload",(()=>{this.cleanupAllPreviewUrls()}))}initWorker(){this.worker={worker:null,timeout:null,tasks:new Map,restart:{count:0,max:3},settings:{timeout:1e4,batchSize:1,maxConcurrent:3,restartAfterTimeout:!0}}}initializeFields(){document.querySelectorAll(this.selectors.field.field).forEach((e=>{this.registerUploader(e)}))}scanFields(e){e.querySelectorAll(this.selectors.field.field).forEach((e=>{this.registerUploader(e)}))}registerUploader(e){const t=this.determineFieldId(e),s=this.extractFieldConfig(e),r={id:t,config:s,element:e,ui:this.buildFieldUI(e),uploads:new Set,groups:new Set,state:"ready"};return this.fields.set(t,r),e.dataset.uploader=t,this.addFieldSelectionHandler(t),"post_group"!==s.destination||this.dragController||this.initGroupFeatures(),t}extractFieldConfig(e){return{destination:e.dataset.destination||"meta",content:e.dataset.content||null,mode:e.dataset.mode||"direct",type:e.dataset.type||"single",name:e.dataset.field,itemID:e.dataset.itemId||0,maxFiles:parseInt(e.dataset.maxFiles)||999,subtype:e.dataset.subtype||"image"}}buildFieldUI(e){let t={field:e,input:e.querySelector(this.selectors.field.input),dropZone:e.querySelector(this.selectors.field.dropZone),preview:e.querySelector(this.selectors.field.preview),progress:{progress:e.querySelector(this.selectors.field.progress),bar:e.querySelector(".bar"),fill:e.querySelector(".fill"),details:e.querySelector(".details"),text:e.querySelector(".details .text"),count:e.querySelector(".details .count")}},s=e.querySelector(".group-display");return s&&(t.groups={display:s,container:e.querySelector(".item-grid.groups"),empty:e.querySelector(".empty-group"),groups:new Map}),t}initListeners(){this.clickHandler=this.handleClick.bind(this),this.changeHandler=this.handleChange.bind(this),document.addEventListener("click",this.clickHandler),document.addEventListener("change",this.changeHandler),this.dragEnterHandler=this.handleExternalDragEnter.bind(this),this.dragLeaveHandler=this.handleExternalDragLeave.bind(this),this.dragOverHandler=this.handleExternalDragOver.bind(this),this.dropHandler=this.handleExternalDrop.bind(this),document.addEventListener("dragenter",this.dragEnterHandler),document.addEventListener("dragleave",this.dragLeaveHandler),document.addEventListener("dragover",this.dragOverHandler),document.addEventListener("drop",this.dropHandler)}initGroupFeatures(){this.dragController=new window.jvbDragHandler({draggableSelector:this.selectors.items.item,dropTargetSelector:`${this.selectors.field.preview}, ${this.selectors.groups.grid}, .empty-group`,ignoreSelector:"input:not(.upload-select), button, select, textarea, details, summary, a",previewElement:"img, video, .icon",getItemId:e=>e.dataset.uploadId,getSelectedItems:e=>{const t=this.getFieldIdFromElement(e),s=e.dataset.uploadId,r=this.getCurrentSelection(t);return r&&r.includes(s)?r:[s]},validateDrop:(e,t)=>{const s=this.getFieldIdFromElement(t),r=document.querySelector(`[data-upload-id="${e[0]}"]`);return s===this.getFieldIdFromElement(r)},onDrop:(e,t)=>{this.handleItemDrop(e,t),t.scrollIntoView({behavior:"smooth",block:"center"})},onDragStart:e=>{},onDragEnd:(e,t)=>{if(t){const t=document.querySelector(`[data-upload-id="${e[0]}"]`),s=this.getFieldIdFromElement(t),r=this.selectionHandlers.get(s);r?.clearSelection()}},previewOptions:{multiOffset:{x:-60,y:-80},singleOffset:{x:-50,y:-60},showCount:!0}})}handleExternalDragLeave(e){const t=e.target.closest(this.selectors.field.dropZone);t&&!t.contains(e.relatedTarget)&&t.classList.remove("dragover")}handleExternalDragEnter(e){if(!e.dataTransfer.types.includes("Files"))return;const t=e.target.closest(this.selectors.field.dropZone);t&&(e.preventDefault(),t.classList.add("dragover"))}handleExternalDragOver(e){if(!e.dataTransfer.types.includes("Files"))return;e.target.closest(this.selectors.field.dropZone)&&(e.preventDefault(),e.dataTransfer.dropEffect="copy")}handleExternalDrop(e){const t=e.target.closest(this.selectors.field.dropZone);if(!t)return;e.preventDefault(),t.classList.remove("dragover");const s=Array.from(e.dataTransfer.files);if(0===s.length)return;const r=this.getFieldIdFromElement(t);r?(this.processFiles(r,s),this.a11y.announce(`${s.length} file(s) dropped for upload`)):console.error("No field ID found for drop zone")}handleItemDrop(e,t){const s=t.classList.contains("preview");let r=t;if(t.classList.contains("empty-group")){const e=this.getFieldIdFromElement(t),s=this.createGroup(e);if(!s)return void console.error("Failed to create group");r=s.grid}e.forEach((e=>{s?this.removeFromGroup(e):this.addToGroup(e,r)}));const o=this.getFieldIdFromElement(t);this.schedulePersistance(o);const i=e.length>1?`Moved ${e.length} items`:"Moved item";this.a11y.announce(i)}handleClick(e){if(e.target.matches(this.selectors.field.dropZone)||e.target.closest(this.selectors.field.dropZone)){const t=e.target.closest(this.selectors.field.dropZone);if(t&&!e.target.matches("input, button, a")){const e=t.querySelector(this.selectors.field.input);e?.click()}}const t=e.target.closest("[data-action]");t&&this.handleAction(t)}handleChange(e){const t=this.getFieldIdFromElement(e.target);if(e.target.matches(this.selectors.field.input)){const t=this.getFieldIdFromElement(e.target),s=Array.from(e.target.files);s.length>0&&t&&this.processFiles(t,s)}t&&("post_group"===this.fields.get(t).config.destination?this.handleGroupMetaChange(e.target):this.queueUploadMeta(e))}getCurrentSelection(e){let t=[];for(let[s,r]of this.selectionHandlers)(e===s||s.includes(e))&&r.selectedItems.size>0&&(t=t.concat([...r.selectedItems]));return t}getSubtypeFromMime(e){return e.startsWith("image/")?"image":e.startsWith("video/")?"video":"document"}getStatusText(e){return this.statusMapping[e]||e}getStatusIcon(e){return window.getIcon(this.queue.icons[e])}getStatusProgress(e){switch(e){case"local_processing":return 28;case"queued":return 50;case"uploading":return 66;case"pending":return 75;case"processing":return 89;case"completed":return 100;default:return 0}}getModalType(e){if(void 0!==e._cachedModalType)return e._cachedModalType;if(!e||!e.element)return e._cachedModalType=null,null;const t=e.element.closest("dialog");if(!t)return e._cachedModalType=null,null;let s=null;return s=t.classList.contains("edit")?"edit":t.classList.contains("create")?"create":t.classList.contains("bulkEdit")?"bulkEdit":t.className,e._cachedModalType=s,s}handleAction(e){const t=e.dataset.action,s=this.getFieldIdFromElement(e);switch(t){case"add-to-group":this.handleAddToGroup(e);break;case"delete-group":this.handleDeleteGroup(e);break;case"delete-upload":case"remove-from-group":this.handleRemoveItem(e);break;case"upload":this.fields.get(s).element.closest("details").open=!1,document.body.classList.add("uploading"),this.submitUploads(s);break;case"restore":this.handleRestoreUploads().then((()=>{}));break;case"clear-cache":confirm("Save these uploads for later?")||this.cleanupStoredUploads(),this.cleanupRestore()}}handleAddToGroup(e){const t=e.closest(this.selectors.field.field),s=t?.dataset.uploader;if(!s)return;const r=this.selected.get(s);if(r&&0!==r.size){const e=this.createGroup(s);if(!e)return;r.forEach((t=>{this.addToGroup(t,e.grid)}));const t=this.selectionHandlers.get(s);t?.clearSelection(),this.a11y.announce(`Created group with ${r.size} items`)}else this.createGroup(s);this.schedulePersistance(s)}handleDeleteGroup(e){const t=e.closest(this.selectors.groups.container);if(!t)return;const s=t.dataset.groupId,r=this.getFieldIdFromElement(t);if(!confirm("Delete this group? Items will be moved back to the upload area."))return;t.querySelectorAll(this.selectors.items.item).forEach((e=>{const t=e.dataset.uploadId;this.removeFromGroup(t)})),this.deleteGroup(s),this.a11y.announce("Group deleted, items returned to upload area"),this.schedulePersistance(r)}handleRemoveItem(e){const t=e.closest(this.selectors.items.item);if(!t)return;const s=t.dataset.uploadId,r=this.getFieldIdFromElement(t);confirm("Remove this item?")&&(this.removeUpload(r,s),this.a11y.announce("Item removed"),this.schedulePersistance(r))}addFieldSelectionHandler(e){if(this.selectionHandlers.has(e))return this.selectionHandlers.get(e);const t=this.fields.get(e);if(!t)return;const s=t.ui.field;if(!s)return;const r=new window.jvbHandleSelection({container:s,ui:{selectAll:s.querySelector('[name="select-all-uploads"]'),bulkControls:s.querySelector(".selection-actions"),count:s.querySelector(".selection-count")},itemSelector:"[data-upload-id]",checkboxSelector:'[name*="select-item"]'});return r.subscribe(((t,s)=>{switch(t){case"item-selected":case"item-deselected":case"range-selected":this.selected.set(e,s.selectedItems);break;case"select-all":this.handleSelectAll(s.container,s.selected)}})),this.selectionHandlers.set(e,r),r}addGroupSelectionHandler(e,t){const s=`${e}_${t}`;if(this.selectionHandlers.has(s))return this.selectionHandlers.get(s);const r=this.groups.get(t);if(!r)return;const o=new window.jvbHandleSelection({container:r.element,ui:{selectAll:r.element.querySelector(this.selectors.groups.selectAll),bulkControls:r.element.querySelector(this.selectors.groups.actions),count:r.element.querySelector(this.selectors.groups.count)},itemSelector:"[data-upload-id]",checkboxSelector:'[name*="select-item"]'});return o.subscribe(((t,s)=>{switch(t){case"item-selected":case"item-deselected":case"range-selected":this.selected.set(e,s.selectedItems);break;case"select-all":this.handleSelectAll(s.container,s.selected)}})),this.selectionHandlers.set(s,o),o}handleSelectAll(e,t){}determineFieldId(e){return`${e.dataset.content||e.closest("dialog")?.dataset.content||e.closest("form")?.dataset.save||""}_${e.dataset.itemId||e.closest("dialog")?.dataset.itemId||""}_${e.dataset.field||""}`}getFromElement(e,t){const s={field:{selector:this.selectors.field.field,key:"uploader",store:this.fields},upload:{selector:this.selectors.items.item,key:"uploadId",store:this.uploads},group:{selector:this.selectors.groups.container,key:"groupId",store:this.groups}}[t];if(!s)return null;const r=e.closest(s.selector);if(!r)return null;const o=r.dataset[s.key];return s.store.get(o)}getFieldFromElement(e){return this.getFromElement(e,"field")}getUploadFromElement(e){return this.getFromElement(e,"upload")}getGroupFromElement(e){return this.getFromElement(e,"group")}getFieldIdFromElement(e){return this.getFromElement(e,"field")?.id??null}getUploadIdFromElement(e){return this.getFromElement(e,"upload")?.id??null}getGroupIdFromElement(e){return this.getFromElement(e,"group")?.id??null}async processFiles(e,t){const s=this.fields.get(e);if(!s)return;s.ui.dropZone&&(s.ui.dropZone.hidden=!0),s.ui.groups.display&&(s.ui.groups.display.hidden=!1);const r=t.length;let o=0;this.updateUploadProgress(e,0,r,"Processing files..."),s.uploads||(s.uploads=new Set);const i=Array.from(t).map((async(t,i)=>{try{const i=`upload_${Date.now()}_${Math.random().toString(36).substr(2,9)}`,a={id:i,attachment_id:null,fieldId:e,originalFile:t,processedFile:null,preview:null,status:"local_processing",element:null,location:null,meta:{originalName:t.name,size:t.size,type:t.type}};a.preview=this.createPreviewUrl(t),t.type.startsWith("image/")?a.processedFile=await this.processImage(t,s.subtype):a.processedFile=t,await this.uploadStore.saveBlob(i,a.processedFile||t);const l=this.getSubtypeFromMime(t.type);return a.element=this.createUploadElement({...a,subtype:l},"post_group"===s.config.destination),this.showUploadProgress(i,!0),this.updateUploadItemProgress(i,50,"local_processing"),s.ui.preview&&(s.ui.preview.appendChild(a.element),a.location=s.ui.preview),this.uploads.set(i,a),s.uploads.add(i),o++,this.updateUploadProgress(e,o,r,"Processing files..."),this.updateUploadItemProgress(i,100,"processed"),a.status="processed",setTimeout((()=>{this.showUploadProgress(i,!1)}),1e3),i}catch(s){return console.error("Error processing file:",t.name,s),o++,this.updateUploadProgress(e,o,r,"Processing files..."),null}}));await Promise.all(i),this.updateFieldState(e),await this.schedulePersistance(e),"post_group"!==s.config.destination&&(await this.queueUpload(e),this.maybeLockUploads(e))}updateFieldState(e){const t=this.fields.get(e);if(!t||!t.ui.field)return;const s=t.ui.field,r=t.uploads?.size||0,o=t.ui.groups?.container?.querySelectorAll(".upload-group").length>0;s.dataset.hasUploads=r>0?"true":"false",s.dataset.uploadCount=r.toString(),s.dataset.hasGroups=o?"true":"false",t.ui.preview&&t.ui.preview.setAttribute("aria-label",`Upload preview area with ${r} item${1!==r?"s":""}`)}updateUploadProgress(e,t,s,r){const o=this.fields.get(e);if(!o?.ui?.progress?.progress)return;const i=o.ui.progress,a=s>0?t/s*100:0;i.fill&&(i.fill.style.width=`${a}%`),i.text&&(i.text.textContent=r),i.count&&(i.count.textContent=`${t}/${s}`),i.progress.hidden=t===s}updateFieldStatus(e,t){const s=this.fields.get(e);s&&(s.state=t)}updateUploadStatus(e,t){const s=this.uploads.get(e);s&&(s.status=t,this.updateUploadUI(e))}updateUploadUI(e){const t=this.uploads.get(e);if(!t?.element)return;t.element.className=t.element.className.replace(/status-[\w-]+/g,""),t.element.classList.add(`status-${t.status}`);t.element.querySelector(".progress")&&this.updateUploadItemProgress(e,this.getStatusProgress(t.status),t.status)}showUploadProgress(e,t=!0){const s=this.uploads.get(e);if(!s||!s.element)return;const r=s.element.querySelector(".progress");r&&(t?(r.style.removeProperty("animation"),r.hidden=!1):(r.style.animation="fadeOut var(--transition-base)",setTimeout((()=>{r.hidden=!0}),300)))}updateUploadItemProgress(e,t,s=null){const r=this.uploads.get(e);if(!r||!r.element)return;const o=r.element.querySelector(".progress");if(!o)return;const i=o.querySelector(".fill"),a=o.querySelector(".details"),l=o.querySelector(".icon");i&&(i.style.width=`${t}%`),s&&a&&(a.textContent=this.getStatusText(s)),s&&l&&(l.innerHTML=this.getStatusIcon(s).outerHTML)}checkFieldLimits(e,t){const s=this.fields.get(e);if(!s)return!1;return(s.uploads?.size||0)+t<=s.maxFiles}validateFile(e,t){return this.settings.allowedTypes.includes(e.type)?!(e.size>this.settings.maxFileSize)||(this.notify(`File too large: ${this.formatBytes(e.size)}`,"error"),!1):(this.notify(`Invalid file type: ${e.type}`,"error"),!1)}formatBytes(e,t=2){if(0===e)return"0 Bytes";const s=t<0?0:t,r=Math.floor(Math.log(e)/Math.log(1024));return parseFloat((e/Math.pow(1024,r)).toFixed(s))+" "+["Bytes","KB","MB","GB"][r]}shouldProcessClientSide(e,t){return!("image"!==t||!e.type.startsWith("image/"))}async processImage(e,t){const s=this.worker.settings.timeout;return new Promise(((r,o)=>{let i,a=!1;i=setTimeout((()=>{a||(a=!0,this.worker.tasks.delete(t),this.worker.settings.restartAfterTimeout&&this.restartCompressionWorker(),o(new Error(`Processing timeout for ${e.name}`)))}),s),this.worker.tasks.set(t,{file:e,timeoutId:i}),this.handleProcess(e,t).then((e=>{a||(a=!0,clearTimeout(i),this.worker.tasks.delete(t),r(e))})).catch((e=>{a||(a=!0,clearTimeout(i),this.worker.tasks.delete(t),o(e))}))}))}async handleProcess(e,t){if(!e.type.startsWith("image/"))return e;const s=this.getMaxDimension();if(this.shouldUseWorker(e))try{if(this.worker.worker||this.initCompressionWorker(),this.worker.worker)return await this.processWithWorker(e,t,s,.85)}catch(e){console.warn("Worker processing failed, falling back to main thread:",e)}return await this.processOnMainThread(e,s,.85)}async processOnMainThread(e,t,s){return new Promise(((r,o)=>{const i=new Image,a=document.createElement("canvas"),l=a.getContext("2d");let n=null;const d=()=>{i.onload=null,i.onerror=null,n&&(URL.revokeObjectURL(n),n=null),a.width=1,a.height=1,l.clearRect(0,0,1,1)};i.onload=()=>{try{const{width:n,height:c}=this.calculateOptimalDimensions(i,t);a.width=n,a.height=c,l.imageSmoothingEnabled=!0,l.imageSmoothingQuality="high",l.drawImage(i,0,0,n,c);const u=this.getOptimalFormat(e),p=this.getOptimalQuality(e,s);a.toBlob((t=>{if(d(),t){const s=new File([t],this.getProcessedFileName(e,u),{type:u,lastModified:Date.now()});r(s)}else o(new Error("Canvas toBlob failed"))}),u,p)}catch(e){d(),o(new Error(`Canvas processing failed: ${e.message}`))}},i.onerror=()=>{d(),o(new Error(`Failed to load image: ${e.name}`))};try{n=this.createPreviewUrl(e),i.src=n}catch(e){d(),o(new Error(`Failed to create object URL: ${e.message}`))}}))}getOptimalFormat(e){return"image/gif"===e.type||"image/svg+xml"===e.type?e.type:this.supportsWebP()?"image/webp":"image/jpeg"}getOptimalQuality(e,t){return e.size<512e3?Math.max(t,.9):e.size<2097152?t:Math.min(t,.8)}getProcessedFileName(e,t){return e.name.replace(/\.[^/.]+$/,"")+({"image/webp":".webp","image/jpeg":".jpg","image/png":".png","image/gif":".gif"}[t]||".jpg")}getMaxDimension(){const e=window.screen.width,t=window.devicePixelRatio||1;return e*t>2560?2400:e*t>1920?1920:1200}shouldUseWorker(e){return this.worker.worker&&e.size>1048576&&"undefined"!=typeof OffscreenCanvas}async processWithWorker(e,t,s,r){return new Promise(((o,i)=>{if(!this.worker.worker)return void i(new Error("Worker not available"));const a=`${t}_${Date.now()}`,l=t=>{if(t.data.messageId===a)if(this.worker.worker.removeEventListener("message",l),this.worker.worker.removeEventListener("error",n),t.data.success){const s=new File([t.data.blob],this.getProcessedFileName(e,t.data.format||"image/webp"),{type:t.data.format||"image/webp",lastModified:Date.now()});o(s)}else i(new Error(t.data.error||"Worker processing failed"))},n=e=>{this.worker.worker.removeEventListener("message",l),this.worker.worker.removeEventListener("error",n),i(new Error(`Worker error: ${e.message}`))};this.worker.worker.addEventListener("message",l),this.worker.worker.addEventListener("error",n),this.worker.worker.postMessage({messageId:a,file:e,maxDimension:s,quality:r,outputFormat:this.getOptimalFormat(e)})}))}restartCompressionWorker(){this.worker.worker&&(this.worker.worker.terminate(),this.worker.worker=null),this.worker.tasks.clear(),this.worker.restart.count>=this.worker.restart.max?console.error("Max worker restarts reached, disabling worker"):(this.worker.restart.count++,this.initCompressionWorker())}initCompressionWorker(){if(!this.worker.worker&&"undefined"!=typeof Worker)try{const e=new Blob(["\n            self.onmessage = async function(e) {\n                const { messageId, file, maxDimension, quality, outputFormat } = e.data;\n\n                try {\n                    // Create ImageBitmap from file\n                    const bitmap = await createImageBitmap(file);\n\n                    // Calculate dimensions\n                    const scale = Math.min(maxDimension / bitmap.width, maxDimension / bitmap.height, 1);\n                    const width = Math.round(bitmap.width * scale);\n                    const height = Math.round(bitmap.height * scale);\n\n                    // Create OffscreenCanvas\n                    const canvas = new OffscreenCanvas(width, height);\n                    const ctx = canvas.getContext('2d');\n\n                    // Draw and resize\n                    ctx.imageSmoothingEnabled = true;\n                    ctx.imageSmoothingQuality = 'high';\n                    ctx.drawImage(bitmap, 0, 0, width, height);\n\n                    // Clean up bitmap\n                    bitmap.close();\n\n                    // Convert to blob\n                    const blob = await canvas.convertToBlob({\n                        type: outputFormat,\n                        quality: quality\n                    });\n\n                    self.postMessage({\n                        messageId,\n                        success: true,\n                        blob: blob,\n                        format: outputFormat\n                    });\n\n                } catch (error) {\n                    self.postMessage({\n                        messageId,\n                        success: false,\n                        error: error.message\n                    });\n                }\n            };\n        "],{type:"application/javascript"});this.worker.worker=new Worker(this.createPreviewUrl(e))}catch(e){console.warn("Failed to initialize compression worker:",e),this.worker.worker=null}}calculateOptimalDimensions(e,t){let{width:s,height:r}=e;if(s<=t&&r<=t)return{width:s,height:r};const o=Math.min(t/s,t/r);return{width:Math.round(s*o),height:Math.round(r*o)}}supportsWebP(){return 0===document.createElement("canvas").toDataURL("image/webp").indexOf("data:image/webp")}createPreviewUrl(e){const t=URL.createObjectURL(e);return this.previewUrls||(this.previewUrls=new Set),this.previewUrls.add(t),t}revokePreviewUrl(e){e?.startsWith("blob:")&&(URL.revokeObjectURL(e),this.previewUrls?.delete(e))}maybeLockUploads(e){const t=this.fields.get(e);if(!t?.ui?.dropZone)return;if("post_group"===t.config.destination)return;const s=t.uploads?.size||0,r=t.config?.maxFiles||999;t.ui.dropZone.hidden=s>=r,t.element.classList.toggle("at-max-uploads",s>=r)}createUploadElement(e,t=!1){let s=window.getTemplate("uploadItem");if(!s)return void console.error("Image template not found");s.dataset.uploadId=e.id,e.originalFile&&(s.dataset.subtype=this.getSubtypeFromMime(e.originalFile.type)),s.querySelector('[name="featured"]').value=e.id;let[r,o,i,a,l]=[s.querySelector('[name="featured"]'),s.querySelector("img"),s.querySelector("video"),s.querySelector("label > span"),s.querySelector("details")];switch([r.value,o.src,o.alt]=[e.id,e.preview,e.originalFile?.name??e.meta?.originalName??""],s.dataset.subtype){case"image":[o.src,o.alt]=[e.preview,e.originalFile?.name??e.meta?.originalName??""],i.remove(),a.remove();break;case"video":i.src=e.preview,o.remove(),a.remove();break;case"document":const t=e.originalFile?.name??e.meta?.originalName??"",s=t.split(".").pop()?.toLowerCase()??"",r={pdf:"file-pdf",csv:"file-csv",doc:"file-doc",docx:"file-doc",txt:"file-txt",xls:"file-xls",xlsx:"file-xls"},l=window.getIcon(r[s]||"file");a.innerText=e.originalFile.name,a.prepend(l),o.remove(),i.remove()}if(l){let e=window.getTemplate("uploadMeta");e&&l.append(e)}return s.draggable=t,s.querySelectorAll("input").forEach((t=>{let s=t.id;if(s){let r=s+e.id,o=t.parentNode.querySelector(`label[for="${s}"]`);t.id=r,o&&(o.htmlFor=r)}})),s}async submitUploads(e){const t=this.fields.get(e);if(!t?.uploads||0===t.uploads.size)return;let s=Array.from(t.uploads);if(0===s.length)return void this.error.log("No uploads to upload",{component:"UploadManager",action:"submitGroupedUploads",fieldId:e});const r=this.getFieldGroups(e);if(0===r.length)return void this.error.log("No groups created for post_group upload",{component:"UploadManager",action:"submitGroupedUploads",fieldId:e});const o=[],i=new FormData;let a=[];s=s.map((e=>this.uploads.get(e))),r.forEach(((e,t)=>{const r={images:[],fields:{}};for(let[t,s]of Object.entries(e.changes))r.fields[t]=s;s.filter((t=>t.groupId===e.id)).forEach((e=>{if(e){const t=e.processedFile||e.originalFile;if(t){i.append("files[]",t);const s={upload_id:e.id,index:a.length};r.images.push(s),a.push(e.id)}}})),o.push(r)})),s.filter((e=>!Object.hasOwn(e,"groupId"))).forEach((e=>{if(e){const t={images:[],fields:{}},s=e.processedFile||e.originalFile;if(s){i.append("files[]",s);const r={upload_id:e.id,index:a.length};t.images.push(r),a.push(e.id)}o.push(t)}})),i.append("content",t.config.content),i.append("user",t.config.itemID),i.append("posts",JSON.stringify(o)),i.append("upload_ids",JSON.stringify(a));for(const[e,t]of i.entries())console.log(e,t);const l={endpoint:"uploads/groups",method:"POST",data:i,title:`Creating ${o.length} ${t.config.content}${o.length>1?"s":""} from uploads...`,popup:`Creating ${o.length} post${o.length>1?"s":""}...`,canMerge:!1,headers:{action_nonce:jvbSettings.dash},append:"_upload"};try{const e=await this.queue.addToQueue(l);return s.forEach((t=>{let s=this.uploads.get(t);s&&(s.operationId=e,this.updateUploadStatus(t,"queued"))})),t.operationId=e,this.a11y.announce(`Creating ${o.length} post${o.length>1?"s":""} from your uploads`),e}catch(t){throw this.error.log(t,{component:"UploadManager",action:"submitGroupedUploads",fieldId:e}),t}finally{this.schedulePersistance(t.id)}}async queueUpload(e){const t=this.fields.get(e);if(!t?.uploads)return;const s=Array.from(t.uploads);if(0===s.length)return;const r=this.prepareUploadData(t,s);this.a11y.announce("Queuing for upload");let o=1===s.length?"file":"files";const i={endpoint:"uploads",method:"POST",data:r,title:`Uploading ${s.length} ${o} to server...`,popup:`Uploading ${s.length} ${o}...`,canMerge:!1,headers:{action_nonce:jvbSettings.dash},append:"_upload"};try{const e=await this.queue.addToQueue(i);return s.forEach((t=>{let s=this.uploads.get(t);s&&(s.operationId=e,this.updateUploadStatus(t,"queued"))})),t.operationId=e,e}catch(e){throw e}finally{this.schedulePersistance(t.id)}}prepareUploadData(e,t){const s=new FormData;s.append("content",e.config.content),s.append("mode",e.config.mode),s.append("field_name",e.config.name),s.append("fieldId",e.id),s.append("field_type",e.config.type),s.append("subtype",e.config.subtype),s.append("item_id",e.config.itemID),s.append("destination",e.config.destination||"meta");let r=[];const o=this.getFieldGroups(e.id);if("post_group"===e.config.destination&&o.length>0){let e=[],t=[],i=[];o.forEach((o=>{let a=[],l=null;o.uploads.forEach((e=>{let t=this.uploads.get(e);if(t){const e=t.processedFile||t.originalFile;if(e){s.append("files[]",e);r.length;r.push(t.id),a.push(t.id);const o=t.element?.querySelector('[name="featured"]');o?.checked&&(l=t.id)}}})),e.push(a),t.push(o.title||""),i.push(l)})),s.append("groups",JSON.stringify(e)),s.append("group_titles",JSON.stringify(t)),s.append("featured_images",JSON.stringify(i))}else t.forEach((e=>{let t=this.uploads.get(e);if(t){const e=t.processedFile||t.originalFile;e&&(s.append("files[]",e),r.push(t.id))}}));return s.append("upload_ids",JSON.stringify(r)),s}getFieldGroups(e){const t=[];return this.groups.forEach(((s,r)=>{if(s.fieldId===e){const o=this.fields.get(e),i=o?.ui?.groups?.groups?.get(r);t.push({id:r,uploads:Array.from(s.uploads||new Set),changes:s.changes||{},element:i||null})}})),t}async queueUploadMeta(e){const t=this.getUploadFromElement(e.target);if(!t)return;if(!this.fields.get(t.fieldId))return;if(!e.target.closest(".upload-meta"))return;let s={};s[e.target.name]=e.target.value,t.meta={...t.meta,...s};let r={};r[t.attachmentId??t.id]=t.meta;const o={endpoint:"uploads/meta",method:"POST",data:r,title:"Updating meta",canMerge:!0,headers:{action_nonce:jvbSettings.dash}};try{await this.queue.addToQueue(o)}catch(e){this.error.log(e,{component:"UploadManager",action:"sendMetaUpdate",uploadId:t.id})}}createGroup(e,t=null){const s=this.fields.get(e);if(!s)return console.error("Field not found:",e),null;t||(t=`group_${Date.now()}_${Math.random().toString(36).substr(2,9)}`);const r=this.createGroupElement(t,e);if(!r)return console.error("Failed to create group element"),null;s.ui.groups||(s.ui.groups={groups:new Map,container:null,empty:null,display:null}),s.ui.groups.groups.set(t,r),s.ui.groups.container&&s.ui.groups.empty?s.ui.groups.container.insertBefore(r,s.ui.groups.empty):s.ui.groups.container&&s.ui.groups.container.appendChild(r);const o={id:t,fieldId:e,element:r,grid:r.querySelector(".item-grid.group"),uploads:new Set,changes:{}};return this.groups.set(t,o),this.addGroupSelectionHandler(e,t),this.schedulePersistance(e),o}createGroupElement(e,t){let s=window.getTemplate("imageGroup");if(!s)return;s.dataset.groupId=e,s.dataset.fieldId=t;let r=window.getTemplate("groupMetadata");const o=s.querySelector(".fields");if(o&&r){o.append(r);const i=o.querySelector('[name="post_title"]'),a=o.querySelector('[name="post_excerpt"]');i&&(i.id=`${e}_title`,i.name=`${e}[post_title]`),a&&(a.id=`${e}_excerpt`,a.name=`${e}[post_excerpt]`);let l=this.fields.get(t);if(""!==l.config.content){s.querySelector("summary").textContent=l.config.content+" Fields"}}else s.querySelector("details").remove();const i=s.querySelector(".item-grid.group");return i&&(i.dataset.groupId=e),s}deleteGroup(e,t=!0){let s=this.groups.get(e);if(!s)return;let r=!0;t&&s.uploads&&s.uploads.size>0&&(r=!window.confirm("Delete uploads in group?")),t&&r&&s.uploads&&s.uploads.size>0&&Array.from(s.uploads).forEach((e=>{this.addImageToGroup(e,null,!1)})),this.groups.delete(e);let o=s.element;o&&(o.remove(),this.a11y.announce("Group removed")),this.schedulePersistance(s.fieldId)}addToGroup(e,t=null,s=!0){let r=this.uploads.get(e);if(!r)return;let o=this.fields.get(r.fieldId);if(!o)return;if(!t&&r.location===o.ui.preview||t===r.location)return;if(r.location){let t=r.location.dataset.groupId;if(t){let s=this.groups.get(t);s&&s.uploads&&(s.uploads.delete(e),0===s.uploads.size&&this.deleteGroup(t))}}const i=r.element.querySelector('[name*="select-item"]');i&&(i.checked=!1);let a=r.element.querySelector('[name="featured"]');if(a.hidden=!t,t){if(!t.classList.contains("item-grid")||!t.classList.contains("preview")){let s=t.dataset.groupId;a.name=s+"_"+a.name;let o=this.groups.get(s);o||(o=this.createGroup(r.fieldId),t=o.grid,s=o.id),o&&(o.uploads.add(e),r.groupId=s)}}else t=o.ui.preview,r.groupId=null;r.location=t,t.append(r.element),s&&this.schedulePersistance(o.id)}removeFromGroup(e){const t=this.uploads.get(e);if(!t)return;const s=this.fields.get(t.fieldId);if(!s)return;if(t.groupId){const s=this.groups.get(t.groupId);s?.uploads&&(s.uploads.delete(e),0===s.uploads.size&&this.deleteGroup(t.groupId,!1)),t.groupId=null}s.ui?.preview&&(s.ui.preview.appendChild(t.element),t.location=s.ui.preview);const r=t.element.querySelector('[name="featured"]');r&&(r.hidden=!0,r.checked=!1)}removeUpload(e,t){const s=this.fields.get(e),r=this.uploads.get(t);if(!s||!r)return;if(s.uploads?.delete(t),r.groupId){const e=this.groups.get(r.groupId);e&&e.uploads&&(e.uploads.delete(t),0===e.uploads.size&&this.removeGroup(r.groupId))}r.element?.remove(),this.clearUpload(t),this.updateFieldState(e),this.maybeLockUploads(e);const o=this.selectionHandlers.get(s.id);o&&o.deselect(t),this.a11y.announce("Upload removed")}schedulePersistance(e){const t=`persist_${e}`;window.debouncer.schedule(t,(()=>this.persistFieldState(e)),1e3)}async persistFieldState(e){const t=this.fields.get(e);if(!t)return;const s={...t,id:e,fieldId:e,uploads:Array.from(t.uploads||[]).map((e=>this.uploads.get(e))),groups:Array.from(this.groups.entries()).filter((([t,s])=>s.fieldId===e&&s.uploads&&s.uploads.size>0)).map((([e,t])=>({id:t.id,uploads:Array.from(t.uploads),changes:t.changes||{}}))),context:{url:this.normalizeUrl(window.location.href),fullUrl:window.location.href,modalType:this.getModalType(t),formId:t.formId,fieldSelector:`.field.upload[data-field="${t.config.name}"]`},timestamp:Date.now()};await this.fieldStore.save(s)}normalizeUrl(e){try{const t=new URL(e);return t.origin+t.pathname}catch(t){return e}}getFieldUploads(e,t=!1){const s=this.fields.get(e);return s&&s.uploads?Array.from(s.uploads).map((e=>{const s=this.uploads.get(e);return s?t?{id:s.id,fieldId:s.fieldId,status:s.status,attachmentId:s.attachmentId,operationId:s.operationId,groupId:s.groupId||null,changes:s.changes||{},meta:{originalName:s.meta?.originalName||s.originalFile?.name,size:s.meta?.size||s.originalFile?.size,type:s.meta?.type||s.originalFile?.type,title:s.meta?.title,alt:s.meta?.alt,caption:s.meta?.caption}}:s:null})).filter(Boolean):[]}async checkForStoredUploads(){if(!this.db)return;const e=this.db.transaction(["fieldStates"],"readonly").objectStore("fieldStates"),t=(await new Promise((t=>{const s=e.getAll();s.onsuccess=()=>t(s.result)}))).filter((e=>e.uploads.some((e=>!e.operationId&&("completed"===e.status||"processed"===e.status||"local_processing"===e.status||"processed-original"===e.status)))));0!==t.length&&this.showRecoveryNotification(t)}async handleRestoreUploads(){let e=document.querySelector("dialog.restore-uploads");if(!e)return;const t=this.getSelectedRestorationUploads(e);0!==t.length&&(await this.restoreSelectedUploads(t),this.cleanupRestore())}getSelectedRestorationUploads(e){let t=[];return e.querySelectorAll("[type=checkbox]:checked").forEach((e=>{const s=e.closest(".item");s&&t.push({uploadId:s.dataset.uploadId,fieldId:s.dataset.fieldId})})),t}handleGroupMetaChange(e){let t=this.getGroupFromElement(e);if(!t)return;Object.hasOwn(t,"changes")||(t.changes={});let s=e.name;if(s.includes("group")){let e=t.id+"_",r=t.id+"[";s=s.replace(e,"").replace(r,"").replace("]","")}t.changes[`${s}`]=e.value,this.groups.set(t.id,t),this.schedulePersistance(t.fieldId)}async showRecoveryNotification(e){const t=e.reduce(((e,t)=>e+t.uploads.length),0),s=e.reduce(((e,t)=>e+(t.groups?.length||0)),0);let r,o=window.getTemplate("restoreNotification");if(!o)return void console.error("Restore notification template not found");if(s>0){r=`${s} ${s>1?"groups":"group"} with ${t} ${t>1?"uploads":"upload"} can be restored.`}else r=`${t} upload(s) from ${e.length} field(s) can be recovered.`;const i=o.querySelector(".restore-details");i&&(i.textContent=r);for(const t of e){let e=window.getTemplate("restoreField");if(!e)continue;const s=e.querySelector("h3");s&&(s.textContent=t.config.name||"Unnamed Field");const r=e.querySelector(".item-grid.restore");for(const e of t.uploads){let s=window.getTemplate("uploadItem");if(!s)continue;const o=await this.uploadStore.getBlob(e.id);if(o)try{const r=new Blob([o.data],{type:o.type}),i=this.createPreviewUrl(r);let[a,l,n,d,c]=[s.querySelector('[name="featured"]'),s.querySelector("img"),s.querySelector("video"),s.querySelector("label > span"),s.querySelector("details")];s.dataset.uploadId=e.id,s.dataset.fieldId=t.id;let u=this.getSubtypeFromMime(o.type);switch(s.dataset.subtype=u,u){case"image":[l.src,l.alt]=[i,e.originalFile?.name??e.meta?.originalName??""],n.remove(),d.remove();break;case"video":n.src=i,l.remove(),d.remove();break;case"document":let t;switch(""){case"pdf":t=window.getIcon("file-pdf");break;case"csv":t=window.getIcon("file-csv");break;case"doc":t=window.getIcon("file-doc");break;case"txt":t=window.getIcon("file-txt");break;case"xls":t=window.getIcon("file-xls");break;default:t=window.getIcon("file")}d.innerText=e.originalFile.name,d.prepend(t),l.remove(),n.remove()}s.dataset.previewUrl=i}catch(t){console.warn("Failed to create preview for upload:",e.id,t)}const i=s.querySelector("summary span");i&&(i.textContent=e.meta?.originalName||"Unknown file");const a=s.querySelector("details");a&&e.meta&&(a.textContent=`${this.formatBytes(e.meta.size)} • ${e.meta.type}`),s.querySelectorAll("input").forEach((t=>{let s=t.id;if(s){let r=s+e.id,o=t.parentNode.querySelector(`label[for="${s}"]`);t.id=r,o&&(o.htmlFor=r)}})),r&&r.appendChild(s)}o.querySelector(".wrap").appendChild(r)}document.querySelector(".field.upload").appendChild(o),o=document.querySelector("dialog.restore-uploads"),this.restoreModal=new window.jvbModal(o),this.restoreSelection=new window.jvbHandleSelection({container:o,ui:{selectAll:o.querySelector("#select-all-restore"),count:o.querySelector(".selection-count")}}),this.restoreModal.handleOpen()}async restoreSelectedUploads(e){const t=new Map;if(e.forEach((e=>{t.has(e.fieldId)||t.set(e.fieldId,[]),t.get(e.fieldId).push(e.uploadId)})),!this.db)return;const s=this.db.transaction(["fieldStates"],"readonly").objectStore("fieldStates");for(const[e,r]of t.entries()){const t=s.get(e),o=await new Promise((e=>{t.onsuccess=()=>e(t.result),t.onerror=()=>e(null)}));o&&(o.uploads=o.uploads.filter((e=>r.includes(e.id))),await this.restoreField(o))}}async restoreField(e){const{config:t,context:s,uploads:r,groups:o,id:i}=e;s.modalType&&await this.openModalForRestore(s);let a=document.querySelector(`.field.upload[data-field="${t.name}"]`);if(!a){const e=`${t.content}_${t.itemID}_${t.name}`;a=document.querySelector(`.field.upload[data-uploader="${e}"]`)}if(!a)return void console.warn(`Field ${t.name} not found for restoration`,t);let l=a.dataset.uploader;l&&this.fields.has(l)||(l=this.registerUploader(a,t));const n=this.fields.get(l);if(n){n.state=e.state||"ready",n.ui=this.buildFieldUI(a),n.ui.groups?.display&&(n.ui.groups.display.hidden=!1),o&&o.length>0&&await this.restoreGroups(l,o);for(const e of r)await this.restoreUpload(n,e);this.updateFieldState(l),this.maybeLockUploads(l),"direct"===t.mode&&"post_group"!==t.destination&&await this.queueUpload(l)}else console.error("Failed to register field for restoration")}async restoreUpload(e,t){const s=await this.uploadStore.getBlob(t.id);if(!s)return void console.warn("Blob data not found for upload:",t.id);{const e=s.data instanceof File?s.data:new File([s.data],s.name,{type:s.type,lastModified:s.lastModified});t.originalFile=e,t.processedFile=e,t.preview=this.createPreviewUrl(e)}e.uploads||(e.uploads=new Set),e.uploads.add(t.id);const r=this.getSubtypeFromMime(t.originalFile.type);let o;if(t.element=this.createUploadElement({...t,subtype:r},"post_group"===e.config.destination),o=t.groupId&&e.ui.groups.groups.has(t.groupId)?e.ui.groups.groups.get(t.groupId).querySelector(".item-grid"):e.ui.preview,o&&(o.appendChild(t.element),t.location=o),this.uploads.set(t.id,t),t.groupId){const e=this.groups.get(t.groupId);e&&e.uploads&&e.uploads.add(t.id)}}async restoreGroups(e,t){for(const s of t){const t=this.createGroup(e,s.id);if(t&&(s.meta&&(t.meta={...s.meta}),s.changes&&(t.changes={...s.changes}),s.title)){const e=t.element.querySelector('[name*="post_title"]');e&&(e.value=s.title)}}}async openModalForRestore(e){const{modalType:t,formId:s}=e;let r=null;switch(t){case"create":r=document.querySelector('[data-action="create"]');break;case"edit":r=document.querySelector(`[data-action="edit"][data-id="${e.itemId}"]`);break;case"bulkEdit":r=document.querySelector('[data-action="bulk-edit"]')}r&&(r.click(),await new Promise((e=>setTimeout(e,300))))}handleFieldStoreEvent(e,t){switch(e){case"data-loaded":break;case"item-saved":console.log(`Field state saved: ${t.key}`)}}handleUploadStoreEvent(e,t){switch(e){case"data-loaded":this.checkForStoredUploads();break;case"item-saved":this.showSaveIndicator(t.key)}}async saveUpload(e){const t=e.processedFile||e.originalFile||e.file;if(t instanceof File||t instanceof Blob){await this.uploadStore.saveBlob(e.id,t);const{file:s,originalFile:r,processedFile:o,...i}=e;await this.uploadStore.save(i)}else await this.uploadStore.save(e)}async loadFields(){(await this.fieldStore.getAll()).forEach((e=>{e.uploads&&Array.isArray(e.uploads)&&(e.uploads=new Set(e.uploads.map((e=>e.id)))),this.fields.set(e.fieldId,e)}))}async loadUploads(){(await this.uploadStore.getAll()).forEach((e=>{this.uploads.set(e.id,e)}))}subscribe(e){return this.subscribers.add(e),()=>this.subscribers.delete(e)}notify(e,t){this.subscribers.forEach((s=>s(e,t)))}destroy(){document.removeEventListener("click",this.clickHandler),document.removeEventListener("change",this.changeHandler),document.removeEventListener("dragenter",this.dragEnterHandler),document.removeEventListener("dragleave",this.dragLeaveHandler),document.removeEventListener("dragover",this.dragOverHandler),document.removeEventListener("drop",this.dropHandler),this.dragController&&this.dragController.destroy(),this.selectionHandlers.forEach((e=>e.destroy())),this.selectionHandlers.clear(),this.cleanupAllPreviewUrls(),this.fields.clear(),this.uploads.clear(),this.groups.clear(),this.selected.clear(),this.subscribers.clear()}cleanupRestore(){this.restoreModal.handleClose(),this.restoreSelection.destroy(),this.restoreSelection=null,this.restoreModal.destroy(),this.restoreModal.modal.remove(),this.restoreModal=null}async cleanupStoredUploads(){this.fieldStore.clear(),this.uploadStore.clear()}async clearField(e){await this.fieldStore.delete(e);const t=this.fields.get(e);if(t?.uploads)for(const e of t.uploads)await this.uploadStore.delete(e);this.fields.delete(e)}async clearUpload(e,t=!0){const s=this.uploads.get(e);if(s){if(this.revokePreviewUrl(s.preview),s.element){const e=s.element.dataset.previewUrl;this.revokePreviewUrl(e),delete s.element.dataset.previewUrl}t&&await this.schedulePersistance(s.fieldId),this.uploads.delete(e),this.uploadStore.delete(e),this.uploadStore.delete(e,"blobs")}}cleanupAllPreviewUrls(){this.previewUrls&&(this.previewUrls.forEach((e=>{try{URL.revokeObjectURL(e)}catch(e){}})),this.previewUrls.clear())}}document.addEventListener("DOMContentLoaded",(()=>{window.jvbUploads=new e}))})();
\ No newline at end of file
+(()=>{class e{constructor(){this.queue=window.jvbQueue,this.a11y=window.jvbA11y,this.error=window.jvbError,this.fieldStore=new window.jvbStore({name:"upload_fields",storeName:"fieldStates",keyPath:"id",version:2,indexes:[{name:"fieldId",keyPath:"fieldId"},{name:"timestamp",keyPath:"timestamp"},{name:"content",keyPath:"content"},{name:"itemId",keyPath:"itemId"},{name:"status",keyPath:"status"}],stripDOMReferences:!0,TTL:6048e5}),this.uploadStore=new window.jvbStore({name:"uploads",storeName:"uploads",keyPath:"id",storeBlobs:!0,indexes:[{name:"fieldId",keyPath:"fieldId"},{name:"status",keyPath:"status"},{name:"groupId",keyPath:"groupId"},{name:"attachmentId",keyPath:"attachmentId"}]}),window.jvbUploadBlobs=this.uploadStore,this.fieldStore.subscribe(this.handleFieldStoreEvent.bind(this)),this.uploadStore.subscribe(this.handleUploadStoreEvent.bind(this)),this.initWorker(),this.fields=new Map,this.uploads=new Map,this.groups=new Map,this.selected=new Map,this.selectionHandlers=new Map,this.previewUrls=new Set,this.subscribers=new Set,this.dragController=null,this.selectors={field:{field:"[data-upload-field]",input:'input[type="file"]',hiddenValue:'input[type="hidden"]',dropZone:".file-upload-container",preview:".item-grid.preview",progress:".image-progress"},groups:{container:".upload-group",grid:".item-grid.group",header:".group-header",selectAll:'[name="select-all-group"]',actions:".group-actions",count:".selection-controls .info"},items:{item:"[data-upload-id]",checkbox:'[name*="select-item"]',featured:'[name="featured"]',details:"details"}},this.statusMapping={received:"Image Received",local_processing:"Processing Image...",queued:"Waiting to upload...",uploading:"Uploading to Server",pending:"Successfully sent to server. In line for further processing.",processing:"Processing on server...",completed:"Upload complete!",failed:"Upload failed (will retry)",failed_permanent:"Upload failed permanently"},this.sortableInstances=new Map,this.sortableConfig={animation:150,draggable:".item",handle:".select-item-label, img",ghostClass:"sortable-ghost",chosenClass:"sortable-chosen",dragClass:"sortable-drag",onEnd:e=>{this.handleReorder(e)}},this.init()}async init(){await this.loadFields(),await this.loadUploads(),this.initializeFields(),this.initListeners(),this.queue.subscribe(((e,t)=>{if("uploads"!==t.endpoint&&"uploads/meta"!==t.endpoint)return;const s=t.data instanceof FormData?t.data.get("fieldId"):t.data.fieldId;switch(e){case"cancel-operation":s&&this.clearField(s);break;case"operation-status":s&&this.updateFieldStatus(s,t.status);break;case"operation-complete":(t.result?.data||[]).forEach((e=>{const t=this.uploads.get(e.upload_id);t&&(t.attachmentId=e.attachment_id,t.status="completed",this.uploads.set(t.id,t))})),s&&this.cleanField(s)}})),window.addEventListener("beforeunload",(()=>{this.cleanupAllPreviewUrls()}))}initWorker(){this.worker={worker:null,timeout:null,tasks:new Map,restart:{count:0,max:3},settings:{timeout:1e4,batchSize:1,maxConcurrent:3,restartAfterTimeout:!0}}}initializeFields(){document.querySelectorAll(this.selectors.field.field).forEach((e=>{this.registerUploader(e)}))}scanFields(e){e.querySelectorAll(this.selectors.field.field).forEach((e=>{this.registerUploader(e)}))}registerUploader(e){const t=this.determineFieldId(e),s=this.extractFieldConfig(e),o={id:t,config:s,element:e,ui:this.buildFieldUI(e),uploads:new Set,groups:new Set,state:"ready"};return this.fields.set(t,o),e.dataset.uploader=t,this.addFieldSelectionHandler(t),"post_group"!==s.destination||this.dragController||this.initGroupFeatures(),"single"!==s.type&&this.initSortable(o),t}extractFieldConfig(e){return{destination:e.dataset.destination||"meta",content:e.dataset.content||null,mode:e.dataset.mode||"direct",type:e.dataset.type||"single",name:e.dataset.field,itemID:e.dataset.itemId||0,maxFiles:parseInt(e.dataset.maxFiles)||999,subtype:e.dataset.subtype||"image"}}buildFieldUI(e){let t={field:e,input:e.querySelector(this.selectors.field.input),dropZone:e.querySelector(this.selectors.field.dropZone),preview:e.querySelector(this.selectors.field.preview),progress:{progress:e.querySelector(this.selectors.field.progress),bar:e.querySelector(".bar"),fill:e.querySelector(".fill"),details:e.querySelector(".details"),text:e.querySelector(".details .text"),count:e.querySelector(".details .count")}},s=e.querySelector(".group-display");return s&&(t.groups={display:s,container:e.querySelector(".item-grid.groups"),empty:e.querySelector(".empty-group"),groups:new Map}),t}initListeners(){this.clickHandler=this.handleClick.bind(this),this.changeHandler=this.handleChange.bind(this),document.addEventListener("click",this.clickHandler),document.addEventListener("change",this.changeHandler),this.dragEnterHandler=this.handleExternalDragEnter.bind(this),this.dragLeaveHandler=this.handleExternalDragLeave.bind(this),this.dragOverHandler=this.handleExternalDragOver.bind(this),this.dropHandler=this.handleExternalDrop.bind(this),document.addEventListener("dragenter",this.dragEnterHandler),document.addEventListener("dragleave",this.dragLeaveHandler),document.addEventListener("dragover",this.dragOverHandler),document.addEventListener("drop",this.dropHandler)}initGroupFeatures(){this.dragController=new window.jvbDragHandler({draggableSelector:this.selectors.items.item,dropTargetSelector:`${this.selectors.field.preview}, ${this.selectors.groups.grid}, .empty-group`,ignoreSelector:"input:not(.upload-select), button, select, textarea, details, summary, a",previewElement:"img, video, .icon",getItemId:e=>e.dataset.uploadId,getSelectedItems:e=>{const t=this.getFieldIdFromElement(e),s=e.dataset.uploadId,o=this.getCurrentSelection(t);return o&&o.includes(s)?o:[s]},validateDrop:(e,t)=>{const s=this.getFieldIdFromElement(t),o=document.querySelector(`[data-upload-id="${e[0]}"]`);return s===this.getFieldIdFromElement(o)},onDrop:(e,t)=>{this.handleItemDrop(e,t),t.scrollIntoView({behavior:"smooth",block:"center"})},onDragStart:e=>{},onDragEnd:(e,t)=>{if(t){const t=document.querySelector(`[data-upload-id="${e[0]}"]`),s=this.getFieldIdFromElement(t),o=this.selectionHandlers.get(s);o?.clearSelection()}},previewOptions:{multiOffset:{x:-60,y:-80},singleOffset:{x:-50,y:-60},showCount:!0}})}initSortable(e){if(!window.Sortable)return;const t=e.element.querySelector(".item-grid:not(.group)");t&&this.sortableInstances.set(`${e.id}-main`,new Sortable(t,{...this.sortableConfig,group:{name:e.id,pull:!0,put:!0}}));e.element.querySelectorAll(".item-grid.group").forEach(((t,s)=>{this.sortableInstances.set(`${e.id}-group-${s}`,new Sortable(t,{...this.sortableConfig,group:{name:e.id,pull:!0,put:!0}}))}))}handleReorder(e){const t=e.to.closest(".field, .upload");if(!t)return;const s=t.closest("form");if(!s)return;const o=s.dataset.formId;if(o&&window.jvbForms){const e=window.jvbForms.forms?.get(o);e?.options.autosave&&window.jvbForms.scheduleSave(e,1e3)}window.jvbA11y&&window.jvbA11y.announce("Item reordered"),t.dispatchEvent(new CustomEvent("jvb-items-reordered",{detail:{from:e.from,to:e.to,oldIndex:e.oldIndex,newIndex:e.newIndex},bubbles:!0}))}handleExternalDragLeave(e){const t=e.target.closest(this.selectors.field.dropZone);t&&!t.contains(e.relatedTarget)&&t.classList.remove("dragover")}handleExternalDragEnter(e){if(!e.dataTransfer.types.includes("Files"))return;const t=e.target.closest(this.selectors.field.dropZone);t&&(e.preventDefault(),t.classList.add("dragover"))}handleExternalDragOver(e){if(!e.dataTransfer.types.includes("Files"))return;e.target.closest(this.selectors.field.dropZone)&&(e.preventDefault(),e.dataTransfer.dropEffect="copy")}handleExternalDrop(e){const t=e.target.closest(this.selectors.field.dropZone);if(!t)return;e.preventDefault(),t.classList.remove("dragover");const s=Array.from(e.dataTransfer.files);if(0===s.length)return;const o=this.getFieldIdFromElement(t);o?(this.processFiles(o,s),this.a11y.announce(`${s.length} file(s) dropped for upload`)):console.error("No field ID found for drop zone")}handleItemDrop(e,t){const s=t.classList.contains("preview");let o=t;if(t.classList.contains("empty-group")){const e=this.getFieldIdFromElement(t),s=this.createGroup(e);if(!s)return void console.error("Failed to create group");o=s.grid}e.forEach((e=>{s?this.removeFromGroup(e):this.addToGroup(e,o)}));const r=this.getFieldIdFromElement(t);this.schedulePersistance(r);const i=e.length>1?`Moved ${e.length} items`:"Moved item";this.a11y.announce(i)}handleClick(e){if(e.target.matches(this.selectors.field.dropZone)||e.target.closest(this.selectors.field.dropZone)){const t=e.target.closest(this.selectors.field.dropZone);if(t&&!e.target.matches("input, button, a")){const e=t.querySelector(this.selectors.field.input);e?.click()}}const t=e.target.closest("[data-action]");t&&this.handleAction(t)}handleChange(e){const t=this.getFieldIdFromElement(e.target);if(e.target.matches(this.selectors.field.input)){const t=this.getFieldIdFromElement(e.target),s=Array.from(e.target.files);s.length>0&&t&&this.processFiles(t,s)}t&&("post_group"===this.fields.get(t).config.destination?this.handleGroupMetaChange(e.target):this.queueUploadMeta(e))}getCurrentSelection(e){let t=[];for(let[s,o]of this.selectionHandlers)(e===s||s.includes(e))&&o.selectedItems.size>0&&(t=t.concat([...o.selectedItems]));return t}getSubtypeFromMime(e){return e.startsWith("image/")?"image":e.startsWith("video/")?"video":"document"}getStatusText(e){return this.statusMapping[e]||e}getStatusIcon(e){return window.getIcon(this.queue.icons[e])}getStatusProgress(e){switch(e){case"local_processing":return 28;case"queued":return 50;case"uploading":return 66;case"pending":return 75;case"processing":return 89;case"completed":return 100;default:return 0}}getModalType(e){if(void 0!==e._cachedModalType)return e._cachedModalType;if(!e||!e.element)return e._cachedModalType=null,null;const t=e.element.closest("dialog");if(!t)return e._cachedModalType=null,null;let s=null;return s=t.classList.contains("edit")?"edit":t.classList.contains("create")?"create":t.classList.contains("bulkEdit")?"bulkEdit":t.className,e._cachedModalType=s,s}handleAction(e){const t=e.dataset.action,s=this.getFieldIdFromElement(e);switch(t){case"add-to-group":this.handleAddToGroup(e);break;case"delete-group":this.handleDeleteGroup(e);break;case"delete-upload":case"remove-from-group":this.handleRemoveItem(e);break;case"upload":this.fields.get(s).element.closest("details").open=!1,document.body.classList.add("uploading"),this.submitUploads(s);break;case"restore":this.handleRestoreUploads().then((()=>{}));break;case"clear-cache":confirm("Save these uploads for later?")||this.cleanupStoredUploads(),this.cleanupRestore()}}handleAddToGroup(e){const t=e.closest(this.selectors.field.field),s=t?.dataset.uploader;if(!s)return;const o=this.selected.get(s);if(o&&0!==o.size){const e=this.createGroup(s);if(!e)return;o.forEach((t=>{this.addToGroup(t,e.grid)}));const t=this.selectionHandlers.get(s);t?.clearSelection(),this.a11y.announce(`Created group with ${o.size} items`)}else this.createGroup(s);this.schedulePersistance(s)}handleDeleteGroup(e){const t=e.closest(this.selectors.groups.container);if(!t)return;const s=t.dataset.groupId,o=this.getFieldIdFromElement(t);if(!confirm("Delete this group? Items will be moved back to the upload area."))return;t.querySelectorAll(this.selectors.items.item).forEach((e=>{const t=e.dataset.uploadId;this.removeFromGroup(t)})),this.deleteGroup(s),this.a11y.announce("Group deleted, items returned to upload area"),this.schedulePersistance(o)}handleRemoveItem(e){const t=e.closest(this.selectors.items.item);if(!t)return;const s=t.dataset.uploadId,o=this.getFieldIdFromElement(t);confirm("Remove this item?")&&(this.removeUpload(o,s),this.a11y.announce("Item removed"),this.schedulePersistance(o))}addFieldSelectionHandler(e){if(this.selectionHandlers.has(e))return this.selectionHandlers.get(e);const t=this.fields.get(e);if(!t)return;const s=t.ui.field;if(!s)return;const o=new window.jvbHandleSelection({container:s,ui:{selectAll:s.querySelector('[name="select-all-uploads"]'),bulkControls:s.querySelector(".selection-actions"),count:s.querySelector(".selection-count")},itemSelector:"[data-upload-id]",checkboxSelector:'[name*="select-item"]'});return o.subscribe(((t,s)=>{switch(t){case"item-selected":case"item-deselected":case"range-selected":this.selected.set(e,s.selectedItems);break;case"select-all":this.handleSelectAll(s.container,s.selected)}})),this.selectionHandlers.set(e,o),o}addGroupSelectionHandler(e,t){const s=`${e}_${t}`;if(this.selectionHandlers.has(s))return this.selectionHandlers.get(s);const o=this.groups.get(t);if(!o)return;const r=new window.jvbHandleSelection({container:o.element,ui:{selectAll:o.element.querySelector(this.selectors.groups.selectAll),bulkControls:o.element.querySelector(this.selectors.groups.actions),count:o.element.querySelector(this.selectors.groups.count)},itemSelector:"[data-upload-id]",checkboxSelector:'[name*="select-item"]'});return r.subscribe(((t,s)=>{switch(t){case"item-selected":case"item-deselected":case"range-selected":this.selected.set(e,s.selectedItems);break;case"select-all":this.handleSelectAll(s.container,s.selected)}})),this.selectionHandlers.set(s,r),r}handleSelectAll(e,t){}determineFieldId(e){return`${e.dataset.content||e.closest("dialog")?.dataset.content||e.closest("form")?.dataset.save||""}_${e.dataset.itemId||e.closest("dialog")?.dataset.itemId||""}_${e.dataset.field||""}`}getFromElement(e,t){const s={field:{selector:this.selectors.field.field,key:"uploader",store:this.fields},upload:{selector:this.selectors.items.item,key:"uploadId",store:this.uploads},group:{selector:this.selectors.groups.container,key:"groupId",store:this.groups}}[t];if(!s)return null;const o=e.closest(s.selector);if(!o)return null;const r=o.dataset[s.key];return s.store.get(r)}getFieldFromElement(e){return this.getFromElement(e,"field")}getUploadFromElement(e){return this.getFromElement(e,"upload")}getGroupFromElement(e){return this.getFromElement(e,"group")}getFieldIdFromElement(e){return this.getFromElement(e,"field")?.id??null}getUploadIdFromElement(e){return this.getFromElement(e,"upload")?.id??null}getGroupIdFromElement(e){return this.getFromElement(e,"group")?.id??null}async processFiles(e,t){const s=this.fields.get(e);if(!s)return;s.ui.dropZone&&(s.ui.dropZone.hidden=!0),s.ui.groups.display&&(s.ui.groups.display.hidden=!1);const o=t.length;let r=0;this.updateUploadProgress(e,0,o,"Processing files..."),s.uploads||(s.uploads=new Set);const i=Array.from(t).map((async(t,i)=>{try{const i=`upload_${Date.now()}_${Math.random().toString(36).substr(2,9)}`,a={id:i,attachment_id:null,fieldId:e,originalFile:t,processedFile:null,preview:null,status:"local_processing",element:null,location:null,meta:{originalName:t.name,size:t.size,type:t.type}};a.preview=this.createPreviewUrl(t),t.type.startsWith("image/")?a.processedFile=await this.processImage(t,s.subtype):a.processedFile=t,await this.uploadStore.saveBlob(i,a.processedFile||t);const l=this.getSubtypeFromMime(t.type);return a.element=this.createUploadElement({...a,subtype:l},"post_group"===s.config.destination),this.showUploadProgress(i,!0),this.updateUploadItemProgress(i,50,"local_processing"),s.ui.preview&&(s.ui.preview.appendChild(a.element),a.location=s.ui.preview),this.uploads.set(i,a),s.uploads.add(i),r++,this.updateUploadProgress(e,r,o,"Processing files..."),this.updateUploadItemProgress(i,100,"processed"),a.status="processed",setTimeout((()=>{this.showUploadProgress(i,!1)}),1e3),i}catch(s){return console.error("Error processing file:",t.name,s),r++,this.updateUploadProgress(e,r,o,"Processing files..."),null}}));await Promise.all(i),this.updateFieldState(e),await this.schedulePersistance(e),"post_group"!==s.config.destination&&(await this.queueUpload(e),this.maybeLockUploads(e))}updateFieldState(e){const t=this.fields.get(e);if(!t||!t.ui.field)return;const s=t.ui.field,o=t.uploads?.size||0,r=t.ui.groups?.container?.querySelectorAll(".upload-group").length>0;s.dataset.hasUploads=o>0?"true":"false",s.dataset.uploadCount=o.toString(),s.dataset.hasGroups=r?"true":"false",t.ui.preview&&t.ui.preview.setAttribute("aria-label",`Upload preview area with ${o} item${1!==o?"s":""}`)}updateUploadProgress(e,t,s,o){const r=this.fields.get(e);if(!r?.ui?.progress?.progress)return;const i=r.ui.progress,a=s>0?t/s*100:0;i.fill&&(i.fill.style.width=`${a}%`),i.text&&(i.text.textContent=o),i.count&&(i.count.textContent=`${t}/${s}`),i.progress.hidden=t===s}updateFieldStatus(e,t){const s=this.fields.get(e);s&&(s.state=t)}updateUploadStatus(e,t){const s=this.uploads.get(e);s&&(s.status=t,this.updateUploadUI(e))}updateUploadUI(e){const t=this.uploads.get(e);if(!t?.element)return;t.element.className=t.element.className.replace(/status-[\w-]+/g,""),t.element.classList.add(`status-${t.status}`);t.element.querySelector(".progress")&&this.updateUploadItemProgress(e,this.getStatusProgress(t.status),t.status)}showUploadProgress(e,t=!0){const s=this.uploads.get(e);if(!s||!s.element)return;const o=s.element.querySelector(".progress");o&&(t?(o.style.removeProperty("animation"),o.hidden=!1):(o.style.animation="fadeOut var(--transition-base)",setTimeout((()=>{o.hidden=!0}),300)))}updateUploadItemProgress(e,t,s=null){const o=this.uploads.get(e);if(!o||!o.element)return;const r=o.element.querySelector(".progress");if(!r)return;const i=r.querySelector(".fill"),a=r.querySelector(".details"),l=r.querySelector(".icon");i&&(i.style.width=`${t}%`),s&&a&&(a.textContent=this.getStatusText(s)),s&&l&&(l.innerHTML=this.getStatusIcon(s).outerHTML)}checkFieldLimits(e,t){const s=this.fields.get(e);if(!s)return!1;return(s.uploads?.size||0)+t<=s.maxFiles}validateFile(e,t){return this.settings.allowedTypes.includes(e.type)?!(e.size>this.settings.maxFileSize)||(this.notify(`File too large: ${this.formatBytes(e.size)}`,"error"),!1):(this.notify(`Invalid file type: ${e.type}`,"error"),!1)}formatBytes(e,t=2){if(0===e)return"0 Bytes";const s=t<0?0:t,o=Math.floor(Math.log(e)/Math.log(1024));return parseFloat((e/Math.pow(1024,o)).toFixed(s))+" "+["Bytes","KB","MB","GB"][o]}shouldProcessClientSide(e,t){return!("image"!==t||!e.type.startsWith("image/"))}async processImage(e,t){const s=this.worker.settings.timeout;return new Promise(((o,r)=>{let i,a=!1;i=setTimeout((()=>{a||(a=!0,this.worker.tasks.delete(t),this.worker.settings.restartAfterTimeout&&this.restartCompressionWorker(),r(new Error(`Processing timeout for ${e.name}`)))}),s),this.worker.tasks.set(t,{file:e,timeoutId:i}),this.handleProcess(e,t).then((e=>{a||(a=!0,clearTimeout(i),this.worker.tasks.delete(t),o(e))})).catch((e=>{a||(a=!0,clearTimeout(i),this.worker.tasks.delete(t),r(e))}))}))}async handleProcess(e,t){if(!e.type.startsWith("image/"))return e;const s=this.getMaxDimension();if(this.shouldUseWorker(e))try{if(this.worker.worker||this.initCompressionWorker(),this.worker.worker)return await this.processWithWorker(e,t,s,.85)}catch(e){console.warn("Worker processing failed, falling back to main thread:",e)}return await this.processOnMainThread(e,s,.85)}async processOnMainThread(e,t,s){return new Promise(((o,r)=>{const i=new Image,a=document.createElement("canvas"),l=a.getContext("2d");let n=null;const d=()=>{i.onload=null,i.onerror=null,n&&(URL.revokeObjectURL(n),n=null),a.width=1,a.height=1,l.clearRect(0,0,1,1)};i.onload=()=>{try{const{width:n,height:c}=this.calculateOptimalDimensions(i,t);a.width=n,a.height=c,l.imageSmoothingEnabled=!0,l.imageSmoothingQuality="high",l.drawImage(i,0,0,n,c);const u=this.getOptimalFormat(e),p=this.getOptimalQuality(e,s);a.toBlob((t=>{if(d(),t){const s=new File([t],this.getProcessedFileName(e,u),{type:u,lastModified:Date.now()});o(s)}else r(new Error("Canvas toBlob failed"))}),u,p)}catch(e){d(),r(new Error(`Canvas processing failed: ${e.message}`))}},i.onerror=()=>{d(),r(new Error(`Failed to load image: ${e.name}`))};try{n=this.createPreviewUrl(e),i.src=n}catch(e){d(),r(new Error(`Failed to create object URL: ${e.message}`))}}))}getOptimalFormat(e){return"image/gif"===e.type||"image/svg+xml"===e.type?e.type:this.supportsWebP()?"image/webp":"image/jpeg"}getOptimalQuality(e,t){return e.size<512e3?Math.max(t,.9):e.size<2097152?t:Math.min(t,.8)}getProcessedFileName(e,t){return e.name.replace(/\.[^/.]+$/,"")+({"image/webp":".webp","image/jpeg":".jpg","image/png":".png","image/gif":".gif"}[t]||".jpg")}getMaxDimension(){const e=window.screen.width,t=window.devicePixelRatio||1;return e*t>2560?2400:e*t>1920?1920:1200}shouldUseWorker(e){return this.worker.worker&&e.size>1048576&&"undefined"!=typeof OffscreenCanvas}async processWithWorker(e,t,s,o){return new Promise(((r,i)=>{if(!this.worker.worker)return void i(new Error("Worker not available"));const a=`${t}_${Date.now()}`,l=t=>{if(t.data.messageId===a)if(this.worker.worker.removeEventListener("message",l),this.worker.worker.removeEventListener("error",n),t.data.success){const s=new File([t.data.blob],this.getProcessedFileName(e,t.data.format||"image/webp"),{type:t.data.format||"image/webp",lastModified:Date.now()});r(s)}else i(new Error(t.data.error||"Worker processing failed"))},n=e=>{this.worker.worker.removeEventListener("message",l),this.worker.worker.removeEventListener("error",n),i(new Error(`Worker error: ${e.message}`))};this.worker.worker.addEventListener("message",l),this.worker.worker.addEventListener("error",n),this.worker.worker.postMessage({messageId:a,file:e,maxDimension:s,quality:o,outputFormat:this.getOptimalFormat(e)})}))}restartCompressionWorker(){this.worker.worker&&(this.worker.worker.terminate(),this.worker.worker=null),this.worker.tasks.clear(),this.worker.restart.count>=this.worker.restart.max?console.error("Max worker restarts reached, disabling worker"):(this.worker.restart.count++,this.initCompressionWorker())}initCompressionWorker(){if(!this.worker.worker&&"undefined"!=typeof Worker)try{const e=new Blob(["\n            self.onmessage = async function(e) {\n                const { messageId, file, maxDimension, quality, outputFormat } = e.data;\n\n                try {\n                    // Create ImageBitmap from file\n                    const bitmap = await createImageBitmap(file);\n\n                    // Calculate dimensions\n                    const scale = Math.min(maxDimension / bitmap.width, maxDimension / bitmap.height, 1);\n                    const width = Math.round(bitmap.width * scale);\n                    const height = Math.round(bitmap.height * scale);\n\n                    // Create OffscreenCanvas\n                    const canvas = new OffscreenCanvas(width, height);\n                    const ctx = canvas.getContext('2d');\n\n                    // Draw and resize\n                    ctx.imageSmoothingEnabled = true;\n                    ctx.imageSmoothingQuality = 'high';\n                    ctx.drawImage(bitmap, 0, 0, width, height);\n\n                    // Clean up bitmap\n                    bitmap.close();\n\n                    // Convert to blob\n                    const blob = await canvas.convertToBlob({\n                        type: outputFormat,\n                        quality: quality\n                    });\n\n                    self.postMessage({\n                        messageId,\n                        success: true,\n                        blob: blob,\n                        format: outputFormat\n                    });\n\n                } catch (error) {\n                    self.postMessage({\n                        messageId,\n                        success: false,\n                        error: error.message\n                    });\n                }\n            };\n        "],{type:"application/javascript"});this.worker.worker=new Worker(this.createPreviewUrl(e))}catch(e){console.warn("Failed to initialize compression worker:",e),this.worker.worker=null}}calculateOptimalDimensions(e,t){let{width:s,height:o}=e;if(s<=t&&o<=t)return{width:s,height:o};const r=Math.min(t/s,t/o);return{width:Math.round(s*r),height:Math.round(o*r)}}supportsWebP(){return 0===document.createElement("canvas").toDataURL("image/webp").indexOf("data:image/webp")}createPreviewUrl(e){const t=URL.createObjectURL(e);return this.previewUrls||(this.previewUrls=new Set),this.previewUrls.add(t),t}revokePreviewUrl(e){e?.startsWith("blob:")&&(URL.revokeObjectURL(e),this.previewUrls?.delete(e))}maybeLockUploads(e){const t=this.fields.get(e);if(!t?.ui?.dropZone)return;if("post_group"===t.config.destination)return;const s=t.uploads?.size||0,o=t.config?.maxFiles||999;t.ui.dropZone.hidden=s>=o,t.element.classList.toggle("at-max-uploads",s>=o)}createUploadElement(e,t=!1){let s=window.getTemplate("uploadItem");if(!s)return void console.error("Image template not found");s.dataset.uploadId=e.id,e.originalFile&&(s.dataset.subtype=this.getSubtypeFromMime(e.originalFile.type)),s.querySelector('[name="featured"]').value=e.id;let[o,r,i,a,l]=[s.querySelector('[name="featured"]'),s.querySelector("img"),s.querySelector("video"),s.querySelector("label > span"),s.querySelector("details")];switch([o.value,r.src,r.alt]=[e.id,e.preview,e.originalFile?.name??e.meta?.originalName??""],s.dataset.subtype){case"image":[r.src,r.alt]=[e.preview,e.originalFile?.name??e.meta?.originalName??""],i.remove(),a.remove();break;case"video":i.src=e.preview,r.remove(),a.remove();break;case"document":const t=e.originalFile?.name??e.meta?.originalName??"",s=t.split(".").pop()?.toLowerCase()??"",o={pdf:"file-pdf",csv:"file-csv",doc:"file-doc",docx:"file-doc",txt:"file-txt",xls:"file-xls",xlsx:"file-xls"},l=window.getIcon(o[s]||"file");a.innerText=e.originalFile.name,a.prepend(l),r.remove(),i.remove()}if(l){let e=window.getTemplate("uploadMeta");e&&l.append(e)}return s.draggable=t,s.querySelectorAll("input").forEach((t=>{let s=t.id;if(s){let o=s+e.id,r=t.parentNode.querySelector(`label[for="${s}"]`);t.id=o,r&&(r.htmlFor=o)}})),s}async submitUploads(e){const t=this.fields.get(e);if(!t?.uploads||0===t.uploads.size)return;let s=Array.from(t.uploads);if(0===s.length)return void this.error.log("No uploads to upload",{component:"UploadManager",action:"submitGroupedUploads",fieldId:e});const o=this.getFieldGroups(e);if(0===o.length)return void this.error.log("No groups created for post_group upload",{component:"UploadManager",action:"submitGroupedUploads",fieldId:e});const r=[],i=new FormData;let a=[];s=s.map((e=>this.uploads.get(e))),o.forEach(((e,t)=>{const o={images:[],fields:{}};for(let[t,s]of Object.entries(e.changes))o.fields[t]=s;s.filter((t=>t.groupId===e.id)).forEach((e=>{if(e){const t=e.processedFile||e.originalFile;if(t){i.append("files[]",t);const s={upload_id:e.id,index:a.length};o.images.push(s),a.push(e.id)}}})),r.push(o)})),s.filter((e=>!Object.hasOwn(e,"groupId"))).forEach((e=>{if(e){const t={images:[],fields:{}},s=e.processedFile||e.originalFile;if(s){i.append("files[]",s);const o={upload_id:e.id,index:a.length};t.images.push(o),a.push(e.id)}r.push(t)}})),i.append("content",t.config.content),i.append("user",t.config.itemID),i.append("posts",JSON.stringify(r)),i.append("upload_ids",JSON.stringify(a));for(const[e,t]of i.entries())console.log(e,t);const l={endpoint:"uploads/groups",method:"POST",data:i,title:`Creating ${r.length} ${t.config.content}${r.length>1?"s":""} from uploads...`,popup:`Creating ${r.length} post${r.length>1?"s":""}...`,canMerge:!1,headers:{action_nonce:jvbSettings.dash},append:"_upload"};try{const e=await this.queue.addToQueue(l);return s.forEach((t=>{let s=this.uploads.get(t);s&&(s.operationId=e,this.updateUploadStatus(t,"queued"))})),t.operationId=e,this.a11y.announce(`Creating ${r.length} post${r.length>1?"s":""} from your uploads`),e}catch(t){throw this.error.log(t,{component:"UploadManager",action:"submitGroupedUploads",fieldId:e}),t}finally{this.schedulePersistance(t.id)}}async queueUpload(e){const t=this.fields.get(e);if(!t?.uploads)return;const s=Array.from(t.uploads);if(0===s.length)return;const o=this.prepareUploadData(t,s);this.a11y.announce("Queuing for upload");let r=1===s.length?"file":"files";const i={endpoint:"uploads",method:"POST",data:o,title:`Uploading ${s.length} ${r} to server...`,popup:`Uploading ${s.length} ${r}...`,canMerge:!1,headers:{action_nonce:jvbSettings.dash},append:"_upload"};try{const e=await this.queue.addToQueue(i);return s.forEach((t=>{let s=this.uploads.get(t);s&&(s.operationId=e,this.updateUploadStatus(t,"queued"))})),t.operationId=e,e}catch(e){throw e}finally{this.schedulePersistance(t.id)}}prepareUploadData(e,t){const s=new FormData;s.append("content",e.config.content),s.append("mode",e.config.mode),s.append("field_name",e.config.name),s.append("fieldId",e.id),s.append("field_type",e.config.type),s.append("subtype",e.config.subtype),s.append("item_id",e.config.itemID),s.append("destination",e.config.destination||"meta");let o=[];const r=this.getFieldGroups(e.id);if("post_group"===e.config.destination&&r.length>0){let e=[],t=[],i=[];r.forEach((r=>{let a=[],l=null;r.uploads.forEach((e=>{let t=this.uploads.get(e);if(t){const e=t.processedFile||t.originalFile;if(e){s.append("files[]",e);o.length;o.push(t.id),a.push(t.id);const r=t.element?.querySelector('[name="featured"]');r?.checked&&(l=t.id)}}})),e.push(a),t.push(r.title||""),i.push(l)})),s.append("groups",JSON.stringify(e)),s.append("group_titles",JSON.stringify(t)),s.append("featured_images",JSON.stringify(i))}else t.forEach((e=>{let t=this.uploads.get(e);if(t){const e=t.processedFile||t.originalFile;e&&(s.append("files[]",e),o.push(t.id))}}));return s.append("upload_ids",JSON.stringify(o)),s}getFieldGroups(e){const t=[];return this.groups.forEach(((s,o)=>{if(s.fieldId===e){const r=this.fields.get(e),i=r?.ui?.groups?.groups?.get(o);t.push({id:o,uploads:Array.from(s.uploads||new Set),changes:s.changes||{},element:i||null})}})),t}async queueUploadMeta(e){const t=this.getUploadFromElement(e.target);if(!t)return;if(!this.fields.get(t.fieldId))return;if(!e.target.closest(".upload-meta"))return;let s={};s[e.target.name]=e.target.value,t.meta={...t.meta,...s};let o={};o[t.attachmentId??t.id]=t.meta;const r={endpoint:"uploads/meta",method:"POST",data:o,title:"Updating meta",canMerge:!0,headers:{action_nonce:jvbSettings.dash}};try{await this.queue.addToQueue(r)}catch(e){this.error.log(e,{component:"UploadManager",action:"sendMetaUpdate",uploadId:t.id})}}createGroup(e,t=null){const s=this.fields.get(e);if(!s)return console.error("Field not found:",e),null;t||(t=`group_${Date.now()}_${Math.random().toString(36).substr(2,9)}`);const o=this.createGroupElement(t,e);if(!o)return console.error("Failed to create group element"),null;s.ui.groups||(s.ui.groups={groups:new Map,container:null,empty:null,display:null}),s.ui.groups.groups.set(t,o),s.ui.groups.container&&s.ui.groups.empty?s.ui.groups.container.insertBefore(o,s.ui.groups.empty):s.ui.groups.container&&s.ui.groups.container.appendChild(o);const r={id:t,fieldId:e,element:o,grid:o.querySelector(".item-grid.group"),uploads:new Set,changes:{}};return this.groups.set(t,r),this.addGroupSelectionHandler(e,t),this.schedulePersistance(e),r}createGroupElement(e,t){let s=window.getTemplate("imageGroup");if(!s)return;s.dataset.groupId=e,s.dataset.fieldId=t;let o=window.getTemplate("groupMetadata");const r=s.querySelector(".fields");if(r&&o){r.append(o);const i=r.querySelector('[name="post_title"]'),a=r.querySelector('[name="post_excerpt"]');i&&(i.id=`${e}_title`,i.name=`${e}[post_title]`),a&&(a.id=`${e}_excerpt`,a.name=`${e}[post_excerpt]`);let l=this.fields.get(t);if(""!==l.config.content){s.querySelector("summary").textContent=l.config.content+" Fields"}}else s.querySelector("details").remove();const i=s.querySelector(".item-grid.group");return i&&(i.dataset.groupId=e),s}deleteGroup(e,t=!0){let s=this.groups.get(e);if(!s)return;let o=!0;t&&s.uploads&&s.uploads.size>0&&(o=!window.confirm("Delete uploads in group?")),t&&o&&s.uploads&&s.uploads.size>0&&Array.from(s.uploads).forEach((e=>{this.addImageToGroup(e,null,!1)})),this.groups.delete(e);let r=s.element;r&&(r.remove(),this.a11y.announce("Group removed")),this.schedulePersistance(s.fieldId)}addToGroup(e,t=null,s=!0){let o=this.uploads.get(e);if(!o)return;let r=this.fields.get(o.fieldId);if(!r)return;if(!t&&o.location===r.ui.preview||t===o.location)return;if(o.location){let t=o.location.dataset.groupId;if(t){let s=this.groups.get(t);s&&s.uploads&&(s.uploads.delete(e),0===s.uploads.size&&this.deleteGroup(t))}}const i=o.element.querySelector('[name*="select-item"]');i&&(i.checked=!1);let a=o.element.querySelector('[name="featured"]');if(a.hidden=!t,t){if(!t.classList.contains("item-grid")||!t.classList.contains("preview")){let s=t.dataset.groupId;a.name=s+"_"+a.name;let r=this.groups.get(s);r||(r=this.createGroup(o.fieldId),t=r.grid,s=r.id),r&&(r.uploads.add(e),o.groupId=s)}}else t=r.ui.preview,o.groupId=null;o.location=t,t.append(o.element),s&&this.schedulePersistance(r.id)}removeFromGroup(e){const t=this.uploads.get(e);if(!t)return;const s=this.fields.get(t.fieldId);if(!s)return;if(t.groupId){const s=this.groups.get(t.groupId);s?.uploads&&(s.uploads.delete(e),0===s.uploads.size&&this.deleteGroup(t.groupId,!1)),t.groupId=null}s.ui?.preview&&(s.ui.preview.appendChild(t.element),t.location=s.ui.preview);const o=t.element.querySelector('[name="featured"]');o&&(o.hidden=!0,o.checked=!1)}removeUpload(e,t){const s=this.fields.get(e),o=this.uploads.get(t);if(!s||!o)return;if(s.uploads?.delete(t),o.groupId){const e=this.groups.get(o.groupId);e&&e.uploads&&(e.uploads.delete(t),0===e.uploads.size&&this.removeGroup(o.groupId))}o.element?.remove(),this.clearUpload(t),this.updateFieldState(e),this.maybeLockUploads(e);const r=this.selectionHandlers.get(s.id);r&&r.deselect(t),this.a11y.announce("Upload removed")}schedulePersistance(e){const t=`persist_${e}`;window.debouncer.schedule(t,(()=>this.persistFieldState(e)),1e3)}async persistFieldState(e){const t=this.fields.get(e);if(!t)return;const s={...t,id:e,fieldId:e,uploads:Array.from(t.uploads||[]).map((e=>this.uploads.get(e))),groups:Array.from(this.groups.entries()).filter((([t,s])=>s.fieldId===e&&s.uploads&&s.uploads.size>0)).map((([e,t])=>({id:t.id,uploads:Array.from(t.uploads),changes:t.changes||{}}))),context:{url:this.normalizeUrl(window.location.href),fullUrl:window.location.href,modalType:this.getModalType(t),formId:t.formId,fieldSelector:`.field.upload[data-field="${t.config.name}"]`},timestamp:Date.now()};await this.fieldStore.save(s)}normalizeUrl(e){try{const t=new URL(e);return t.origin+t.pathname}catch(t){return e}}getFieldUploads(e,t=!1){const s=this.fields.get(e);return s&&s.uploads?Array.from(s.uploads).map((e=>{const s=this.uploads.get(e);return s?t?{id:s.id,fieldId:s.fieldId,status:s.status,attachmentId:s.attachmentId,operationId:s.operationId,groupId:s.groupId||null,changes:s.changes||{},meta:{originalName:s.meta?.originalName||s.originalFile?.name,size:s.meta?.size||s.originalFile?.size,type:s.meta?.type||s.originalFile?.type,title:s.meta?.title,alt:s.meta?.alt,caption:s.meta?.caption}}:s:null})).filter(Boolean):[]}async checkForStoredUploads(){if(!this.db)return;const e=this.db.transaction(["fieldStates"],"readonly").objectStore("fieldStates"),t=(await new Promise((t=>{const s=e.getAll();s.onsuccess=()=>t(s.result)}))).filter((e=>e.uploads.some((e=>!e.operationId&&("completed"===e.status||"processed"===e.status||"local_processing"===e.status||"processed-original"===e.status)))));0!==t.length&&this.showRecoveryNotification(t)}async handleRestoreUploads(){let e=document.querySelector("dialog.restore-uploads");if(!e)return;const t=this.getSelectedRestorationUploads(e);0!==t.length&&(await this.restoreSelectedUploads(t),this.cleanupRestore())}getSelectedRestorationUploads(e){let t=[];return e.querySelectorAll("[type=checkbox]:checked").forEach((e=>{const s=e.closest(".item");s&&t.push({uploadId:s.dataset.uploadId,fieldId:s.dataset.fieldId})})),t}handleGroupMetaChange(e){let t=this.getGroupFromElement(e);if(!t)return;Object.hasOwn(t,"changes")||(t.changes={});let s=e.name;if(s.includes("group")){let e=t.id+"_",o=t.id+"[";s=s.replace(e,"").replace(o,"").replace("]","")}t.changes[`${s}`]=e.value,this.groups.set(t.id,t),this.schedulePersistance(t.fieldId)}async showRecoveryNotification(e){const t=e.reduce(((e,t)=>e+t.uploads.length),0),s=e.reduce(((e,t)=>e+(t.groups?.length||0)),0);let o,r=window.getTemplate("restoreNotification");if(!r)return void console.error("Restore notification template not found");if(s>0){o=`${s} ${s>1?"groups":"group"} with ${t} ${t>1?"uploads":"upload"} can be restored.`}else o=`${t} upload(s) from ${e.length} field(s) can be recovered.`;const i=r.querySelector(".restore-details");i&&(i.textContent=o);for(const t of e){let e=window.getTemplate("restoreField");if(!e)continue;const s=e.querySelector("h3");s&&(s.textContent=t.config.name||"Unnamed Field");const o=e.querySelector(".item-grid.restore");for(const e of t.uploads){let s=window.getTemplate("uploadItem");if(!s)continue;const r=await this.uploadStore.getBlob(e.id);if(r)try{const o=new Blob([r.data],{type:r.type}),i=this.createPreviewUrl(o);let[a,l,n,d,c]=[s.querySelector('[name="featured"]'),s.querySelector("img"),s.querySelector("video"),s.querySelector("label > span"),s.querySelector("details")];s.dataset.uploadId=e.id,s.dataset.fieldId=t.id;let u=this.getSubtypeFromMime(r.type);switch(s.dataset.subtype=u,u){case"image":[l.src,l.alt]=[i,e.originalFile?.name??e.meta?.originalName??""],n.remove(),d.remove();break;case"video":n.src=i,l.remove(),d.remove();break;case"document":let t;switch(""){case"pdf":t=window.getIcon("file-pdf");break;case"csv":t=window.getIcon("file-csv");break;case"doc":t=window.getIcon("file-doc");break;case"txt":t=window.getIcon("file-txt");break;case"xls":t=window.getIcon("file-xls");break;default:t=window.getIcon("file")}d.innerText=e.originalFile.name,d.prepend(t),l.remove(),n.remove()}s.dataset.previewUrl=i}catch(t){console.warn("Failed to create preview for upload:",e.id,t)}const i=s.querySelector("summary span");i&&(i.textContent=e.meta?.originalName||"Unknown file");const a=s.querySelector("details");a&&e.meta&&(a.textContent=`${this.formatBytes(e.meta.size)} • ${e.meta.type}`),s.querySelectorAll("input").forEach((t=>{let s=t.id;if(s){let o=s+e.id,r=t.parentNode.querySelector(`label[for="${s}"]`);t.id=o,r&&(r.htmlFor=o)}})),o&&o.appendChild(s)}r.querySelector(".wrap").appendChild(o)}document.querySelector(".field.upload").appendChild(r),r=document.querySelector("dialog.restore-uploads"),this.restoreModal=new window.jvbModal(r),this.restoreSelection=new window.jvbHandleSelection({container:r,ui:{selectAll:r.querySelector("#select-all-restore"),count:r.querySelector(".selection-count")}}),this.restoreModal.handleOpen()}async restoreSelectedUploads(e){const t=new Map;if(e.forEach((e=>{t.has(e.fieldId)||t.set(e.fieldId,[]),t.get(e.fieldId).push(e.uploadId)})),!this.db)return;const s=this.db.transaction(["fieldStates"],"readonly").objectStore("fieldStates");for(const[e,o]of t.entries()){const t=s.get(e),r=await new Promise((e=>{t.onsuccess=()=>e(t.result),t.onerror=()=>e(null)}));r&&(r.uploads=r.uploads.filter((e=>o.includes(e.id))),await this.restoreField(r))}}async restoreField(e){const{config:t,context:s,uploads:o,groups:r,id:i}=e;s.modalType&&await this.openModalForRestore(s);let a=document.querySelector(`.field.upload[data-field="${t.name}"]`);if(!a){const e=`${t.content}_${t.itemID}_${t.name}`;a=document.querySelector(`.field.upload[data-uploader="${e}"]`)}if(!a)return void console.warn(`Field ${t.name} not found for restoration`,t);let l=a.dataset.uploader;l&&this.fields.has(l)||(l=this.registerUploader(a,t));const n=this.fields.get(l);if(n){n.state=e.state||"ready",n.ui=this.buildFieldUI(a),n.ui.groups?.display&&(n.ui.groups.display.hidden=!1),r&&r.length>0&&await this.restoreGroups(l,r);for(const e of o)await this.restoreUpload(n,e);this.updateFieldState(l),this.maybeLockUploads(l),"direct"===t.mode&&"post_group"!==t.destination&&await this.queueUpload(l)}else console.error("Failed to register field for restoration")}async restoreUpload(e,t){const s=await this.uploadStore.getBlob(t.id);if(!s)return void console.warn("Blob data not found for upload:",t.id);{const e=s.data instanceof File?s.data:new File([s.data],s.name,{type:s.type,lastModified:s.lastModified});t.originalFile=e,t.processedFile=e,t.preview=this.createPreviewUrl(e)}e.uploads||(e.uploads=new Set),e.uploads.add(t.id);const o=this.getSubtypeFromMime(t.originalFile.type);let r;if(t.element=this.createUploadElement({...t,subtype:o},"post_group"===e.config.destination),r=t.groupId&&e.ui.groups.groups.has(t.groupId)?e.ui.groups.groups.get(t.groupId).querySelector(".item-grid"):e.ui.preview,r&&(r.appendChild(t.element),t.location=r),this.uploads.set(t.id,t),t.groupId){const e=this.groups.get(t.groupId);e&&e.uploads&&e.uploads.add(t.id)}}async restoreGroups(e,t){for(const s of t){const t=this.createGroup(e,s.id);if(t&&(s.meta&&(t.meta={...s.meta}),s.changes&&(t.changes={...s.changes}),s.title)){const e=t.element.querySelector('[name*="post_title"]');e&&(e.value=s.title)}}}async openModalForRestore(e){const{modalType:t,formId:s}=e;let o=null;switch(t){case"create":o=document.querySelector('[data-action="create"]');break;case"edit":o=document.querySelector(`[data-action="edit"][data-id="${e.itemId}"]`);break;case"bulkEdit":o=document.querySelector('[data-action="bulk-edit"]')}o&&(o.click(),await new Promise((e=>setTimeout(e,300))))}handleFieldStoreEvent(e,t){switch(e){case"data-loaded":break;case"item-saved":console.log(`Field state saved: ${t.key}`)}}handleUploadStoreEvent(e,t){switch(e){case"data-loaded":this.checkForStoredUploads();break;case"item-saved":this.showSaveIndicator(t.key)}}async saveUpload(e){const t=e.processedFile||e.originalFile||e.file;if(t instanceof File||t instanceof Blob){await this.uploadStore.saveBlob(e.id,t);const{file:s,originalFile:o,processedFile:r,...i}=e;await this.uploadStore.save(i)}else await this.uploadStore.save(e)}async loadFields(){(await this.fieldStore.getAll()).forEach((e=>{e.uploads&&Array.isArray(e.uploads)&&(e.uploads=new Set(e.uploads.map((e=>e.id)))),this.fields.set(e.fieldId,e)}))}async loadUploads(){(await this.uploadStore.getAll()).forEach((e=>{this.uploads.set(e.id,e)}))}subscribe(e){return this.subscribers.add(e),()=>this.subscribers.delete(e)}notify(e,t){this.subscribers.forEach((s=>s(e,t)))}destroy(){document.removeEventListener("click",this.clickHandler),document.removeEventListener("change",this.changeHandler),document.removeEventListener("dragenter",this.dragEnterHandler),document.removeEventListener("dragleave",this.dragLeaveHandler),document.removeEventListener("dragover",this.dragOverHandler),document.removeEventListener("drop",this.dropHandler),this.dragController&&this.dragController.destroy(),this.selectionHandlers.forEach((e=>e.destroy())),this.selectionHandlers.clear(),this.cleanupAllPreviewUrls(),this.sortableInstances.forEach((e=>{e?.destroy&&e.destroy()})),this.sortableInstances.clear(),this.fields.clear(),this.uploads.clear(),this.groups.clear(),this.selected.clear(),this.subscribers.clear()}destroySortable(e){Array.from(this.sortableInstances.keys()).filter((t=>t.startsWith(e))).forEach((e=>{const t=this.sortableInstances.get(e);t?.destroy&&t.destroy(),this.sortableInstances.delete(e)}))}cleanupRestore(){this.restoreModal.handleClose(),this.restoreSelection.destroy(),this.restoreSelection=null,this.restoreModal.destroy(),this.restoreModal.modal.remove(),this.restoreModal=null}async cleanupStoredUploads(){this.fieldStore.clear(),this.uploadStore.clear()}async clearField(e){await this.fieldStore.delete(e);const t=this.fields.get(e);if(t?.uploads)for(const e of t.uploads)await this.uploadStore.delete(e);this.fields.delete(e)}async clearUpload(e,t=!0){const s=this.uploads.get(e);if(s){if(this.revokePreviewUrl(s.preview),s.element){const e=s.element.dataset.previewUrl;this.revokePreviewUrl(e),delete s.element.dataset.previewUrl}t&&await this.schedulePersistance(s.fieldId),this.uploads.delete(e),this.uploadStore.delete(e),this.uploadStore.delete(e,"blobs")}}cleanupAllPreviewUrls(){this.previewUrls&&(this.previewUrls.forEach((e=>{try{URL.revokeObjectURL(e)}catch(e){}})),this.previewUrls.clear())}}document.addEventListener("DOMContentLoaded",(()=>{window.jvbUploads=new e}))})();
\ No newline at end of file
diff --git a/assets/js/min/utility.min.js b/assets/js/min/utility.min.js
index d1c6ce2..c79b1bb 100644
--- a/assets/js/min/utility.min.js
+++ b/assets/js/min/utility.min.js
@@ -1 +1 @@
-(()=>{window.fade=function(t,e=!0){e?t.style.animation="fadeIn var(--transition-base)":(t.style.animation="fadeOut var(--transition-base)",window.debouncer.schedule(`remove-${t.dataset.id??t.id??t.className.replace(" ","-")}`,(()=>{t.remove()}),500))},window.formatTimeAgo=function(t){const e=t instanceof Date?t:new Date(t),n=new Date,i=Math.floor((n-e)/1e3),o=Math.floor(i/60),r=Math.floor(o/60),a=Math.floor(r/24);return r<24?0===r?0===o?"Just now":`${o} ${1===o?"minute":"minutes"} ago`:`${r} ${1===r?"hour":"hours"} ago`:a<7?`${a} ${1===a?"day":"days"} ago`:e.toLocaleDateString()},window.formatTimeSoon=function(t){const e=t instanceof Date?t:new Date(t),n=new Date;if(e<=n)return"Just now";const i=Math.floor((e-n)/1e3),o=Math.floor(i/60);return i<60?"In a moment":o<5?"In a few minutes":o<20?"Coming up soon":o<60?"In about half an hour":"Later today"},window.uppercaseFirst=function(t){return t.charAt(0).toUpperCase()+t.slice(1)},window.templates=new Map,document.addEventListener("DOMContentLoaded",(()=>{window.loadTemplates()})),window.loadTemplates=function(){document.querySelectorAll("template").forEach((t=>{const e=Array.from(t.classList);if(e.length>0){const n=t.content.cloneNode(!0).firstElementChild;e.forEach((t=>{window.templates.has(t)||window.templates.set(t,n)}))}}))},window.getTemplate=function(t){return 0===window.templates.size&&loadTemplates(),!!window.templates.has(t)&&window.templates.get(t).cloneNode(!0)},window.formatVote=function(t,e){let n=window.getTemplate("voteButton");n.dataset.itemId=t.id,n.dataset.content=t.content;let i=n.querySelector("button.up"),o=n.querySelector("button.down");return"up"===e&&i.classList.add("voted"),"down"===e&&o.classList.add("voted"),t.upvotes>0&&(i.querySelector(".count").textContent=t.upvotes),t.downvotes>0&&(o.querySelector(".count").textContent="-"+t.downvotes),n},window.checkVoteStatus=function(t,e){if(!jvbSettings.currentUser)return"";let n="";return window.userVotes&&window.userVotes[t]?.has(e)&&(n=window.userVotes[t].get(e)),n},window.getIcon=function(t){if(void 0===t)return"";if(window.jvbIcons||(window.jvbIcons=new Map),!window.jvbIcons.has(t)&&jvbSettings.icons[t]){let e=document.createElement("div");e.innerHTML=jvbSettings.icons[t],window.jvbIcons.set(t,e.firstElementChild.cloneNode(!0)),e.remove()}return window.jvbIcons.get(t)?.cloneNode(!0)},window.isEmptyObject=function(t){return 0===Object.keys(t).length},window.formatNumber=function(t){return t.toString().replace(/\B(?=(\d{3})+(?!\d))/g,",")},window.formatPrice=function(t,e="CAD"){return new Intl.NumberFormat("en-CA",{style:"currency",currency:e}).format(t)},window.escapeHtml=function(t){return t?("string"==typeof t||t instanceof String||(t=String(t)),t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;")):""},window.truncateText=function(t,e=100){return!t||t.length<=e?t:t.substring(0,e)+"..."},window.removeChildren=function(t){if(0!==t.children.length)for(;t.firstChild;)t.removeChild(t.firstChild)},window.formatDateRange=function(t,e){const n=new Date(t),i=new Date(e);return n.toDateString()===i.toDateString()?n.toLocaleDateString("en-US",{year:"numeric",month:"short",day:"numeric"}):n.getMonth()===i.getMonth()&&n.getFullYear()===i.getFullYear()?`${n.toLocaleDateString("en-US",{month:"short",day:"numeric"})} - ${i.getDate()}, ${i.getFullYear()}`:n.getFullYear()===i.getFullYear()?`${n.toLocaleDateString("en-US",{month:"short",day:"numeric"})} - ${i.toLocaleDateString("en-US",{month:"short",day:"numeric"})}, ${i.getFullYear()}`:`${n.toLocaleDateString("en-US",{month:"short",day:"numeric",year:"numeric"})} - ${i.toLocaleDateString("en-US",{month:"short",day:"numeric",year:"numeric"})}`},window.debounce=function(t,e=300){let n;return function(...i){clearTimeout(n),n=setTimeout((()=>t.apply(this,i)),e)}},window.throttle=function(t,e){let n;return function(){const i=arguments,o=this;n||(t.apply(o,i),n=!0,setTimeout((()=>n=!1),e))}},window.throttle=function(t,e=300){let n;return function(...i){n||(t.apply(this,i),n=!0,setTimeout((()=>n=!1),e))}},window.uppercaseFirst=function(t){return t.charAt(0).toUpperCase()+t.slice(1)},window.sanitizeHtml=function(t){const e=document.createElement("div");return e.textContent=t,e.innerHTML},window.formatDate=function(t){if(!t)return"";const e=new Date(t),n=new Date,i=Math.floor((n-e)/864e5);return i<1?"Today":i<2?"Yesterday":i<7?`${i} days ago`:e.toLocaleDateString()},window.getPluralContent=function(t){return"artwork"===t?"artwork":t+"s"},window.showToast=function(t,e="success",n={}){window.jvbNotifications.showToast(t,e,n)},window.dateFormatter=new Intl.DateTimeFormat("en-CA",{year:"numeric",month:"long",day:"numeric",hour:"2-digit",minute:"2-digit",second:"2-digit",timeZoneName:"short"}),window.formatDate=function(t){return t instanceof Date&&!isNaN(t)||(t=new Date(t)),window.dateFormatter.format(t)},window.typeText=function(t,e,n=50){return t.classList.add("typeText"),new Promise((i=>{let o=0;t.textContent="";const r=setInterval((()=>{o<e.length?(t.textContent+=e.charAt(o),o++):(clearInterval(r),i())}),n)}))},window.eraseText=function(t,e=10){return new Promise((n=>{let i=t.textContent,o=i.length;const r=setInterval((()=>{o>0?(o--,t.textContent=i.substring(0,o)):(clearInterval(r),n())}),e)}))},window.typeLoop=function(t,e,n=50,i=10,o=1e3,r=250){let a=!0;return async function(){for(;a;)await window.typeText(t,e,n),await new Promise((t=>setTimeout(t,o))),await window.eraseText(t,i),await new Promise((t=>setTimeout(t,r)))}(),function(){a=!1}},window.toCamelCase=function(t){return t.replace(/-([a-z])/g,(function(t){return t[1].toUpperCase()}))},window.targetCheck=function(t,e){return"string"==typeof e&&(t.target.closest(e)??!1)},window.getDifferences={VALUE_CREATED:"created",VALUE_UPDATED:"updated",VALUE_DELETED:"deleted",VALUE_UNCHANGED:"unchanged",map:function(t,e){if(this.isFunction(t)||this.isFunction(e))throw"Invalid argument. Function given, object expected.";if(this.isFile(t)||this.isFile(e)){const n=this.compareFiles(t,e);return n===this.VALUE_UNCHANGED?null:{type:n,data:void 0===t?e:t}}if(this.isValue(t)||this.isValue(e)){const n=this.compareValues(t,e);if(n===this.VALUE_UNCHANGED)return null;let i;switch(n){case this.VALUE_CREATED:i=e;break;case this.VALUE_DELETED:i=this.getEmptyValue(t);break;case this.VALUE_UPDATED:default:i=e}return{type:n,data:i}}let n={},i=!1;for(let o in t)if(!this.isFunction(t[o])){let r;e&&void 0!==e[o]&&(r=e[o]);const a=this.map(t[o],r);null!==a&&(a.hasOwnProperty("type")&&a.hasOwnProperty("data")?n[o]=a.data:n[o]=a,i=!0)}if(e)for(let o in e)if(!this.isFunction(e[o])&&(void 0===t||void 0===t[o])){const t=this.map(void 0,e[o]);null!==t&&(t.hasOwnProperty("type")&&t.hasOwnProperty("data")?n[o]=t.data:n[o]=t,i=!0)}return i?n:null},getEmptyValue:function(t){return this.isArray(t)?[]:this.isObject(t)?{}:"number"==typeof t?0:"boolean"!=typeof t&&""},compareValues:function(t,e){return t===e||this.isDate(t)&&this.isDate(e)&&t.getTime()===e.getTime()?this.VALUE_UNCHANGED:void 0===t?this.VALUE_CREATED:void 0===e?this.VALUE_DELETED:this.VALUE_UPDATED},isFunction:function(t){return"[object Function]"===Object.prototype.toString.call(t)},isArray:function(t){return"[object Array]"===Object.prototype.toString.call(t)},isDate:function(t){return"[object Date]"===Object.prototype.toString.call(t)},isObject:function(t){return"[object Object]"===Object.prototype.toString.call(t)},isFile:function(t){return t instanceof File},isValue:function(t){return!this.isObject(t)&&!this.isArray(t)},compareFiles:function(t,e){return!this.isFile(t)&&this.isFile(e)?this.VALUE_CREATED:this.isFile(t)&&!this.isFile(e)?this.VALUE_DELETED:this.isFile(t)&&this.isFile(e)?t.name===e.name&&t.size===e.size&&t.type===e.type&&t.lastModified===e.lastModified?this.VALUE_UNCHANGED:this.VALUE_UPDATED:this.VALUE_UNCHANGED},merge:function(t,e){if(null==t)return e;if(null==e)return t;if(this.isFunction(t)||this.isFunction(e))return e;if(this.isFile(t)||this.isFile(e))return e;if(this.isValue(t)||this.isValue(e)||this.isArray(t)||this.isArray(e))return e;if(this.isObject(t)&&this.isObject(e)){let n={};for(let e in t)this.isFunction(t[e])||(n[e]=t[e]);for(let i in e)this.isFunction(e[i])||(void 0!==t[i]?n[i]=this.merge(t[i],e[i]):n[i]=e[i]);return n}return e}},window.deepMerge=function(t,e){return window.getDifferences.merge(t,e)},window.isInt=function(t){return!isNaN(parseInt(t))&&isFinite(t)},window.isNumeric=function(t){return!isNaN(parseFloat(t))&&isFinite(t)},window.handleListField=function(t,e){if(!Array.isArray(e))return void t.remove();let n=t.querySelector("li");e.forEach((e=>{let i=n.cloneNode(!0);i.textContent=e,t.append(i)})),n.remove()},window.handleTextField=function(t,e){"string"==typeof e?t.textContent=e:t.remove()},window.handleImageField=function(t,e){if(!Array.isArray(e)||0===e)return void t.remove();let n="IMG"===t.tagName?t:t.querySelector("img");n?(n.alt=e.alt,n.src=e.thumbnail,n.dataset.small=e.small,n.dataset.medium=e.medium,n.dataset.large=e.full):t.remove()},window.handleGalleryField=function(t,e){if(!Array.isArray(e))return void t.remove();let n=t.querySelector("img");e.forEach((e=>{let i=n.cloneNode(!0);window.handleImageField(i,e),t.append(i)})),n.remove()},window.uiFromSelectors=function(t,e=null){let n={};for(let[i,o]of Object.entries(t))n[i]="object"==typeof o?window.uiFromSelectors(o,e):e?e.querySelector(o):document.querySelector(o);return n};window.debouncer=new class{constructor(){this.timeouts=new Map,window.addEventListener("beforeunload",(()=>this.cleanup()))}schedule(t,e,n=1e3){this.cancel(t),this.timeouts.set(t,setTimeout((()=>{e(),this.timeouts.delete(t)}),n))}cancel(t){this.timeouts.has(t)&&(clearTimeout(this.timeouts.get(t)),this.timeouts.delete(t))}cleanup(){for(let t of this.timeouts.values())clearTimeout(t);this.timeouts.clear()}}})();
\ No newline at end of file
+(()=>{window.fade=function(t,e=!0){e?t.style.animation="fadeIn var(--transition-base)":(t.style.animation="fadeOut var(--transition-base)",window.debouncer.schedule(`remove-${t.dataset.id??t.id??t.className.replace(" ","-")}`,(()=>{t.remove()}),500))},window.formatTimeAgo=function(t){const e=t instanceof Date?t:new Date(t),n=new Date,i=Math.floor((n-e)/1e3),o=Math.floor(i/60),r=Math.floor(o/60),a=Math.floor(r/24);return r<24?0===r?0===o?"Just now":`${o} ${1===o?"minute":"minutes"} ago`:`${r} ${1===r?"hour":"hours"} ago`:a<7?`${a} ${1===a?"day":"days"} ago`:e.toLocaleDateString()},window.formatTimeSoon=function(t){const e=t instanceof Date?t:new Date(t),n=new Date;if(e<=n)return"Just now";const i=Math.floor((e-n)/1e3),o=Math.floor(i/60);return i<60?"In a moment":o<5?"In a few minutes":o<20?"Coming up soon":o<60?"In about half an hour":"Later today"},window.uppercaseFirst=function(t){return t.charAt(0).toUpperCase()+t.slice(1)},window.templates=new Map,document.addEventListener("DOMContentLoaded",(()=>{window.loadTemplates()})),window.loadTemplates=function(){document.querySelectorAll("template").forEach((t=>{const e=Array.from(t.classList);if(e.length>0){const n=t.content.cloneNode(!0).firstElementChild;e.forEach((t=>{window.templates.has(t)||window.templates.set(t,n)}))}}))},window.getTemplate=function(t){return 0===window.templates.size&&loadTemplates(),!!window.templates.has(t)&&window.templates.get(t).cloneNode(!0)},window.formatVote=function(t,e){let n=window.getTemplate("voteButton");n.dataset.itemId=t.id,n.dataset.content=t.content;let i=n.querySelector("button.up"),o=n.querySelector("button.down");return"up"===e&&i.classList.add("voted"),"down"===e&&o.classList.add("voted"),t.upvotes>0&&(i.querySelector(".count").textContent=t.upvotes),t.downvotes>0&&(o.querySelector(".count").textContent="-"+t.downvotes),n},window.checkVoteStatus=function(t,e){if(!jvbSettings.currentUser)return"";let n="";return window.userVotes&&window.userVotes[t]?.has(e)&&(n=window.userVotes[t].get(e)),n},window.icon=document.querySelector(".icon").cloneNode(!0),window.icon.className="icon",window.getIcon=function(t){if(void 0===t)return"";console.log("Getting icon: "+t);let e=window.icon.cloneNode(!0);return e.classList.add("icon-"+t),e},window.isEmptyObject=function(t){return 0===Object.keys(t).length},window.formatNumber=function(t){return t.toString().replace(/\B(?=(\d{3})+(?!\d))/g,",")},window.formatPrice=function(t,e="CAD"){return new Intl.NumberFormat("en-CA",{style:"currency",currency:e}).format(t)},window.escapeHtml=function(t){return t?("string"==typeof t||t instanceof String||(t=String(t)),t.replace(/&/g,"&amp;").replace(/</g,"&lt;").replace(/>/g,"&gt;").replace(/"/g,"&quot;").replace(/'/g,"&#039;")):""},window.truncateText=function(t,e=100){return!t||t.length<=e?t:t.substring(0,e)+"..."},window.removeChildren=function(t){if(0!==t.children.length)for(;t.firstChild;)t.removeChild(t.firstChild)},window.formatDateRange=function(t,e){const n=new Date(t),i=new Date(e);return n.toDateString()===i.toDateString()?n.toLocaleDateString("en-US",{year:"numeric",month:"short",day:"numeric"}):n.getMonth()===i.getMonth()&&n.getFullYear()===i.getFullYear()?`${n.toLocaleDateString("en-US",{month:"short",day:"numeric"})} - ${i.getDate()}, ${i.getFullYear()}`:n.getFullYear()===i.getFullYear()?`${n.toLocaleDateString("en-US",{month:"short",day:"numeric"})} - ${i.toLocaleDateString("en-US",{month:"short",day:"numeric"})}, ${i.getFullYear()}`:`${n.toLocaleDateString("en-US",{month:"short",day:"numeric",year:"numeric"})} - ${i.toLocaleDateString("en-US",{month:"short",day:"numeric",year:"numeric"})}`},window.debounce=function(t,e=300){let n;return function(...i){clearTimeout(n),n=setTimeout((()=>t.apply(this,i)),e)}},window.throttle=function(t,e){let n;return function(){const i=arguments,o=this;n||(t.apply(o,i),n=!0,setTimeout((()=>n=!1),e))}},window.throttle=function(t,e=300){let n;return function(...i){n||(t.apply(this,i),n=!0,setTimeout((()=>n=!1),e))}},window.uppercaseFirst=function(t){return t.charAt(0).toUpperCase()+t.slice(1)},window.sanitizeHtml=function(t){const e=document.createElement("div");return e.textContent=t,e.innerHTML},window.formatDate=function(t){if(!t)return"";const e=new Date(t),n=new Date,i=Math.floor((n-e)/864e5);return i<1?"Today":i<2?"Yesterday":i<7?`${i} days ago`:e.toLocaleDateString()},window.getPluralContent=function(t){return"artwork"===t?"artwork":t+"s"},window.showToast=function(t,e="success",n={}){window.jvbNotifications.showToast(t,e,n)},window.dateFormatter=new Intl.DateTimeFormat("en-CA",{year:"numeric",month:"long",day:"numeric",hour:"2-digit",minute:"2-digit",second:"2-digit",timeZoneName:"short"}),window.formatDate=function(t){return t instanceof Date&&!isNaN(t)||(t=new Date(t)),window.dateFormatter.format(t)},window.typeText=function(t,e,n=50){return t.classList.add("typeText"),new Promise((i=>{let o=0;t.textContent="";const r=setInterval((()=>{o<e.length?(t.textContent+=e.charAt(o),o++):(clearInterval(r),i())}),n)}))},window.eraseText=function(t,e=10){return new Promise((n=>{let i=t.textContent,o=i.length;const r=setInterval((()=>{o>0?(o--,t.textContent=i.substring(0,o)):(clearInterval(r),n())}),e)}))},window.typeLoop=function(t,e,n=50,i=10,o=1e3,r=250){let a=!0;return async function(){for(;a;)await window.typeText(t,e,n),await new Promise((t=>setTimeout(t,o))),await window.eraseText(t,i),await new Promise((t=>setTimeout(t,r)))}(),function(){a=!1}},window.toCamelCase=function(t){return t.replace(/-([a-z])/g,(function(t){return t[1].toUpperCase()}))},window.targetCheck=function(t,e){return"string"==typeof e&&(t.target.closest(e)??!1)},window.getDifferences={VALUE_CREATED:"created",VALUE_UPDATED:"updated",VALUE_DELETED:"deleted",VALUE_UNCHANGED:"unchanged",map:function(t,e){if(this.isFunction(t)||this.isFunction(e))throw"Invalid argument. Function given, object expected.";if(this.isFile(t)||this.isFile(e)){const n=this.compareFiles(t,e);return n===this.VALUE_UNCHANGED?null:{type:n,data:void 0===t?e:t}}if(this.isValue(t)||this.isValue(e)){const n=this.compareValues(t,e);if(n===this.VALUE_UNCHANGED)return null;let i;switch(n){case this.VALUE_CREATED:i=e;break;case this.VALUE_DELETED:i=this.getEmptyValue(t);break;case this.VALUE_UPDATED:default:i=e}return{type:n,data:i}}let n={},i=!1;for(let o in t)if(!this.isFunction(t[o])){let r;e&&void 0!==e[o]&&(r=e[o]);const a=this.map(t[o],r);null!==a&&(a.hasOwnProperty("type")&&a.hasOwnProperty("data")?n[o]=a.data:n[o]=a,i=!0)}if(e)for(let o in e)if(!this.isFunction(e[o])&&(void 0===t||void 0===t[o])){const t=this.map(void 0,e[o]);null!==t&&(t.hasOwnProperty("type")&&t.hasOwnProperty("data")?n[o]=t.data:n[o]=t,i=!0)}return i?n:null},getEmptyValue:function(t){return this.isArray(t)?[]:this.isObject(t)?{}:"number"==typeof t?0:"boolean"!=typeof t&&""},compareValues:function(t,e){return t===e||this.isDate(t)&&this.isDate(e)&&t.getTime()===e.getTime()?this.VALUE_UNCHANGED:void 0===t?this.VALUE_CREATED:void 0===e?this.VALUE_DELETED:this.VALUE_UPDATED},isFunction:function(t){return"[object Function]"===Object.prototype.toString.call(t)},isArray:function(t){return"[object Array]"===Object.prototype.toString.call(t)},isDate:function(t){return"[object Date]"===Object.prototype.toString.call(t)},isObject:function(t){return"[object Object]"===Object.prototype.toString.call(t)},isFile:function(t){return t instanceof File},isValue:function(t){return!this.isObject(t)&&!this.isArray(t)},compareFiles:function(t,e){return!this.isFile(t)&&this.isFile(e)?this.VALUE_CREATED:this.isFile(t)&&!this.isFile(e)?this.VALUE_DELETED:this.isFile(t)&&this.isFile(e)?t.name===e.name&&t.size===e.size&&t.type===e.type&&t.lastModified===e.lastModified?this.VALUE_UNCHANGED:this.VALUE_UPDATED:this.VALUE_UNCHANGED},merge:function(t,e){if(null==t)return e;if(null==e)return t;if(this.isFunction(t)||this.isFunction(e))return e;if(this.isFile(t)||this.isFile(e))return e;if(this.isValue(t)||this.isValue(e)||this.isArray(t)||this.isArray(e))return e;if(this.isObject(t)&&this.isObject(e)){let n={};for(let e in t)this.isFunction(t[e])||(n[e]=t[e]);for(let i in e)this.isFunction(e[i])||(void 0!==t[i]?n[i]=this.merge(t[i],e[i]):n[i]=e[i]);return n}return e}},window.deepMerge=function(t,e){return window.getDifferences.merge(t,e)},window.isInt=function(t){return!isNaN(parseInt(t))&&isFinite(t)},window.isNumeric=function(t){return!isNaN(parseFloat(t))&&isFinite(t)},window.handleListField=function(t,e){if(!Array.isArray(e))return void t.remove();let n=t.querySelector("li");e.forEach((e=>{let i=n.cloneNode(!0);i.textContent=e,t.append(i)})),n.remove()},window.handleTextField=function(t,e){"string"==typeof e?t.textContent=e:t.remove()},window.handleImageField=function(t,e){if(!Array.isArray(e)||0===e)return void t.remove();let n="IMG"===t.tagName?t:t.querySelector("img");n?(n.alt=e.alt,n.src=e.thumbnail,n.dataset.small=e.small,n.dataset.medium=e.medium,n.dataset.large=e.full):t.remove()},window.handleGalleryField=function(t,e){if(!Array.isArray(e))return void t.remove();let n=t.querySelector("img");e.forEach((e=>{let i=n.cloneNode(!0);window.handleImageField(i,e),t.append(i)})),n.remove()},window.uiFromSelectors=function(t,e=null){let n={};for(let[i,o]of Object.entries(t))n[i]="object"==typeof o?window.uiFromSelectors(o,e):e?e.querySelector(o):document.querySelector(o);return n};window.debouncer=new class{constructor(){this.timeouts=new Map,window.addEventListener("beforeunload",(()=>this.cleanup()))}schedule(t,e,n=1e3){this.cancel(t),this.timeouts.set(t,setTimeout((()=>{e(),this.timeouts.delete(t)}),n))}cancel(t){this.timeouts.has(t)&&(clearTimeout(this.timeouts.get(t)),this.timeouts.delete(t))}cleanup(){for(let t of this.timeouts.values())clearTimeout(t);this.timeouts.clear()}}})();
\ No newline at end of file
diff --git a/assets/js/min/view.min.js b/assets/js/min/view.min.js
index 63ff69b..4b63dae 100644
--- a/assets/js/min/view.min.js
+++ b/assets/js/min/view.min.js
@@ -1 +1 @@
-window.jvbViews=class{constructor(e,t){this.a11y=window.jvbA11y,this.error=window.jvbError,this.container=e,this.initElements(),this.settings=window.jvbUserSettings,this.store=t,this.items={list:new Map,grid:new Map,table:new Map},this.currentView="grid",this.selectedItems=new Set,this.init()}initElements(){this.selectors={grid:".item-grid",table:{table:"table",body:"table body",selectedColumns:".all-filters .multi-select",columns:"thead th"},bulk:{count:".bulk-controls .selected-count",control:".bulk-controls .bulk-actions",select:".bulk-controls select",selectAll:".select-all"}},this.ui=window.uiFromSelectors(this.selectors,this.container)}init(){this.store.subscribe(((e,t)=>{switch(e){case"items-saved":case"item-saved":case"item-deleted":break;case"data-loaded":this.handleItemsUpdate()}})),this.setupViewSwitcher(),this.changeHandler=this.handleChange.bind(this),this.clickHandler=this.handleClick.bind(this),this.lastSelected=null,document.addEventListener("change",this.changeHandler),document.addEventListener("click",this.clickHandler)}handleClick(e){e.target.closest(".select-item-label")&&(e.shiftKey?(e.preventDefault(),this.handleRangeSelection(e.target)):this.lastSelected=e.target.closest(".item"))}handleRangeSelection(e){if(!this.lastSelected)return void(this.lastSelected=e.closest(".item"));const t=e.closest(".item"),i=Array.from(this.container.querySelectorAll(".item")),s=i.indexOf(this.lastSelected),l=i.indexOf(t);if(-1===s||-1===l)return void(this.lastSelected=t);const a=Math.min(s,l),r=Math.max(s,l);let d=0;for(let e=a;e<=r;e++){let t=i[e];this.selectedItems.add(t.dataset.id);let s=t.querySelector(".select-item");s&&!s.checked&&(s.checked=!0,d++)}this.updateSelectionUI(),window.jvbA11y.announce(`Selected ${d} items in range.`)}handleChange(e){e.target.closest(".select-all")?this.selectAll(e.target.checked):e.target.closest(".select-item")?this.toggleSelection(e.target.closest(".item").dataset.id):e.target.closest("details.multi-select")&&this.toggleColumns(e.target.id,e.target.checked)}toggleColumns(e,t){this.ui.table.columns.filter((t=>t.className===e))}setupViewSwitcher(){document.querySelectorAll("[data-view]").forEach((e=>{this.settings.addSetting(e),e.addEventListener("click",(()=>{this.currentView=e.dataset.view,this.render()}))}))}handleDataUpdate(e){console.log(e);const t=e.data?.items||e.items||[];this.render(t)}handleItemsUpdate(){console.log(this.store.data),this.render(this.store.data)}render(e=[]){if(this.store)if(0!==e.length){switch(this.currentView){case"grid":this.renderGrid(e);break;case"table":this.renderTable(e);break;case"list":this.renderList(e)}this.updateSelectionUI()}else this.renderEmpty();else console.error("No store connected to renderer")}renderEmpty(){this.toggleTable(!1),window.removeChildren(this.ui.grid);const e=window.getTemplate("emptyState");e&&(this.ui.grid.appendChild(e),this.a11y?.announce("No items found"))}renderGrid(e){this.toggleGrid(),this.toggleTable(!1),this.ui.grid.classList.remove("list-view"),this.ui.grid.classList.add("grid-view");const t=document.createDocumentFragment();e.forEach((e=>{let i;this.store.renderOrRetrieve?i=this.store.renderOrRetrieve(e,"grid",this.renderGridItem.bind(this)):this.items.grid.has(e.id)?i=this.items.grid.get(e.id):(i=this.renderGridItem(e),this.items.grid.set(e.id,i)),t.appendChild(i)})),this.ui.grid.appendChild(t)}renderGridItem(e){const t=window.getTemplate("gridView");t.dataset.id=e.id,e._pending&&t.classList.add("pending");let[i,s,l,a,r]=[t.querySelector("input"),t.querySelector("label"),t.querySelector("img"),t.querySelector('[data-action="edit"]'),t.querySelector('[data-action="trash"]')];return[i.value,i.id,i.checked,s.htmlFor,a.dataset.id,r.dataset.id]=[e.id,`select-${e.id}`,this.selectedItems.has(`${e.id}`),`select-${e.id}`,e.id,e.id],"progress"===this.store.config.storeName?[l.src,l.alt]=[e.images[e.fields.timeline[0].post_thumbnail]?.medium??"",e.images[e.fields.timeline[0].post_thumbnail]?.alt??""]:[l.src,l.alt]=[e.images[e.fields.post_thumbnail]?.medium??"",e.images[e.fields.post_thumbnail]?.alt??""],t}toggleTable(e){if(this.ui.table.selectedColumns.hidden=!e,e&&!this.ui.table.table){let e=window.getTemplate("contentTable");this.container.append(e),this.ui.table.table=this.container.querySelector("form.table"),this.ui.table.body=this.ui.table.table.querySelector("tbody"),this.ui.table.columns=this.container.querySelectorAll(this.selectors.table.columns)}this.ui.table.table&&(this.ui.table.table.hidden=!e,window.removeChildren(this.ui.table.body)),this.ui.table.selectedColumns.hidden=!e}toggleGrid(){window.removeChildren(this.ui.grid)}renderTable(e){this.toggleTable(!0),this.toggleGrid(),e.forEach((e=>{let t;this.items.table.has(e.id)?t=this.items.table.get(e.id):(t=this.store.renderOrRetrieve(e,"table",this.renderTableItem.bind(this)),this.items.table.set(e.id,t)),this.ui.table.body.append(t)})),window.jvbSelector.scanExistingFields()}renderTableItem(e){let t=["",0];const i=window.getTemplate("tableView");return i.dataset.id=e.id,[i.querySelector(".select-item").id,i.querySelector(".select-item").value,i.querySelector(".select-item").checked,i.querySelector(".select-item + label").htmlFor,i.querySelector(`input[name="post_status"][value="${e.status}"]`).checked]=[e.id,e.id,this.selectedItems.has(`${e.id}`),e.id,e.status],i.querySelectorAll("td[data-field]").forEach((i=>{let s,l=e.fields[i.dataset.field],a=i.querySelector("label"),r=t.includes(l);switch(i.dataset.fieldType){case"text":case"number":case"url":case"tel":case"email":r||(i.querySelector("input").value=l),a.remove();break;case"textarea":r||(i.querySelector("textarea").value=l),a.remove();break;case"taxonomy":a.remove(),r||(s=i.querySelector("input[type=hidden]"),s.value=l);break;case"image":if(!r){let t=window.getTemplate("uploadItem"),s=t.querySelector("img");[s.src,s.alt]=[e.images[l].medium??"",e.images[l].alt??""],i.querySelector(".item-grid").append(t),i.querySelector("input[type=hidden]").value=l}i.querySelectorAll(".progress,label,.upload-select,.status,details").forEach((e=>{e.remove()}));break;case"true_false":r||(i.querySelector("input").checked=1===parseInt(l)),i.querySelector(".toggle-label")?.remove();break;case"select":a.remove();case"radio":case"checkbox":i.querySelector(".label")?.remove(),r||(l=l.split(","),l.forEach((e=>{s=i.querySelector(`[value="${e}"]`),s&&(s.checked=!0)})));break;default:r||console.log(l)}})),i}renderList(e){this.toggleGrid(),this.toggleTable(!1),this.ui.grid.classList.remove("grid-view"),this.ui.grid.classList.add("list-view"),e.forEach((e=>{let t;this.items.list.has(e.id)?t=this.items.list.get(e.id):(t=this.store.renderOrRetrieve(e,"list",this.renderListItem.bind(this)),this.items.list.set(e.id,t)),this.ui.grid.appendChild(t)}))}renderListItem(e){const t=window.getTemplate("listView");t.dataset.id=e.id,e._pending&&t.classList.add("pending");let i=t.querySelector(".select-item"),s=t.querySelector(".select-item + label");[i.id,i.value,i.checked,s.htmlFor]=[e.id,e.id,this.selectedItems.has(`${e.id}`),e.id],t.querySelectorAll("[data-attr]").forEach((t=>{""!==e[t.dataset.attr]?t.textContent=e[t.dataset.attr]:t.remove()})),t.querySelectorAll("[data-field]").forEach((t=>{let i=e.fields[t.dataset.field];""!==i?"DIV"===t.tagName?t.innerHTML=i:t.textContent=i:t.remove()}));let l=t.querySelector("img");return l&&([l.src,l.alt]=[e.images[e.fields.post_thumbnail]?.medium??"",e.images[e.fields.post_thumbnail]?.alt??""]),t}toggleSelection(e){this.selectedItems.has(e)?this.selectedItems.delete(e):this.selectedItems.add(e),this.updateSelectionUI()}selectAll(e){const t=this.container.querySelectorAll(".item");e||(this.selectedItems.clear(),this.ui.bulk.selectAll.checked=!1,this.ui.bulk.select.value=""),t.forEach((t=>{e&&this.selectedItems.add(t.dataset.id),t.querySelector(".select-item").checked=e})),this.updateSelectionUI()}clearSelection(){this.selectAll(!1),this.ui.bulk.select.value=""}updateSelectionUI(){const e=this.selectedItems.size;if(this.ui.bulk.control&&(this.ui.bulk.control.hidden=0===e),this.ui.bulk.count){let t=1===e?"item":"items";this.ui.bulk.count.hidden=0===e,this.ui.bulk.count.textContent=0===e?"":`${e} ${t} selected`}}};
\ No newline at end of file
+window.jvbViews=class{constructor(e,t){this.a11y=window.jvbA11y,this.error=window.jvbError,this.container=e,this.initElements(),this.settings=window.jvbUserSettings,this.store=t,this.items={list:new Map,grid:new Map,table:new Map},this.currentView="grid",this.selectedItems=new Set,this.init()}initElements(){this.selectors={grid:".item-grid",table:{table:"table",body:"table body",selectedColumns:".all-filters .multi-select",columns:"thead th"},bulk:{count:".bulk-controls .selected-count",control:".bulk-controls .bulk-actions",select:".bulk-controls select",selectAll:".select-all"}},this.ui=window.uiFromSelectors(this.selectors,this.container)}init(){this.store.subscribe(((e,t)=>{switch(e){case"items-saved":case"item-saved":case"item-deleted":break;case"data-loaded":this.handleItemsUpdate()}})),this.setupViewSwitcher(),this.changeHandler=this.handleChange.bind(this),this.clickHandler=this.handleClick.bind(this),this.lastSelected=null,document.addEventListener("change",this.changeHandler),document.addEventListener("click",this.clickHandler)}handleClick(e){e.target.closest(".select-item-label")&&(e.shiftKey?(e.preventDefault(),this.handleRangeSelection(e.target)):this.lastSelected=e.target.closest(".item"))}handleRangeSelection(e){if(!this.lastSelected)return void(this.lastSelected=e.closest(".item"));const t=e.closest(".item"),i=Array.from(this.container.querySelectorAll(".item")),s=i.indexOf(this.lastSelected),l=i.indexOf(t);if(-1===s||-1===l)return void(this.lastSelected=t);const a=Math.min(s,l),r=Math.max(s,l);let d=0;for(let e=a;e<=r;e++){let t=i[e];this.selectedItems.add(t.dataset.id);let s=t.querySelector(".select-item");s&&!s.checked&&(s.checked=!0,d++)}this.updateSelectionUI(),window.jvbA11y.announce(`Selected ${d} items in range.`)}handleChange(e){e.target.closest(".select-all")?this.selectAll(e.target.checked):e.target.closest(".select-item")?this.toggleSelection(e.target.closest(".item").dataset.id):e.target.closest("details.multi-select")&&this.toggleColumns(e.target.id,e.target.checked)}toggleColumns(e,t){this.ui.table.columns.filter((t=>t.className===e))}setupViewSwitcher(){document.querySelectorAll("[data-view]").forEach((e=>{this.settings.addSetting(e),e.addEventListener("click",(()=>{this.currentView=e.dataset.view,this.render()}))}))}handleDataUpdate(e){console.log(e);const t=e.data?.items||e.items||[];this.render(t)}handleItemsUpdate(){console.log(this.store.data),this.render(this.store.data)}render(e=[]){if(this.store)if(console.log(e),0!==e.length){switch(this.currentView){case"grid":this.renderGrid(e);break;case"table":this.renderTable(e);break;case"list":this.renderList(e)}this.updateSelectionUI()}else this.renderEmpty();else console.error("No store connected to renderer")}renderEmpty(){this.toggleTable(!1),window.removeChildren(this.ui.grid);const e=window.getTemplate("emptyState");e&&(this.ui.grid.appendChild(e),this.a11y?.announce("No items found"))}renderGrid(e){this.toggleGrid(),this.toggleTable(!1),this.ui.grid.classList.remove("list-view"),this.ui.grid.classList.add("grid-view");const t=document.createDocumentFragment();e.forEach((e=>{let i;this.store.renderOrRetrieve?i=this.store.renderOrRetrieve(e,"grid",this.renderGridItem.bind(this)):this.items.grid.has(e.id)?i=this.items.grid.get(e.id):(i=this.renderGridItem(e),this.items.grid.set(e.id,i)),t.appendChild(i)})),this.ui.grid.appendChild(t)}renderGridItem(e){const t=window.getTemplate("gridView");t.dataset.id=e.id,e._pending&&t.classList.add("pending");let[i,s,l,a,r]=[t.querySelector("input"),t.querySelector("label"),t.querySelector("img"),t.querySelector('[data-action="edit"]'),t.querySelector('[data-action="trash"]')];return[i.value,i.id,i.checked,s.htmlFor,a.dataset.id,r.dataset.id]=[e.id,`select-${e.id}`,this.selectedItems.has(`${e.id}`),`select-${e.id}`,e.id,e.id],[l.src,l.alt]=[e.images[e.fields.post_thumbnail]?.medium??"",e.images[e.fields.post_thumbnail]?.alt??""],t}toggleTable(e){if(this.ui.table.selectedColumns.hidden=!e,e&&!this.ui.table.table){let e=window.getTemplate("contentTable");this.container.append(e),this.ui.table.table=this.container.querySelector("form.table"),this.ui.table.body=this.ui.table.table.querySelector("tbody"),this.ui.table.columns=this.container.querySelectorAll(this.selectors.table.columns)}this.ui.table.table&&(this.ui.table.table.hidden=!e,window.removeChildren(this.ui.table.body)),this.ui.table.selectedColumns.hidden=!e}toggleGrid(){window.removeChildren(this.ui.grid)}renderTable(e){this.toggleTable(!0),this.toggleGrid(),e.forEach((e=>{let t;this.items.table.has(e.id)?t=this.items.table.get(e.id):(t=this.store.renderOrRetrieve(e,"table",this.renderTableItem.bind(this)),this.items.table.set(e.id,t)),this.ui.table.body.append(t)})),window.jvbSelector.scanExistingFields()}renderTableItem(e){let t=["",0];const i=window.getTemplate("tableView");return i.dataset.id=e.id,[i.querySelector(".select-item").id,i.querySelector(".select-item").value,i.querySelector(".select-item").checked,i.querySelector(".select-item + label").htmlFor,i.querySelector(`input[name="post_status"][value="${e.status}"]`).checked]=[e.id,e.id,this.selectedItems.has(`${e.id}`),e.id,e.status],i.querySelectorAll("td[data-field]").forEach((i=>{let s,l=e.fields[i.dataset.field],a=i.querySelector("label"),r=t.includes(l);switch(i.dataset.fieldType){case"text":case"number":case"url":case"tel":case"email":r||(i.querySelector("input").value=l),a.remove();break;case"textarea":r||(i.querySelector("textarea").value=l),a.remove();break;case"taxonomy":a.remove(),r||(s=i.querySelector("input[type=hidden]"),s.value=l);break;case"image":if(!r){let t=window.getTemplate("uploadItem"),s=t.querySelector("img");[s.src,s.alt]=[e.images[l].medium??"",e.images[l].alt??""],i.querySelector(".item-grid").append(t),i.querySelector("input[type=hidden]").value=l}i.querySelectorAll(".progress,label,.upload-select,.status,details").forEach((e=>{e.remove()}));break;case"true_false":r||(i.querySelector("input").checked=1===parseInt(l)),i.querySelector(".toggle-label")?.remove();break;case"select":a.remove();case"radio":case"checkbox":i.querySelector(".label")?.remove(),r||(l=l.split(","),l.forEach((e=>{s=i.querySelector(`[value="${e}"]`),s&&(s.checked=!0)})));break;default:r||console.log(l)}})),i}renderList(e){this.toggleGrid(),this.toggleTable(!1),this.ui.grid.classList.remove("grid-view"),this.ui.grid.classList.add("list-view"),e.forEach((e=>{let t;this.items.list.has(e.id)?t=this.items.list.get(e.id):(t=this.store.renderOrRetrieve(e,"list",this.renderListItem.bind(this)),this.items.list.set(e.id,t)),this.ui.grid.appendChild(t)}))}renderListItem(e){const t=window.getTemplate("listView");t.dataset.id=e.id,e._pending&&t.classList.add("pending");let i=t.querySelector(".select-item"),s=t.querySelector(".select-item + label");[i.id,i.value,i.checked,s.htmlFor]=[e.id,e.id,this.selectedItems.has(`${e.id}`),e.id],t.querySelectorAll("[data-attr]").forEach((t=>{""!==e[t.dataset.attr]?t.textContent=e[t.dataset.attr]:t.remove()})),t.querySelectorAll("[data-field]").forEach((t=>{let i=e.fields[t.dataset.field];""!==i?"DIV"===t.tagName?t.innerHTML=i:t.textContent=i:t.remove()}));let l=t.querySelector("img");return l&&([l.src,l.alt]=[e.images[e.fields.post_thumbnail]?.medium??"",e.images[e.fields.post_thumbnail]?.alt??""]),t}toggleSelection(e){this.selectedItems.has(e)?this.selectedItems.delete(e):this.selectedItems.add(e),this.updateSelectionUI()}selectAll(e){const t=this.container.querySelectorAll(".item");e||(this.selectedItems.clear(),this.ui.bulk.selectAll.checked=!1,this.ui.bulk.select.value=""),t.forEach((t=>{e&&this.selectedItems.add(t.dataset.id),t.querySelector(".select-item").checked=e})),this.updateSelectionUI()}clearSelection(){this.selectAll(!1),this.ui.bulk.select.value=""}updateSelectionUI(){const e=this.selectedItems.size;if(this.ui.bulk.control&&(this.ui.bulk.control.hidden=0===e),this.ui.bulk.count){let t=1===e?"item":"items";this.ui.bulk.count.hidden=0===e,this.ui.bulk.count.textContent=0===e?"":`${e} ${t} selected`}}};
\ No newline at end of file
diff --git a/base/_setup.php b/base/_setup.php
index 83d5657..9855a72 100644
--- a/base/_setup.php
+++ b/base/_setup.php
@@ -2,6 +2,7 @@
 
 require(JVB_DIR.'/base/site.php');
 require(JVB_DIR.'/base/content.php');
+require(JVB_DIR.'/base/email.php');
 require(JVB_DIR.'/base/taxonomies.php');
 require(JVB_DIR.'/base/users.php');
 require(JVB_DIR.'/base/login.php');
diff --git a/base/email.php b/base/email.php
new file mode 100644
index 0000000..81865b2
--- /dev/null
+++ b/base/email.php
@@ -0,0 +1,97 @@
+<?php
+$defaults = [
+	'colours'		=> [
+		'action-0'		=> '#ff0080',
+		'action-50'		=> '#ff2492',
+		'action-100'	=> '#ff47a4',
+		'action-200'	=> '#ff6bb5',
+		'secondary-0'	=> '#D69121',
+		'secondary-50'	=> '#ffc421',
+		'secondary-100'	=> '#ffcd44',
+		'secondary-200'	=> '#ffd768',
+		'light'			=> '#efefef',
+		'light-50'		=> '#e2e2e2',
+		'light-100'		=> '#d5d5d5',
+		'light-200'		=> '#c9c9c9',
+		'dark'			=> '#151515',
+		'dark-50'		=> '#222222',
+		'dark-100'		=> '#2e2e2e',
+		'dark-200'		=> '#3b3b3b',
+		'action-contrast'=> '',
+		'secondary-contrast'=> '',
+	],
+	'content'		=> [
+		'title'				=> get_bloginfo('name'),
+		'subjectPrefix'		=> '['.get_bloginfo('name').']',
+		'signature'			=> '<p>&emsp; —  ♡ the edmonton.ink crew</p>',
+		'footer'	=> [
+			'<p>&copy; ' . date('Y') . ' edmonton.ink — Your tattoo scene on your screen.</p>',
+			'<p><a href="' . get_home_url() . '" class="text-link">edmonton.ink</a></p>'
+		]
+	],
+	'types' => [
+		'newUser' => [
+			'subject'       => 'Welcome to Legacy! Finish creating your account.',
+			'showPrefix'    => true,
+		],
+		'resetPass' => [
+			'subject'   => 'Reset your Password'
+		],
+		'emailChange'   => [
+			'subject'   => 'Successfully Changed Email'
+		],
+		'passwordChange'    => [
+			'subject'   => 'Successfully Changed Password',
+		]
+	]
+];
+
+
+/*
+ * EXTEND WITH:
+ *      {string} $message: email content
+*       {WP_User} $user: user
+ * apply_filters
+ * 		jvbNewUserEmail			-> 	New user email content
+ *          add_filter('jvbNewUserEmail', 'customFunction', 10, 2);
+ * 		jvbNewUserAdminEmail	-> 	New User notification for admin
+ *          add_filter('jvbNewUserAdminEmail', 'customFunction', 10, 2);
+ * 		jvbPasswordResetEmail	->	Password Reset Email
+ *          {string} $message: email content
+ *          {string} $key
+ *          {string} $user_login,
+ *          {WP_User} $user_data
+ *          add_filter('jvbPasswordResetEmail', 'customFunction', 10, 4);
+ * 		jvbEmailChangeRequestEmail		->	Request for email change
+ *          {string} $message
+ *          {array} $oldUser
+ *          {array} newUser
+ *          add_filter('jvbEmailChangeRequestEmail', 'customFunction', 10, 3);
+ * 		jvbEmailChangedEmail	->	Notification that email successfully changed
+ *          {string} $message
+ *          {string} $confirm_url
+            add_filter('jvbEmailChangedEmail', 'customFunction', 10, 2);
+ * 		jvbPasswordChangeEmail	->	Request for password change
+ *          {string} $message
+ *          {array} $oldUser
+ *          {array} $newUser
+ *          add_filter('jvbPasswordChangeEmail', 'customFunction', 10, 3);
+ * 		jvbPersonalDataExport	->	Request for User Data
+ *          {string} $message
+ *          {string} $request_type
+ *          {string} $confirmation_url
+ *          {array} $emailData, from the original filter
+ *          add_filter('jvbPersonalDataExport', 'customFunction', 10, 4);
+ * 		jvbPersonalDataExported	->	Notification that data is ready for download
+ *          {string} $message
+ *          {string} $downloadURL
+ *          {string} $expiresAt
+ *          {array} $emailData, from the original filter
+ *          add_filter('jvbPersonalDataExported', 'customFunction', 10, );
+ *
+ */
+
+
+$jvbEmail = apply_filters('jvb_email', $defaults);
+define('JVB_EMAIL', $jvbEmail);
+
diff --git a/build/feed/render.php b/build/feed/render.php
index 2284965..7af8d29 100644
--- a/build/feed/render.php
+++ b/build/feed/render.php
@@ -195,7 +195,7 @@
                         <div class="filter-actions">
                             <?= jvbRenderToggleTextField('match', 'Match', 'Filters', 'ALL', 'ANY', true) ?>
                             <button type="button" class="clear-filters row" hidden>
-                                <?= jvbIcon('close', ['title'    => 'Clear']) ?>
+                                <?= jvbIcon('x', ['title'    => 'Clear']) ?>
                                 Clear All Filters
                             </button>
                         </div>
@@ -220,7 +220,7 @@
 
                             <input type="radio" id="order-random" class="btn" name="orderby" value="random">
                             <label for="order-random" title="Random Order">
-                                <?= jvbIcon('random') ?>
+                                <?= jvbIcon('shuffle') ?>
                                 <span class="label">Random</span>
                             </label>
                         </div>
@@ -229,12 +229,12 @@
                     <div class="order-direction radio-group-label" data-for-order="date,title">
                         <input type="radio" id="order-desc" class="btn" name="order" value="desc" checked>
                         <label for="order-desc" title="Newest First">
-                            <?= jvbIcon('desc') ?>
+                            <?= jvbIcon('sort-descending') ?>
                         </label>
 
                         <input type="radio" id="order-asc" class="btn" name="order" value="asc">
                         <label for="order-asc" title="Oldest First">
-                            <?= jvbIcon('asc') ?>
+                            <?= jvbIcon('sort-ascending') ?>
                         </label>
                     </div>
                 </div>
diff --git a/build/glossary/view.asset.php b/build/glossary/view.asset.php
index 58bb33c..5ec1386 100644
--- a/build/glossary/view.asset.php
+++ b/build/glossary/view.asset.php
@@ -1 +1 @@
-<?php return array('dependencies' => array(), 'version' => '90747b206e7cdf47035c');
+<?php return array('dependencies' => array(), 'version' => '2f66c620b1540459b263');
diff --git a/build/glossary/view.js b/build/glossary/view.js
index 1b7171f..71ae741 100644
--- a/build/glossary/view.js
+++ b/build/glossary/view.js
@@ -1 +1 @@
-(()=>{class t{constructor(t="dl.glossary",e="nav.glossary-index"){this.glossary=document.querySelector(t),this.nav=document.querySelector(e),this.glossary&&this.nav&&(this.terms=this.glossary.querySelectorAll("dt[id]"),this.navList=this.nav.querySelector("ul"),this.activeClass="active",this.currentActive=null,this.breakpoint=768,this.init(),this.setupResizeHandler())}init(){const t={root:null,rootMargin:this.getRootMargin(),threshold:0};this.observer=new IntersectionObserver((t=>this.handleIntersection(t)),t),this.terms.forEach((t=>this.observer.observe(t))),this.handleScroll=this.debounce((()=>this.checkActiveTerm()),100),window.addEventListener("scroll",this.handleScroll,{passive:!0})}getRootMargin(){if(window.innerWidth<this.breakpoint){const t=parseFloat(getComputedStyle(document.documentElement).fontSize),e=Math.round(5*t);return`-${e}px 0px -${e}px 0px`}return"-50% 0px -50% 0px"}setupResizeHandler(){let t;window.addEventListener("resize",(()=>{clearTimeout(t),t=setTimeout((()=>{this.reinitialize()}),250)}))}reinitialize(){this.observer&&this.observer.disconnect(),this.init()}handleIntersection(t){const e=t.find((t=>t.isIntersecting));e&&this.setActive(e.target)}checkActiveTerm(){const t=4*parseFloat(getComputedStyle(document.documentElement).fontSize);let e=null,i=1/0;this.terms.forEach((s=>{const n=s.getBoundingClientRect();if(window.innerWidth<this.breakpoint?n.top>=t&&n.top<=window.innerHeight-t:n.top+n.height/2>=0&&n.top+n.height/2<=window.innerHeight){const o=window.innerWidth<this.breakpoint?t:window.innerHeight/2,r=Math.abs(n.top-o);r<i&&(i=r,e=s)}})),e&&this.setActive(e)}setActive(t){this.currentActive!==t&&(this.currentActive&&this.currentActive.classList.remove(this.activeClass),t.classList.add(this.activeClass),this.currentActive=t,this.updateNavigation(t.id))}updateNavigation(t){this.nav.querySelectorAll("a").forEach((t=>t.classList.remove(this.activeClass)));const e=this.nav.querySelector(`a[href="#${t}"]`);e&&(e.classList.add(this.activeClass),this.centerNavItem(e))}centerNavItem(t){const e=this.navList.getBoundingClientRect(),i=t.getBoundingClientRect(),s=this.navList.scrollTop,n=i.top-e.top,o=e.height/2-i.height/2;this.navList.scrollTo({top:s+n-o,behavior:"smooth"})}debounce(t,e){let i;return function(...s){clearTimeout(i),i=setTimeout((()=>{clearTimeout(i),t(...s)}),e)}}destroy(){this.observer&&this.observer.disconnect(),window.removeEventListener("scroll",this.handleScroll)}}"loading"===document.readyState?document.addEventListener("DOMContentLoaded",(()=>{new t})):new t})();
\ No newline at end of file
+(()=>{class t{constructor(t="dl.glossary",e="nav.glossary-index"){this.glossary=document.querySelector(t),this.nav=document.querySelector(e),this.glossary&&this.nav&&(this.terms=this.glossary.querySelectorAll("dt[id]"),this.navList=this.nav.querySelector("ul"),this.activeClass="active",this.currentActive=null,this.init(),this.setupResizeHandler())}init(){const t={root:null,rootMargin:this.getRootMargin(),threshold:0};this.observer=new IntersectionObserver((t=>this.handleIntersection(t)),t),this.terms.forEach((t=>this.observer.observe(t))),this.handleScroll=this.debounce((()=>this.checkActiveTerm()),100),window.addEventListener("scroll",this.handleScroll,{passive:!0})}getRootMargin(){return"-50% 0px -50% 0px"}setupResizeHandler(){let t;window.addEventListener("resize",(()=>{clearTimeout(t),t=setTimeout((()=>{this.reinitialize()}),250)}))}reinitialize(){this.observer&&this.observer.disconnect(),this.init()}handleIntersection(t){const e=t.find((t=>t.isIntersecting));e&&this.setActive(e.target)}checkActiveTerm(){parseFloat(getComputedStyle(document.documentElement).fontSize);let t=null,e=1/0;this.terms.forEach((i=>{const s=i.getBoundingClientRect();if(s.top+s.height/2>=0&&s.top+s.height/2<=window.innerHeight){const n=window.innerHeight/2,r=Math.abs(s.top-n);r<e&&(e=r,t=i)}})),t&&this.setActive(t)}setActive(t){this.currentActive!==t&&(this.currentActive&&this.currentActive.classList.remove(this.activeClass),t.classList.add(this.activeClass),this.currentActive=t,this.updateNavigation(t.id))}updateNavigation(t){this.nav.querySelectorAll("a").forEach((t=>t.classList.remove(this.activeClass)));const e=this.nav.querySelector(`a[href="#${t}"]`);e&&(e.classList.add(this.activeClass),this.centerNavItem(e))}centerNavItem(t){const e=this.navList.getBoundingClientRect(),i=t.getBoundingClientRect(),s=this.navList.scrollTop,n=i.top-e.top,r=e.height/2-i.height/2;this.navList.scrollTo({top:s+n-r,behavior:"smooth"})}debounce(t,e){let i;return function(...s){clearTimeout(i),i=setTimeout((()=>{clearTimeout(i),t(...s)}),e)}}destroy(){this.observer&&this.observer.disconnect(),window.removeEventListener("scroll",this.handleScroll)}}"loading"===document.readyState?document.addEventListener("DOMContentLoaded",(()=>{new t})):new t})();
\ No newline at end of file
diff --git a/build/summary/render.php b/build/summary/render.php
index 92863f5..d6a2040 100644
--- a/build/summary/render.php
+++ b/build/summary/render.php
@@ -1,4 +1,7 @@
 <?php
+
+use JVBase\managers\CacheManager;
+
 if (!defined('ABSPATH')) {
     exit; // Exit if accessed directly
 }
@@ -28,7 +31,7 @@
 function jvbRenderArtistSummary():string
 {
     $current = get_queried_object();
-    $cache = new JVBase\managers\CacheManager('artists', WEEK_IN_SECONDS);
+    $cache = CacheManager::for('artists', WEEK_IN_SECONDS);
     $key = 'artist-bio-'.$current->ID;
     $cached = $cache->get($key);
     $cached = false;
@@ -49,11 +52,11 @@
     <nav id="artist" class="on-this-page index">
         <label>Jump to:
             <button type="button" aria-label="Show Index" title="Show Index" class="toggle" aria-expanded="false">
-                <?= jvbIcon('add')?>
+                <?= jvbIcon('plus-square')?>
             </button>
         </label>
         <ul>
-            <li><a href="#top" title="Back to Top"><?=jvbIcon('up')?></a></li>
+            <li><a href="#top" title="Back to Top"><?=jvbIcon('caret-circle-up')?></a></li>
             <li><a href="#about">About</a></li>
             <li><a href="#styles">Styles</a></li>
             <li><a href="#contact">Contact</a></li>
@@ -155,7 +158,7 @@
 {
     $current = get_queried_object();
 
-    $cache = new JVBase\managers\CacheManager('shops', WEEK_IN_SECONDS);
+    $cache = CacheManager::for('shops', WEEK_IN_SECONDS);
     $key = 'shop-bio-'.$current->term_id;
     $cached = $cache->get($key);
     $cached = false;
@@ -181,11 +184,11 @@
     <nav id="shop" class="on-this-page index">
         <label>Jump to:
             <button type="button" aria-label="Show Index" title="Show Index" class="toggle" aria-expanded="false">
-                <?= jvbIcon('add')?>
+                <?= jvbIcon('plus-square')?>
             </button>
         </label>
         <ul>
-            <li><a href="#top" title="Back to Top"><?=jvbIcon('up')?></a></li> <?php
+            <li><a href="#top" title="Back to Top"><?=jvbIcon('caret-circle-up')?></a></li> <?php
             if ($rating !== 'none') {
                 ?>
                 <li><a href="#rating">Rating</a></li>
@@ -286,8 +289,8 @@
 function jvbRenderTermSummary()
 {
     $current = get_queried_object();
-    $cache = new JVBase\managers\CacheManager($current->taxonomy, WEEK_IN_SECONDS);
-    $key = $current->taxonomy.'-'.$current->ID;
+    $cache = CacheManager::for(jvbNoBase($current->taxonomy), WEEK_IN_SECONDS);
+    $key = $current->ID;
     $cached = $cache->get($key);
     $cached = false;
     if ($cached !== false) {
diff --git a/cleanup.php b/cleanup.php
index 87c9092..6444cb4 100644
--- a/cleanup.php
+++ b/cleanup.php
@@ -44,7 +44,9 @@
 	}
 
 	// Remove block-specific scripts and modules
-	wp_deregister_script('heartbeat');
+	if (!is_admin()) {
+		wp_deregister_script('heartbeat');
+	}
 	wp_dequeue_script('wp-block-template-skip-link');
 
 	// Remove WordPress 6.5+ script modules
diff --git a/icons.php b/icons.php
deleted file mode 100644
index 6685f4f..0000000
--- a/icons.php
+++ /dev/null
@@ -1,761 +0,0 @@
-<?php
-namespace JVBase;
-
-use JVBase\managers\CacheManager;
-
-if (!defined('ABSPATH')) {
-    exit; // Exit if accessed directly
-}
-class JVBIcons
-{
-	static array $processing = ['map' => [],'label' => []];
-	protected string $style;
-	protected array $styles = [
-		'regular',
-		'bold',
-		'duotone',
-		'fill',
-		'light',
-		'thin'
-	];
-
-	protected array $labels = [
-		'submenu' 			=> 'Toggle Menu',
-		'dashboard' 		=> 'Dashboard',
-		'arrow-fat-down' 	=> 'Downvote',
-		'arrow-fat-up'		=> 'Upvote',
-		'hamburger'			=> 'Menu',
-		'check-square-offset'=> 'Approval Requests',
-		'h1'				=> 'Heading 1',
-		'h2'				=> 'Heading 2',
-		'h3'				=> 'Heading 3',
-		'note-pencil'		=> 'Notes',
-		'sun-dim'			=> 'Light Mode',
-		'moon'				=> 'Dark Mode',
-		'grid'				=> 'Grid View',
-		'list'				=> 'List View',
-		'clock-clockwise'	=> 'Upcoming Events',
-		'clock-counter-clockwise'=>'Past Events',
-		'repeat'			=> 'Recurring Events',
-		'floppy-disk'		=> 'Save',
-	];
-
-	protected array $map = [
-		'time'			=> 'clock',
-		'back'			=> 'arrow-u-up-left',
-		'logo'			=> 'logo.svg',
-		'logo-basic'	=> 'logo-basic.svg',
-		'save'			=> 'floppy-disk',
-		'restore'		=> 'arrow-counter-clockwise',
-		'help'			=> 'question',
-		'upload'		=> 'cloud-arrow-up',
-		'download'		=> 'cloud-arrow-down',
-		'synced'		=> 'cloud-check',
-		'syncing'		=> 'cloud-sync-thin.svg',
-		'offline'		=> 'cloud-slash',
-		'error'			=> 'cloud-warning',
-		'cart'			=> 'shopping-cart',
-		'delete'		=> 'trash',
-		'edit'			=> 'pencil-simple',
-		'bio'			=> 'user',
-		'login'			=> 'sign-in',
-		'logout'		=> 'sign-out',
-		'future'		=> 'clock-clockwise',
-		'past'			=> 'clock-counter-clockwise',
-		'light'			=> 'sun-dim',
-		'dark'			=> 'moon',
-		'h1'			=> 'text-h-one',
-		'h2'			=> 'text-h-two',
-		'h3'			=> 'text-h-three',
-		'bold'			=> 'text-b',
-		'italic'		=> 'text-italic',
-		'underline'		=> 'text-underline',
-		'strike'		=> 'text-strikethrough',
-		'image'			=> 'image-square',
-		'reply'			=> 'arrow-bend-up-left',
-		'approval'		=> 'check-square-offset',
-		'response'		=> 'chat-teardrop',
-		'karma'			=> 'scales',
-		'show'			=> 'eye',
-		'publish'		=> 'eye',
-		'hide'			=> 'eye-closed',
-		'draft'			=> 'eye-closed',
-		'asc'			=> 'sort-ascending',
-		'desc'			=> 'sort-descending',
-		'all'			=> 'infinity',
-		'random'		=> 'shuffle',
-		'location'		=> 'map-pin',
-		'hours'			=> 'clock',
-		'favourite'		=> 'heart',
-		'email'			=> 'envelope',
-		'text'			=> 'chat',
-		'integrations'	=> 'plugs-connected',
-		'connected'		=> 'plugs-connected',
-		'disconnected'	=> 'plugs',
-		'umami'			=> 'chart-line',
-		'square-up'		=> 'square-logo',
-		'tiktok'		=> 'tiktok-logo',
-		'threads'		=> 'threads-logo',
-		'twitch'		=> 'twitch-logo',
-		'snapchat'		=> 'snapchat-logo',
-		'linktree'		=> 'linktree-logo',
-		'fediverse'		=> 'fediverse-logo',
-		'mastadon'		=> 'mastadon-logo',
-		'youtube'		=> 'youtube-logo',
-		'twitter'		=> 'twitter-logo',
-		'messenger'		=> 'messenger-logo',
-		'facebook'		=> 'facebook-logo',
-		'instagram'		=> 'instagram-logo',
-		'dash'			=> 'door',
-		'event'			=> 'calendar',
-		'events'		=> 'calendar',
-		'settings'		=> 'gear-six',
-		'grid'			=> 'squares-four',
-		'list'			=> 'rows',
-		'pinned'		=> 'push-pin',
-		'search'			=> 'magnifying-glass',
-		'add'			=> 'plus-square',
-		'minus'			=> 'minus-square',
-		'support'		=> 'question',
-		'grab'			=> 'dots-six-vertical',
-		'checkout'		=> 'receipt',
-		'home'			=> 'house',
-		'elbow-right-down' => 'arrow-elbow-right-down',
-		'elbow-right-up'=> 'arrow-elbow-right-up',
-		'elbow-left-down'=> 'arrow-elbow-left-down',
-		'elbow-left-up' => 'arrow-elbow-left-up',
-		'menu'			=> 'list',
-		'submenu'		=> 'caret-down',
-		'close'			=> 'x',
-		'close-square'	=> 'x-square',
-		'dashboard'		=> 'door',
-		'system'		=> 'gear-six',
-		'options'		=> 'gear-six',
-		'news'			=> 'newspaper',
-		'metrics'		=> 'chart-line',
-		'prev'			=> 'caret-left',
-		'up'			=> 'caret-circle-up',
-		'right'			=> 'caret-double-right',
-		'left'			=> 'caret-double-left',
-		'next'			=> 'caret-right',
-		'refresh'		=> 'arrows-clockwise',
-		'pending'		=> 'arrows-clockwise',
-		'copy'			=> 'copy-simple',
-		'align-center'	=> 'text-align-center',
-		'align-left'	=> 'text-align-left',
-		'align-right'	=> 'text-align-right',
-		'alphabetical'	=> 'alphabetical.svg',
-		'date'			=> 'calendar',
-		'down'			=> 'caret-double-down',
-		'tattoo'		=> 'drop-simple',
-		'tattoos'		=> 'drop-simple',
-		'theme'			=> 'folder-open',
-		'arttheme'		=> 'folder-open',
-		'style'			=> 'hash',
-		'artstyle'		=> 'hash',
-		'colour'		=> 'drop',
-		'placement'		=> 'person-arms-spread',
-		'artmedia'		=> 'squares-four',
-		'artist'		=> 'user',
-		'client'		=> 'user',
-		'artists'		=> 'users-three',
-		'partner'		=> 'currency-circle-dollar',
-		'shop'			=> 'storefront',
-		'piercing'		=> 'needle',
-		'piercings'		=> 'needle',
-		'artwork'		=> 'palette',
-		'artform'		=> 'shapes',
-		'city'			=> 'map-pin',
-		'type'			=> 'users-three',
-		'pstyle'		=> 'nut',
-		'project'		=> 'code',
-		'map'			=> 'map-trifold',
-		'offer'			=> 'gift',
-		'referrals'		=> 'hand-heart'
-	];
-
-
-    private const ICON_GROUPS = [
-        'navigation' => [
-            'back'          => ['filename' => 'back', 'label' => 'Back', 'size' => 24],
-            'home'          => ['filename' => 'house', 'label' => 'Home'],
-            'right'         => ['filename' => 'arrow-fat-right', 'label' => ''],
-            'elbow-right-down'=> ['filename' => 'arrow-elbow-right-down', 'label' => ''],
-            'elbow-right-up'=> ['filename' => 'arrow-elbow-right-up', 'label' => ''],
-            'elbow-left-down'=> ['filename' => 'arrow-elbow-left-down', 'label' => ''],
-            'elbow-left-up'=> ['filename' => 'arrow-elbow-left-up', 'label' => ''],
-            'menu'          => ['filename' => 'list', 'label' => 'Toggle Menu', 'size' => 32],
-            'submenu'       => ['filename' => 'caret-down', 'label' => 'Toggle Submenu'],
-            'close'         => ['filename' => 'x', 'label' => 'Close', 'size' => 32],
-            'close-square'  => ['filename' => 'x-square', 'label' => 'Close'],
-            'dashboard'     => ['filename' => 'door', 'label'=> 'Behind the Scenes', 'size' => 24],
-            'dash'     		=> ['filename' => 'door', 'label'=> 'Behind the Scenes', 'size' => 24],
-            'settings'      => ['filename' => 'gear-six', 'label'=> 'Settings', 'size' => 24],
-            'system'      => ['filename' => 'gear-six', 'label'=> 'Settings', 'size' => 24],
-            'options'       => ['filename' => 'gear-six', 'label'=> 'Options', 'size' => 24],
-            'news'          => ['filename' => 'newspaper', 'label' => 'News', 'size' => 24],
-            'metrics'       => ['filename' => 'chart-line', 'label' => 'Metrics', 'size' => 24],
-            'prev'          => ['filename' => 'caret-left', 'label' => 'Previous', 'size' => 24],
-            'up'            => ['filename' => 'caret-circle-up', 'label' => 'Up', 'size' => 24],
-            'down'            => ['filename' => 'caret-double-down', 'label' => 'Down', 'size' => 24],
-            'right'            => ['filename' => 'caret-double-right', 'label' => 'Right', 'size' => 24],
-            'next'          => ['filename' => 'caret-right', 'label' => 'Next', 'size' => 24],
-            'refresh'       => ['filename' => 'arrows-clockwise', 'label' => 'Refresh', 'size' => 24],
-            'pending'       => ['filename' => 'arrows-clockwise', 'label' => 'Refresh', 'size' => 24],
-            'clock'         => ['filename' => 'clock', 'label' => 'Hours'],
-            'copy'          => ['filename' => 'copy-simple', 'label' => 'Duplicate'],
-            'list-heart'    => ['filename' => 'list-heart', 'label'=> 'Lists'],
-            'downvoted'      => ['filename' => 'arrow-fat-down-fill.svg', 'label'=> 'Downvote'],
-            'upvoted'        => ['filename' => 'arrow-fat-up-fill.svg', 'label'=> 'Upvote'],
-            'downvote'      => ['filename' => 'arrow-fat-down', 'label'=> 'Downvote'],
-            'upvote'        => ['filename' => 'arrow-fat-up', 'label'=> 'Upvote'],
-            'karma'         => ['filename' => 'scales', 'label'=> 'Karma'],
-            'response'      => ['filename' => 'chat-teardrop', 'label' => 'Responses'],
-            'reply'         => ['filename' => 'arrow-bend-up-left', 'label' => 'Reply'],
-            'approval'      => ['filename' => 'check-square-offset', 'label' => 'Approval Requests'],
-            'check'         => ['filename' => 'check-circle', 'label' => 'done'],
-            'hamburger'         => ['filename' => 'hamburger', 'label' => 'menu']
-        ],
-        'formatting' => [
-            'paragraph'     => ['filename' => 'paragraph', 'label' => 'Paragraph', 'size' => 24],
-            'h1'            => ['filename' => 'text-h-one', 'label' => 'Heading 1', 'size' => 24],
-            'h2'            => ['filename' => 'text-h-two', 'label' => 'Heading 2', 'size' => 24],
-            'h3'            => ['filename' => 'text-h-three', 'label' => 'Heading 3', 'size' => 24],
-            'bold'          => ['filename' => 'text-b-bold.svg', 'label' => 'Bold', 'size' => 24],
-            'italic'        => ['filename' => 'text-italic', 'label' => 'Italic', 'size' => 24],
-            'underline'     => ['filename' => 'text-underline', 'label' => 'Underline', 'size' => 24],
-            'strike'        => ['filename' => 'text-strikethrough', 'label' => 'Strikethrough', 'size' => 24],
-            'list-bullets'  => ['filename' => 'list-bullets', 'label' => 'Bulleted List', 'size' => 24],
-            'list-numbers'  => ['filename' => 'list-numbers', 'label' => 'Numbered List', 'size' => 24],
-            'image'         => ['filename' => 'image-square', 'label' => 'Image', 'size' => 24],
-            'align-left'    => ['filename' => 'text-align-left', 'label' => 'Align Left', 'size' => 24],
-            'align-right'   => ['filename' => 'text-align-right', 'label' => 'Align Right', 'size' => 24],
-            'align-center'  => ['filename' => 'text-align-center', 'label' => 'Align Center', 'size' => 24],
-            'link'          =>['filename' => 'link', 'label' => 'Link', 'size' => 24],
-            'note'          => ['filename' => 'note-pencil', 'label' => 'Notes', 'size' => 24],
-			'password'		=> ['filename' => 'password', 'label' => 'Password']
-        ],
-        'theme' => [
-            'light'     => ['filename' => 'sun-dim', 'label' => 'Light Mode'],
-            'dark'      => ['filename' => 'moon', 'label' => 'Dark Mode'],
-            'grid'      => ['filename' => 'squares-four', 'label' => 'Grid View'],
-            'list'      => ['filename' => 'rows', 'label' => 'List View'],
-        ],
-        'content'   => [
-            'offer'     => ['filename'=>'gift', 'label'=>'Offer'],
-            'logo' => ['filename' => 'logo.svg', 'label' => 'North\'eh', 'size' => 20],
-            'logo-basic' => ['filename' => 'logo-basic.svg', 'label' => 'North\'eh', 'size' => 20],
-            'theme'     => ['filename' => 'folder-open', 'label' => 'Theme', 'size' => 20],
-            'arttheme'  => ['filename' => 'folder-open', 'label' => 'Art Theme', 'size' => 20],
-            'style'     => ['filename' => 'hash', 'label' => 'Style', 'size' => 20],
-            'artstyle'  => ['filename' => 'hash', 'label' => 'Art Style', 'size' => 20],
-            'colour'    => ['filename' => 'drop', 'label' => 'Colour', 'size' => 20],
-            'placement' => ['filename' => 'person-arms-spread', 'label' => 'Placement', 'size' => 20],
-            'artmedia'  => ['filename' => 'squares-four', 'label'   => 'Media', 'size' => 20],
-            'artist'    => ['filename' => 'user', 'label'=> 'Artist', 'size' => 24],
-            'client'    => ['filename' => 'user', 'label'=> 'Artist', 'size' => 24],
-            'artists'   => ['filename' => 'users-three', 'label' => 'Artists'],
-            'partner'   => ['filename' => 'currency-circle-dollar', 'label' => 'Partner'],
-            'shop'      => ['filename' => 'storefront', 'label' => 'Shop', 'size' => 20],
-            'tattoo'    => ['filename' => 'drop-simple', 'label' => 'Tattoo', 'size' => 20],
-            'tattoos'   => ['filename' => 'drop-simple', 'label' => 'Your Tattoos', 'size' => 20],
-            'event'     => ['filename' => 'calendar', 'label' => 'Event', 'size' => 20],
-            'events'     => ['filename' => 'calendar', 'label' => 'Your Events', 'size' => 20],
-            'piercing'  => ['filename' => 'needle', 'label' => 'Piercings', 'size' => 20],
-            'piercings' => ['filename' => 'needle', 'label' => 'Your Piercings', 'size' => 20],
-            'artwork'   => ['filename' => 'palette', 'label' => 'Artwork', 'size' => 20],
-            'artform'   => ['filename' => 'shapes', 'label' => 'Art Form', 'size' => 20],
-            'city'      => ['filename' => 'map-pin', 'label' => 'City', 'size' => 20],
-            'type'      => ['filename' => 'users-three', 'label' => 'Artist Type', 'size' => 20],
-            'pstyle'    => ['filename' => 'nut', 'label' => 'Body Modifications', 'size' => 20],
-            'past'      => ['filename' => 'clock-counter-clockwise', 'label' => 'Past Events', 'size' => 20],
-            'future'    => ['filename' => 'clock-clockwise', 'label' => 'Upcoming Events', 'size' => 20],
-            'repeat'    => ['filename' => 'repeat', 'label' => 'Recurring Events', 'size' => 20],
-			'project'	=> ['filename' => 'code', 'label' => 'Project'],
-            'gauge' => ['filename' => 'gauge', 'label'=> 'Dashboard'],
-			'map'		=> ['filename' => 'map-trifold', 'label' => 'Map'],
-        ],
-        'users' => [
-            'new-user'  => ['filename' => 'user-circle-plus', 'label' => 'New Artist'],
-            'user'      => ['filename' => 'user', 'label' => 'Artist', 'size' => 20],
-            'bio'       => ['filename' => 'user', 'label' => 'Your Bio', 'size' => 20],
-            'login'     => ['filename' => 'sign-in', 'label' => 'Login'],
-            'logout'    => ['filename' => 'sign-out', 'label' => 'Logout']
-        ],
-        'actions'   => [
-            'save'  => ['filename' => 'floppy-disk', 'label' => 'Save'],
-            'restore'=> ['filename' => 'arrow-counter-clockwise', 'label' => 'Restore', 'size' => 32],
-            'edit'  => ['filename' => 'pencil-simple', 'label' => 'Edit', 'size' => 24],
-            'delete'=> ['filename' => 'trash', 'label' => 'Delete', 'size' => 32],
-            'search'=> ['filename' => 'magnifying-glass', 'label' => 'Search', 'size' => 32],
-            'add'   => ['filename' => 'plus-square', 'label' => 'Add New', 'size' => 32],
-            'minus' => ['filename' => 'minus-square', 'label' => 'Collapse', 'size' => 32],
-            'help'  => ['filename' => 'question', 'label' => 'Toggle Quick Help'],
-            'support'  => ['filename' => 'question', 'label' => 'Toggle Quick Help'],
-            'grab'  => ['filename' => 'dots-six-vertical', 'label'=> 'Grab'],
-            'share' => ['filename' => 'share', 'label' => 'Share', 'size'=> 24],
-            'cart' => ['filename' => 'shopping-cart', 'label' => 'Your Cart', 'size'=> 24],
-            'checkout' => ['filename' => 'receipt', 'label' => 'Checkout', 'size'=> 24],
-            'upload' => ['filename' => 'cloud-arrow-up', 'label' => 'Upload to Server', 'size'=> 24],
-            'download' => ['filename' => 'cloud-arrow-down', 'label' => 'Downloading', 'size'=> 24],
-            'synced' => ['filename' => 'cloud-check', 'label' => 'Synced', 'size'=> 24],
-            'syncing' => ['filename' => 'cloud-sync-thin.svg', 'label' => 'Synced', 'size'=> 24],
-            'cloud' => ['filename' => 'cloud', 'label' => 'Synced', 'size'=> 24],
-//            'pending' => ['filename' => 'cloud', 'label' => 'Synced', 'size'=> 24],
-            'offline' => ['filename' => 'cloud-slash', 'label' => 'Offline', 'size'=> 24],
-            'error' => ['filename' => 'cloud-warning', 'label' => 'Upload Failed', 'size'=> 24],
-
-        ],
-        'status' => [
-            'show'          => ['filename' => 'eye', 'label' => 'Show', 'size' => 20],
-            'publish'       => ['filename' => 'eye', 'label' => 'Public', 'size' => 20],
-            'hide'          => ['filename' => 'eye-closed', 'label' => 'Hide', 'size' => 20],
-            'draft'         => ['filename' => 'eye-closed', 'label' => 'Hidden', 'size' => 20],
-            'pinned'        => ['filename' => 'push-pin-simple', 'label' => 'Pin', 'size' => 32],
-            'bell'          => ['filename' => 'bell', 'label' => 'Notification', 'size' => 32],
-            'bell-ringing'  => ['filename' => 'bell-ringing', 'label' => 'Notification', 'size' => 32]
-        ],
-        'sorting' => [
-            'table' => ['filename' => 'table', 'label' => 'Table View', 'size' => 20],
-            'columns' => ['filename' => 'columns', 'label' => 'Show Columns', 'size' => 20],
-            'alphabetical' => ['filename' => 'alphabetical', 'label' => 'Alphabetical', 'size' => 20],
-            'calendar' => ['filename' => 'calendar-heart', 'label' => 'Date', 'size' => 20],
-            'asc' => ['filename' => 'sort-ascending', 'label' => 'Sort Ascending', 'size' => 20],
-            'ASC' => ['filename' => 'sort-ascending', 'label' => 'Sort Ascending', 'size' => 20],
-            'desc' => ['filename' => 'sort-descending', 'label' => 'Sort Descending', 'size' => 20],
-            'DESC' => ['filename' => 'sort-descending', 'label' => 'Sort Descending', 'size' => 20],
-            'filter' => ['filename' => 'faders', 'label' => 'Filter'],
-            'all' => ['filename' => 'infinity', 'label' => 'All', 'size' => 32],
-            'random' => ['filename' => 'shuffle', 'label' => 'Random', 'size' => 20]
-        ],
-        'business' => [
-            'location' => ['filename' => 'map-pin', 'label' => 'Location'],
-            'hours' => ['filename' => 'clock', 'label' => 'Hours'],
-            'star'  => ['filename' => 'star', 'label' => 'Empty Star'],
-            'star-fill'  => ['filename' => 'star-fill.svg', 'label' => 'Star'],
-            'star-half'  => ['filename' => 'star-half-fill.svg', 'label' => 'Half Star'],
-        ],
-        'social' => [
-            'heart'     => ['filename' => 'heart', 'label' => 'Favourite', 'size' => 24],
-            'favourite'     => ['filename' => 'heart', 'label' => 'Favourite', 'size' => 24],
-            'favourites'=> ['filename' => 'heart', 'label' => 'Your Favourites', 'size' => 24],
-            'heart-fill'=> ['filename' => 'heart-fill.svg', 'label' => 'Favourited', 'size' => 24],
-            'share'     => ['filename' => 'share', 'label' => 'Share', 'size' => 32],
-            'email'     => ['filename' => 'envelope','label'=> 'Email'],
-            'text'      => ['filename' => 'chat','label'=> 'Text'],
-            'phone'     => ['filename' => 'phone','label'=> 'Call'],
-        ],
-        'external'   => [
-			'integrations' => ['filename' => 'plugs-connected', 'label' => 'Integrations'],
-			'connected' => ['filename' => 'plugs-connected', 'label' => 'Connected'],
-			'disconnected' => ['filename' => 'plugs', 'label' => 'Disconnected'],
-            'instagram' => ['filename' => 'instagram-logo', 'label' => 'Instagram'],
-            'facebook'  => ['filename' => 'facebook-logo', 'label' => 'Facebook'],
-            'messenger' => ['filename' => 'messenger-logo', 'label' => 'Facebook Messenger'],
-            'twitter'   => ['filename' => 'twitter-logo', 'label' => 'Twitter'],
-            'x'   => ['filename' => 'x-logo', 'label' => 'X'],
-            'youtube'   => ['filename' => 'youtube-logo', 'label' => 'YouTube'],
-            'mastadon'   => ['filename' => 'mastadon-logo', 'label' => 'Mastadon'],
-            'fediverse'   => ['filename' => 'fediverse-logo', 'label' => 'Fediverse'],
-            'linktree'   => ['filename' => 'linktree-logo', 'label' => 'LinkTree'],
-            'snapchat'   => ['filename' => 'snapchat-logo', 'label' => 'Snapchat'],
-            'twitch'   => ['filename' => 'twitch-logo', 'label' => 'Twitch'],
-            'threads'   => ['filename' => 'threads-logo', 'label' => 'Threads'],
-            'tiktok'   => ['filename' => 'tiktok-logo', 'label' => 'TikTok'],
-            'square-up'   => ['filename' => 'square-logo', 'label' => 'Square'],
-            'umami'   => ['filename' => 'chart-line', 'label' => 'Umami'],
-        ]
-    ];
-    private string $defaultStyle = '-thin';
-    private int $defaultSize = 16;
-    protected string $name;
-	protected array $used;
-	protected int $size = 20;
-	protected CacheManager $cache;
-
-    public function __construct()
-    {
-		$this->cache = CacheManager::for('icons', WEEK_IN_SECONDS);
-//		$this->cache->invalidateGroup('icons');
-		$this->style = JVB_SITE['icons']??'regular';
-
-
-		$this->used = get_option(BASE.'used_icons', [
-			$this->style => [
-				'heart',
-				'hours',
-				'random',
-				'alphabetical',
-				'calendar',
-				'asc',
-				'desc',
-				'all',
-				'paragraph',
-				'h1',
-				'h2',
-				'h3',
-				'bold',
-				'italic',
-				'underline',
-				'strike',
-				'list-bullets',
-				'list-numbers',
-				'image',
-				'align-left',
-				'align-right',
-				'align-center',
-				'link',
-				'note',
-				'password',
-				'light',
-				'left',
-				'dark',
-				'grid',
-				'list',
-				'save',
-				'restore',
-				'edit',
-				'delete',
-				'search',
-				'add',
-				'minus',
-				'help',
-				'support',
-				'grab',
-				'share',
-				'cart',
-				'checkout',
-				'upload',
-				'download',
-				'synced',
-				'syncing',
-				'cloud',
-				'offline',
-				'error',
-				'show',
-				'publish',
-				'hide',
-				'draft',
-				'pinned',
-				'bell',
-				'bell-ringing',
-				'back',
-				'home',
-				'right',
-				'elbow-right-down',
-				'elbow-right-up',
-				'elbow-left-down',
-				'elbow-left-up',
-				'menu',
-				'submenu',
-				'close',
-				'close-square',
-				'dashboard',
-				'dash',
-				'settings',
-				'system',
-				'options',
-				'news',
-				'metrics',
-				'prev',
-				'up',
-				'down',
-				'right',
-				'copy',
-				'next',
-				'refresh',
-				'pending',
-				'clock',
-				'copy',
-				'list-heart',
-				'karma',
-				'response',
-				'reply',
-				'approval',
-				'check',
-				'hamburger',
-				'location',
-				'hours',
-				'star',
-				'star-half',
-				'exclamation-mark'
-			],
-			'fill'	=> [
-				'heart',
-				'arrow-fat-down',
-				'arrow-fat-up',
-				'star'
-			]
-		]);
-    }
-
-	protected function updateUsed():void
-	{
-		$icons = [];
-		foreach ($this->used as $style => $items) {
-			$temp = array_unique($items);
-			sort($temp);
-			$icons[$style] = $temp;
-		}
-		update_option(BASE . 'used_icons', $icons);
-	}
-
-	protected function checkMap(string $name): string
-	{
-		$result = apply_filters('jvbIconMap', (array_key_exists($name, $this->map)) ? $this->map[$name] : $name, $name);
-		return $result;
-	}
-
-	protected function checkName(string $name) {
-		$name = $this->checkMap($name);
-		$path = (str_contains($name, '.svg')) ? '/assets/icons/' : '/assets/phosphor-icons/regular/';
-		$filename = (str_contains($name, '.svg')) ? $name : $name.'.svg';
-		return file_exists(JVB_DIR . $path . $filename);
-	}
-    public function getIcon(string $name, array $options = []):?string
-    {
-		if (!$this->checkName($name)) {
-			error_log('[Icons]Icon not found for: '.print_r($name, true));
-			return '';
-		}
-		$style = (array_key_exists('style', $options) && in_array($options['style'], $this->styles)) ? $options['style'] : 'regular';
-
-		$update = false;
-		if (!array_key_exists($style, $this->used)) {
-			$this->used[$style] = [];
-			$update = true;
-		}
-		if (!in_array($name, $this->used[$style])) {
-			$this->used[$style][] = $name;
-			$update = true;
-		}
-		if ($update) {
-			$this->updateUsed();
-		}
-
-		$name = $this->checkMap($name);
-        $this->name = str_replace('.svg', '', $name);
-
-
-        // Merge options with defaults and icon config
-        $options = array_merge([
-            'title' => $options['label']??$this->getIconLabel($name),
-            'size' => $options['size'] ?? $this->size,
-            'style' => $style,
-            'class' => '',
-            'wrap'  => true,
-            'color' => 'currentColor'
-        ], $options);
-
-		return $this->cache->remember(
-			array_merge($options, ['name' => $name]),
-			function () use ($name, $options) {
-				return $this->buildIcon($name, $options);
-			}
-		);
-    }
-
-    public function getIconsByGroup(string $group):array
-    {
-        if (!isset(self::ICON_GROUPS[$group])) {
-            return [];
-        }
-
-        $icons = [];
-        foreach (self::ICON_GROUPS[$group] as $name => $config) {
-            $icons[$name] = $this->getIcon($name);
-        }
-
-        return array_filter($icons);
-    }
-
-    public function getAllIcons():array
-    {
-        $icons = [];
-        foreach (self::ICON_GROUPS as $group => $groupIcons) {
-            foreach ($groupIcons as $name => $config) {
-                $icons[$name] = $this->getIcon($name);
-            }
-        }
-
-        return array_filter($icons);
-    }
-
-    public function getGroups():array
-    {
-        return array_keys(self::ICON_GROUPS);
-    }
-
-    private function findIconConfig(string $name):?array
-    {
-        foreach (self::ICON_GROUPS as $groupIcons) {
-            if (isset($groupIcons[$name])) {
-                return $groupIcons[$name];
-            }
-        }
-        return null;
-    }
-
-    private function buildIcon(string $name, array $options):?string
-    {
-
-        $filepath = $this->buildFilePath($name, $options['style']);
-        if (!file_exists($filepath)) {
-            error_log("Icon file not found: $filepath");
-            return null;
-        }
-
-        $svg = file_get_contents($filepath);
-        if ($svg === false) {
-            return null;
-        }
-
-        return $this->formatSvg($svg, $options);
-    }
-
-    private function buildFilePath(string $filename, string $style = ''):string
-    {
-		$svg = false;
-		if (str_contains($filename, '.svg')) {
-			$svg = true;
-			$nameExtra = '';
-			$path = '/assets/icons/';
-		} else {
-			$nameExtra = ($style === 'regular') ? '' : '-'.$style;
-			$path = '/assets/phosphor-icons/';
-		}
-
-        $filename = (str_contains($filename, '.svg')) ? $filename : $filename . $nameExtra . '.svg';
-		$style = ($style === '') ? '' : $style .'/';
-		$style = ($svg) ? '' : $style;
-        return JVB_DIR . $path . $style . $filename;
-    }
-
-	private function formatSvg(string $svg, array $options): string
-	{
-		// Clean up SVG
-		$svg = preg_replace("/([\n\t]+)/", ' ', $svg);
-		$svg = preg_replace('/>\s*</', '><', $svg);
-
-		// Add size attributes - FIXED: assign results back to $svg
-		if ($options['size'] !== 32) {
-			$svg = str_replace('width="32"', 'width="' . (int)$options['size'] . '"', $svg);
-			$svg = str_replace('height="32"', 'height="' . (int)$options['size'] . '"', $svg);
-		}
-
-		// Add color if provided - FIXED: assign results back to $svg
-		if ($options['color'] !== 'currentColor') {
-			$svg = str_replace('currentColor', $options['color'], $svg);
-		}
-
-		// Add title if provided
-		if (!empty($options['title'])) {
-			$svg = str_replace('<svg', '<svg title="' . esc_attr($options['title']) . '"', $svg);
-			$svg = str_replace('aria-hidden="true"', '', $svg);
-		}
-
-		$classes = trim('icon row ' . $this->name . ' ' . $options['class']);
-		if (array_key_exists('wrap', $options) && $options['wrap'] === false) {
-			return $svg;
-		}
-		return sprintf(
-			'<i class="%s">%s</i>',
-			esc_attr($classes),
-			$svg
-		);
-	}
-
-	/****************************
-	 * Converts to CSS icon
-	 ***************************/
-	public function getCSSIcon(string $name, array $options = []): ?string
-	{
-		if (!$this->checkName($name)) {
-			return '';
-		}
-		$name = $this->checkMap($name);
-		$style = (array_key_exists('style', $options) && in_array($options['style'], $this->styles)) ? $options['style'] : 'regular';
-
-		$this->name = str_replace('.svg', '', $name);
-
-		// Merge options with defaults and icon config
-		$options = array_merge([
-			'title' => $options['label']??$this->getIconLabel($name),
-			'size' => $options['size'] ?? $this->size,
-			'style' => $style,
-			'class' => '',
-			'wrap'  => true,
-			'color' => 'currentColor'
-		], $options);
-
-		$svg = $this->cache->remember(
-			array_merge($options, ['name' => $name, 'css' => true]),
-			function() use ($name, $options) {
-				return $this->buildRawSvg($name, $options);
-			}
-		);
-		// Convert to base64 data URI
-		$svg = ($svg) ? 'data:image/svg+xml;base64,' . base64_encode($svg) : null;
-		return $svg;
-	}
-
-	private function buildRawSvg(string $filename, array $options): ?string
-	{
-		$filepath = $this->buildFilePath($filename, $options['style']);
-		if (!file_exists($filepath)) {
-			error_log("Icon file not found: $filepath");
-			return null;
-		}
-
-		$svg = file_get_contents($filepath);
-		if ($svg === false) {
-			return null;
-		}
-
-		return $this->formatRawSvg($svg, $options);
-	}
-
-	private function formatRawSvg(string $svg, array $options): string
-	{
-		// Clean up SVG
-		$svg = preg_replace("/([\n\t]+)/", ' ', $svg);
-		$svg = preg_replace('/>\s*</', '><', $svg);
-
-		// Add size attributes - FIXED: assign results back to $svg
-		if ($options['size'] > 0) {
-			$svg = str_replace('width="32"', 'width="' . (int)$options['size'] . '"', $svg);
-			$svg = str_replace('height="32"', 'height="' . (int)$options['size'] . '"', $svg);
-		}
-
-		// Add color if provided and not currentColor - FIXED: assign results back to $svg
-		if ($options['color'] !== 'currentColor') {
-			$svg = str_replace('currentColor', $options['color'], $svg);
-		}
-
-		return $svg;
-	}
-
-	public function localizeIcons():array
-	{
-		if (empty($this->used)) {
-			return [];
-		}
-		$used = [];
-		foreach ($this->used as $style => $icons) {
-			foreach ($icons as $icon) {
-				$used[$icon] = $this->getIcon($icon, ['style' => $style]);
-			}
-		}
-		return $used;
-	}
-
-	public function getIconLabel(string $name): string
-	{
-		$name = str_replace('.svg', '', $name);
-		$label = (array_key_exists($name, $this->labels)) ? $this->labels[$name] : '';
-
-		$result = apply_filters('jvbIconLabel', $label, $name);
-
-		return $result;
-	}
-}
diff --git a/iconsOld.php b/iconsOld.php
new file mode 100644
index 0000000..860838a
--- /dev/null
+++ b/iconsOld.php
@@ -0,0 +1,761 @@
+<?php
+namespace JVBase;
+
+use JVBase\managers\CacheManager;
+
+if (!defined('ABSPATH')) {
+	exit; // Exit if accessed directly
+}
+class JVBIcons
+{
+	static array $processing = ['map' => [],'label' => []];
+	protected string $style;
+	protected array $styles = [
+		'regular',
+		'bold',
+		'duotone',
+		'fill',
+		'light',
+		'thin'
+	];
+
+	protected array $labels = [
+		'submenu' 			=> 'Toggle Menu',
+		'dashboard' 		=> 'Dashboard',
+		'arrow-fat-down' 	=> 'Downvote',
+		'arrow-fat-up'		=> 'Upvote',
+		'hamburger'			=> 'Menu',
+		'check-square-offset'=> 'Approval Requests',
+		'h1'				=> 'Heading 1',
+		'h2'				=> 'Heading 2',
+		'h3'				=> 'Heading 3',
+		'note-pencil'		=> 'Notes',
+		'sun-dim'			=> 'Light Mode',
+		'moon'				=> 'Dark Mode',
+		'grid'				=> 'Grid View',
+		'list'				=> 'List View',
+		'clock-clockwise'	=> 'Upcoming Events',
+		'clock-counter-clockwise'=>'Past Events',
+		'repeat'			=> 'Recurring Events',
+		'floppy-disk'		=> 'Save',
+	];
+
+	protected array $map = [
+		'time'			=> 'clock',
+		'back'			=> 'arrow-u-up-left',
+		'logo'			=> 'logo.svg',
+		'logo-basic'	=> 'logo-basic.svg',
+		'save'			=> 'floppy-disk',
+		'restore'		=> 'arrow-counter-clockwise',
+		'help'			=> 'question',
+		'upload'		=> 'cloud-arrow-up',
+		'download'		=> 'cloud-arrow-down',
+		'synced'		=> 'cloud-check',
+		'syncing'		=> 'cloud-sync-thin.svg',
+		'offline'		=> 'cloud-slash',
+		'error'			=> 'cloud-warning',
+		'cart'			=> 'shopping-cart',
+		'delete'		=> 'trash',
+		'edit'			=> 'pencil-simple',
+		'bio'			=> 'user',
+		'login'			=> 'sign-in',
+		'logout'		=> 'sign-out',
+		'future'		=> 'clock-clockwise',
+		'past'			=> 'clock-counter-clockwise',
+		'light'			=> 'sun-dim',
+		'dark'			=> 'moon',
+		'h1'			=> 'text-h-one',
+		'h2'			=> 'text-h-two',
+		'h3'			=> 'text-h-three',
+		'bold'			=> 'text-b',
+		'italic'		=> 'text-italic',
+		'underline'		=> 'text-underline',
+		'strike'		=> 'text-strikethrough',
+		'image'			=> 'image-square',
+		'reply'			=> 'arrow-bend-up-left',
+		'approval'		=> 'check-square-offset',
+		'response'		=> 'chat-teardrop',
+		'karma'			=> 'scales',
+		'show'			=> 'eye',
+		'publish'		=> 'eye',
+		'hide'			=> 'eye-closed',
+		'draft'			=> 'eye-closed',
+		'asc'			=> 'sort-ascending',
+		'desc'			=> 'sort-descending',
+		'all'			=> 'infinity',
+		'random'		=> 'shuffle',
+		'location'		=> 'map-pin',
+		'hours'			=> 'clock',
+		'favourite'		=> 'heart',
+		'email'			=> 'envelope',
+		'text'			=> 'chat',
+		'integrations'	=> 'plugs-connected',
+		'connected'		=> 'plugs-connected',
+		'disconnected'	=> 'plugs',
+		'umami'			=> 'chart-line',
+		'square-up'		=> 'square-logo',
+		'tiktok'		=> 'tiktok-logo',
+		'threads'		=> 'threads-logo',
+		'twitch'		=> 'twitch-logo',
+		'u'		=> 'snapchat-logo',
+		'linktree'		=> 'linktree-logo',
+		'fediverse'		=> 'fediverse-logo',
+		'mastadon'		=> 'mastadon-logo',
+		'youtube'		=> 'youtube-logo',
+		'twitter'		=> 'twitter-logo',
+		'messenger'		=> 'messenger-logo',
+		'facebook'		=> 'facebook-logo',
+		'instagram'		=> 'instagram-logo',
+		'dash'			=> 'door',
+		'event'			=> 'calendar',
+		'events'		=> 'calendar',
+		'settings'		=> 'gear-six',
+		'grid'			=> 'squares-four',
+		'list'			=> 'rows',
+		'pinned'		=> 'push-pin',
+		'search'			=> 'magnifying-glass',
+		'add'			=> 'plus-square',
+		'minus'			=> 'minus-square',
+		'support'		=> 'question',
+		'grab'			=> 'dots-six-vertical',
+		'checkout'		=> 'receipt',
+		'home'			=> 'house',
+		'elbow-right-down' => 'arrow-elbow-right-down',
+		'elbow-right-up'=> 'arrow-elbow-right-up',
+		'elbow-left-down'=> 'arrow-elbow-left-down',
+		'elbow-left-up' => 'arrow-elbow-left-up',
+		'menu'			=> 'list',
+		'submenu'		=> 'caret-down',
+		'close'			=> 'x',
+		'close-square'	=> 'x-square',
+		'dashboard'		=> 'door',
+		'system'		=> 'gear-six',
+		'options'		=> 'gear-six',
+		'news'			=> 'newspaper',
+		'metrics'		=> 'chart-line',
+		'prev'			=> 'caret-left',
+		'up'			=> 'caret-circle-up',
+		'right'			=> 'caret-double-right',
+		'left'			=> 'caret-double-left',
+		'next'			=> 'caret-right',
+		'refresh'		=> 'arrows-clockwise',
+		'pending'		=> 'arrows-clockwise',
+		'copy'			=> 'copy-simple',
+		'align-center'	=> 'text-align-center',
+		'align-left'	=> 'text-align-left',
+		'align-right'	=> 'text-align-right',
+	//	'alphabetical'	=> 'alphabetical.svg',
+		'date'			=> 'calendar',
+		'down'			=> 'caret-double-down',
+		'tattoo'		=> 'drop-simple',
+		'tattoos'		=> 'drop-simple',
+		'theme'			=> 'folder-open',
+		'arttheme'		=> 'folder-open',
+		'style'			=> 'hash',
+		'artstyle'		=> 'hash',
+		'colour'		=> 'drop',
+		'placement'		=> 'person-arms-spread',
+		'artmedia'		=> 'squares-four',
+		'artist'		=> 'user',
+		'client'		=> 'user',
+		'artists'		=> 'users-three',
+		'partner'		=> 'currency-circle-dollar',
+		'shop'			=> 'storefront',
+		'piercing'		=> 'needle',
+		'piercings'		=> 'needle',
+		'artwork'		=> 'palette',
+		'artform'		=> 'shapes',
+		'city'			=> 'map-pin',
+		'type'			=> 'users-three',
+		'pstyle'		=> 'nut',
+		'project'		=> 'code',
+		'map'			=> 'map-trifold',
+		'offer'			=> 'gift',
+		'referrals'		=> 'hand-heart'
+	];
+
+
+	private const ICON_GROUPS = [
+		'navigation' => [
+			'back'          => ['filename' => 'back', 'label' => 'Back', 'size' => 24],
+			'home'          => ['filename' => 'house', 'label' => 'Home'],
+			'right'         => ['filename' => 'arrow-fat-right', 'label' => ''],
+			'elbow-right-down'=> ['filename' => 'arrow-elbow-right-down', 'label' => ''],
+			'elbow-right-up'=> ['filename' => 'arrow-elbow-right-up', 'label' => ''],
+			'elbow-left-down'=> ['filename' => 'arrow-elbow-left-down', 'label' => ''],
+			'elbow-left-up'=> ['filename' => 'arrow-elbow-left-up', 'label' => ''],
+			'menu'          => ['filename' => 'list', 'label' => 'Toggle Menu', 'size' => 32],
+			'submenu'       => ['filename' => 'caret-down', 'label' => 'Toggle Submenu'],
+			'close'         => ['filename' => 'x', 'label' => 'Close', 'size' => 32],
+			'close-square'  => ['filename' => 'x-square', 'label' => 'Close'],
+			'dashboard'     => ['filename' => 'door', 'label'=> 'Behind the Scenes', 'size' => 24],
+			'dash'     		=> ['filename' => 'door', 'label'=> 'Behind the Scenes', 'size' => 24],
+			'settings'      => ['filename' => 'gear-six', 'label'=> 'Settings', 'size' => 24],
+			'system'      => ['filename' => 'gear-six', 'label'=> 'Settings', 'size' => 24],
+			'options'       => ['filename' => 'gear-six', 'label'=> 'Options', 'size' => 24],
+			'news'          => ['filename' => 'newspaper', 'label' => 'News', 'size' => 24],
+			'metrics'       => ['filename' => 'chart-line', 'label' => 'Metrics', 'size' => 24],
+			'prev'          => ['filename' => 'caret-left', 'label' => 'Previous', 'size' => 24],
+			'up'            => ['filename' => 'caret-circle-up', 'label' => 'Up', 'size' => 24],
+			'down'            => ['filename' => 'caret-double-down', 'label' => 'Down', 'size' => 24],
+			'right'            => ['filename' => 'caret-double-right', 'label' => 'Right', 'size' => 24],
+			'next'          => ['filename' => 'caret-right', 'label' => 'Next', 'size' => 24],
+			'refresh'       => ['filename' => 'arrows-clockwise', 'label' => 'Refresh', 'size' => 24],
+			'pending'       => ['filename' => 'arrows-clockwise', 'label' => 'Refresh', 'size' => 24],
+			'clock'         => ['filename' => 'clock', 'label' => 'Hours'],
+			'copy'          => ['filename' => 'copy-simple', 'label' => 'Duplicate'],
+			'list-heart'    => ['filename' => 'list-heart', 'label'=> 'Lists'],
+			'downvoted'      => ['filename' => 'arrow-fat-down-fill.svg', 'label'=> 'Downvote'],
+			'upvoted'        => ['filename' => 'arrow-fat-up-fill.svg', 'label'=> 'Upvote'],
+			'downvote'      => ['filename' => 'arrow-fat-down', 'label'=> 'Downvote'],
+			'upvote'        => ['filename' => 'arrow-fat-up', 'label'=> 'Upvote'],
+			'karma'         => ['filename' => 'scales', 'label'=> 'Karma'],
+			'response'      => ['filename' => 'chat-teardrop', 'label' => 'Responses'],
+			'reply'         => ['filename' => 'arrow-bend-up-left', 'label' => 'Reply'],
+			'approval'      => ['filename' => 'check-square-offset', 'label' => 'Approval Requests'],
+			'check'         => ['filename' => 'check-circle', 'label' => 'done'],
+			'hamburger'         => ['filename' => 'hamburger', 'label' => 'menu']
+		],
+		'formatting' => [
+			'paragraph'     => ['filename' => 'paragraph', 'label' => 'Paragraph', 'size' => 24],
+			'h1'            => ['filename' => 'text-h-one', 'label' => 'Heading 1', 'size' => 24],
+			'h2'            => ['filename' => 'text-h-two', 'label' => 'Heading 2', 'size' => 24],
+			'h3'            => ['filename' => 'text-h-three', 'label' => 'Heading 3', 'size' => 24],
+			'bold'          => ['filename' => 'text-b-bold.svg', 'label' => 'Bold', 'size' => 24],
+			'italic'        => ['filename' => 'text-italic', 'label' => 'Italic', 'size' => 24],
+			'underline'     => ['filename' => 'text-underline', 'label' => 'Underline', 'size' => 24],
+			'strike'        => ['filename' => 'text-strikethrough', 'label' => 'Strikethrough', 'size' => 24],
+			'list-bullets'  => ['filename' => 'list-bullets', 'label' => 'Bulleted List', 'size' => 24],
+			'list-numbers'  => ['filename' => 'list-numbers', 'label' => 'Numbered List', 'size' => 24],
+			'image'         => ['filename' => 'image-square', 'label' => 'Image', 'size' => 24],
+			'align-left'    => ['filename' => 'text-align-left', 'label' => 'Align Left', 'size' => 24],
+			'align-right'   => ['filename' => 'text-align-right', 'label' => 'Align Right', 'size' => 24],
+			'align-center'  => ['filename' => 'text-align-center', 'label' => 'Align Center', 'size' => 24],
+			'link'          =>['filename' => 'link', 'label' => 'Link', 'size' => 24],
+			'note'          => ['filename' => 'note-pencil', 'label' => 'Notes', 'size' => 24],
+			'password'		=> ['filename' => 'password', 'label' => 'Password']
+		],
+		'theme' => [
+			'light'     => ['filename' => 'sun-dim', 'label' => 'Light Mode'],
+			'dark'      => ['filename' => 'moon', 'label' => 'Dark Mode'],
+			'grid'      => ['filename' => 'squares-four', 'label' => 'Grid View'],
+			'list'      => ['filename' => 'rows', 'label' => 'List View'],
+		],
+		'content'   => [
+			'offer'     => ['filename'=>'gift', 'label'=>'Offer'],
+			'logo' => ['filename' => 'logo.svg', 'label' => 'North\'eh', 'size' => 20],
+			'logo-basic' => ['filename' => 'logo-basic.svg', 'label' => 'North\'eh', 'size' => 20],
+			'theme'     => ['filename' => 'folder-open', 'label' => 'Theme', 'size' => 20],
+			'arttheme'  => ['filename' => 'folder-open', 'label' => 'Art Theme', 'size' => 20],
+			'style'     => ['filename' => 'hash', 'label' => 'Style', 'size' => 20],
+			'artstyle'  => ['filename' => 'hash', 'label' => 'Art Style', 'size' => 20],
+			'colour'    => ['filename' => 'drop', 'label' => 'Colour', 'size' => 20],
+			'placement' => ['filename' => 'person-arms-spread', 'label' => 'Placement', 'size' => 20],
+			'artmedia'  => ['filename' => 'squares-four', 'label'   => 'Media', 'size' => 20],
+			'artist'    => ['filename' => 'user', 'label'=> 'Artist', 'size' => 24],
+			'client'    => ['filename' => 'user', 'label'=> 'Artist', 'size' => 24],
+			'artists'   => ['filename' => 'users-three', 'label' => 'Artists'],
+			'partner'   => ['filename' => 'currency-circle-dollar', 'label' => 'Partner'],
+			'shop'      => ['filename' => 'storefront', 'label' => 'Shop', 'size' => 20],
+			'tattoo'    => ['filename' => 'drop-simple', 'label' => 'Tattoo', 'size' => 20],
+			'tattoos'   => ['filename' => 'drop-simple', 'label' => 'Your Tattoos', 'size' => 20],
+			'event'     => ['filename' => 'calendar', 'label' => 'Event', 'size' => 20],
+			'events'     => ['filename' => 'calendar', 'label' => 'Your Events', 'size' => 20],
+			'piercing'  => ['filename' => 'needle', 'label' => 'Piercings', 'size' => 20],
+			'piercings' => ['filename' => 'needle', 'label' => 'Your Piercings', 'size' => 20],
+			'artwork'   => ['filename' => 'palette', 'label' => 'Artwork', 'size' => 20],
+			'artform'   => ['filename' => 'shapes', 'label' => 'Art Form', 'size' => 20],
+			'city'      => ['filename' => 'map-pin', 'label' => 'City', 'size' => 20],
+			'type'      => ['filename' => 'users-three', 'label' => 'Artist Type', 'size' => 20],
+			'pstyle'    => ['filename' => 'nut', 'label' => 'Body Modifications', 'size' => 20],
+			'past'      => ['filename' => 'clock-counter-clockwise', 'label' => 'Past Events', 'size' => 20],
+			'future'    => ['filename' => 'clock-clockwise', 'label' => 'Upcoming Events', 'size' => 20],
+			'repeat'    => ['filename' => 'repeat', 'label' => 'Recurring Events', 'size' => 20],
+			'project'	=> ['filename' => 'code', 'label' => 'Project'],
+			'gauge' => ['filename' => 'gauge', 'label'=> 'Dashboard'],
+			'map'		=> ['filename' => 'map-trifold', 'label' => 'Map'],
+		],
+		'users' => [
+			'new-user'  => ['filename' => 'user-circle-plus', 'label' => 'New Artist'],
+			'user'      => ['filename' => 'user', 'label' => 'Artist', 'size' => 20],
+			'bio'       => ['filename' => 'user', 'label' => 'Your Bio', 'size' => 20],
+			'login'     => ['filename' => 'sign-in', 'label' => 'Login'],
+			'logout'    => ['filename' => 'sign-out', 'label' => 'Logout']
+		],
+		'actions'   => [
+			'save'  => ['filename' => 'floppy-disk', 'label' => 'Save'],
+			'restore'=> ['filename' => 'arrow-counter-clockwise', 'label' => 'Restore', 'size' => 32],
+			'edit'  => ['filename' => 'pencil-simple', 'label' => 'Edit', 'size' => 24],
+			'delete'=> ['filename' => 'trash', 'label' => 'Delete', 'size' => 32],
+			'search'=> ['filename' => 'magnifying-glass', 'label' => 'Search', 'size' => 32],
+			'add'   => ['filename' => 'plus-square', 'label' => 'Add New', 'size' => 32],
+			'minus' => ['filename' => 'minus-square', 'label' => 'Collapse', 'size' => 32],
+			'help'  => ['filename' => 'question', 'label' => 'Toggle Quick Help'],
+			'support'  => ['filename' => 'question', 'label' => 'Toggle Quick Help'],
+			'grab'  => ['filename' => 'dots-six-vertical', 'label'=> 'Grab'],
+			'share' => ['filename' => 'share', 'label' => 'Share', 'size'=> 24],
+			'cart' => ['filename' => 'shopping-cart', 'label' => 'Your Cart', 'size'=> 24],
+			'checkout' => ['filename' => 'receipt', 'label' => 'Checkout', 'size'=> 24],
+			'upload' => ['filename' => 'cloud-arrow-up', 'label' => 'Upload to Server', 'size'=> 24],
+			'download' => ['filename' => 'cloud-arrow-down', 'label' => 'Downloading', 'size'=> 24],
+			'synced' => ['filename' => 'cloud-check', 'label' => 'Synced', 'size'=> 24],
+			'syncing' => ['filename' => 'cloud-sync-thin.svg', 'label' => 'Synced', 'size'=> 24],
+			'cloud' => ['filename' => 'cloud', 'label' => 'Synced', 'size'=> 24],
+//            'pending' => ['filename' => 'cloud', 'label' => 'Synced', 'size'=> 24],
+			'offline' => ['filename' => 'cloud-slash', 'label' => 'Offline', 'size'=> 24],
+			'error' => ['filename' => 'cloud-warning', 'label' => 'Upload Failed', 'size'=> 24],
+
+		],
+		'status' => [
+			'show'          => ['filename' => 'eye', 'label' => 'Show', 'size' => 20],
+			'publish'       => ['filename' => 'eye', 'label' => 'Public', 'size' => 20],
+			'hide'          => ['filename' => 'eye-closed', 'label' => 'Hide', 'size' => 20],
+			'draft'         => ['filename' => 'eye-closed', 'label' => 'Hidden', 'size' => 20],
+			'pinned'        => ['filename' => 'push-pin-simple', 'label' => 'Pin', 'size' => 32],
+			'bell'          => ['filename' => 'bell', 'label' => 'Notification', 'size' => 32],
+			'bell-ringing'  => ['filename' => 'bell-ringing', 'label' => 'Notification', 'size' => 32]
+		],
+		'sorting' => [
+			'table' => ['filename' => 'table', 'label' => 'Table View', 'size' => 20],
+			'columns' => ['filename' => 'columns', 'label' => 'Show Columns', 'size' => 20],
+			'alphabetical' => ['filename' => 'alphabetical', 'label' => 'Alphabetical', 'size' => 20],
+			'calendar' => ['filename' => 'calendar-heart', 'label' => 'Date', 'size' => 20],
+			'asc' => ['filename' => 'sort-ascending', 'label' => 'Sort Ascending', 'size' => 20],
+			'ASC' => ['filename' => 'sort-ascending', 'label' => 'Sort Ascending', 'size' => 20],
+			'desc' => ['filename' => 'sort-descending', 'label' => 'Sort Descending', 'size' => 20],
+			'DESC' => ['filename' => 'sort-descending', 'label' => 'Sort Descending', 'size' => 20],
+			'filter' => ['filename' => 'faders', 'label' => 'Filter'],
+			'all' => ['filename' => 'infinity', 'label' => 'All', 'size' => 32],
+			'random' => ['filename' => 'shuffle', 'label' => 'Random', 'size' => 20]
+		],
+		'business' => [
+			'location' => ['filename' => 'map-pin', 'label' => 'Location'],
+			'hours' => ['filename' => 'clock', 'label' => 'Hours'],
+			'star'  => ['filename' => 'star', 'label' => 'Empty Star'],
+			'star-fill'  => ['filename' => 'star-fill.svg', 'label' => 'Star'],
+			'star-half'  => ['filename' => 'star-half-fill.svg', 'label' => 'Half Star'],
+		],
+		'social' => [
+			'heart'     => ['filename' => 'heart', 'label' => 'Favourite', 'size' => 24],
+			'favourite'     => ['filename' => 'heart', 'label' => 'Favourite', 'size' => 24],
+			'favourites'=> ['filename' => 'heart', 'label' => 'Your Favourites', 'size' => 24],
+			'heart-fill'=> ['filename' => 'heart-fill.svg', 'label' => 'Favourited', 'size' => 24],
+			'share'     => ['filename' => 'share', 'label' => 'Share', 'size' => 32],
+			'email'     => ['filename' => 'envelope','label'=> 'Email'],
+			'text'      => ['filename' => 'chat','label'=> 'Text'],
+			'phone'     => ['filename' => 'phone','label'=> 'Call'],
+		],
+		'external'   => [
+			'integrations' => ['filename' => 'plugs-connected', 'label' => 'Integrations'],
+			'connected' => ['filename' => 'plugs-connected', 'label' => 'Connected'],
+			'disconnected' => ['filename' => 'plugs', 'label' => 'Disconnected'],
+			'instagram' => ['filename' => 'instagram-logo', 'label' => 'Instagram'],
+			'facebook'  => ['filename' => 'facebook-logo', 'label' => 'Facebook'],
+			'messenger' => ['filename' => 'messenger-logo', 'label' => 'Facebook Messenger'],
+			'twitter'   => ['filename' => 'twitter-logo', 'label' => 'Twitter'],
+			'x'   => ['filename' => 'x-logo', 'label' => 'X'],
+			'youtube'   => ['filename' => 'youtube-logo', 'label' => 'YouTube'],
+			'mastadon'   => ['filename' => 'mastadon-logo', 'label' => 'Mastadon'],
+			'fediverse'   => ['filename' => 'fediverse-logo', 'label' => 'Fediverse'],
+			'linktree'   => ['filename' => 'linktree-logo', 'label' => 'LinkTree'],
+			'snapchat'   => ['filename' => 'snapchat-logo', 'label' => 'Snapchat'],
+			'twitch'   => ['filename' => 'twitch-logo', 'label' => 'Twitch'],
+			'threads'   => ['filename' => 'threads-logo', 'label' => 'Threads'],
+			'tiktok'   => ['filename' => 'tiktok-logo', 'label' => 'TikTok'],
+			'square-up'   => ['filename' => 'square-logo', 'label' => 'Square'],
+			'umami'   => ['filename' => 'chart-line', 'label' => 'Umami'],
+		]
+	];
+	private string $defaultStyle = '-thin';
+	private int $defaultSize = 16;
+	protected string $name;
+	protected array $used;
+	protected int $size = 20;
+	protected CacheManager $cache;
+
+	public function __construct()
+	{
+		$this->cache = new CacheManager('icons', 604800); //1 week in seconds
+//		$this->cache->invalidateGroup('icons');
+		$this->style = JVB_SITE['icons']??'regular';
+
+
+		$this->used = get_option(BASE.'used_icons', [
+			$this->style => [
+				'heart',
+				'hours',
+				'random',
+				'alphabetical',
+				'calendar',
+				'asc',
+				'desc',
+				'all',
+				'paragraph',
+				'h1',
+				'h2',
+				'h3',
+				'bold',
+				'italic',
+				'underline',
+				'strike',
+				'list-bullets',
+				'list-numbers',
+				'image',
+				'align-left',
+				'align-right',
+				'align-center',
+				'link',
+				'note',
+				'password',
+				'light',
+				'left',
+				'dark',
+				'grid',
+				'list',
+				'save',
+				'restore',
+				'edit',
+				'delete',
+				'search',
+				'add',
+				'minus',
+				'help',
+				'support',
+				'grab',
+				'share',
+				'cart',
+				'checkout',
+				'upload',
+				'download',
+				'synced',
+				'syncing',
+				'cloud',
+				'offline',
+				'error',
+				'show',
+				'publish',
+				'hide',
+				'draft',
+				'pinned',
+				'bell',
+				'bell-ringing',
+				'back',
+				'home',
+				'right',
+				'elbow-right-down',
+				'elbow-right-up',
+				'elbow-left-down',
+				'elbow-left-up',
+				'menu',
+				'submenu',
+				'close',
+				'close-square',
+				'dashboard',
+				'dash',
+				'settings',
+				'system',
+				'options',
+				'news',
+				'metrics',
+				'prev',
+				'up',
+				'down',
+				'right',
+				'copy',
+				'next',
+				'refresh',
+				'pending',
+				'clock',
+				'copy',
+				'list-heart',
+				'karma',
+				'response',
+				'reply',
+				'approval',
+				'check',
+				'hamburger',
+				'location',
+				'hours',
+				'star',
+				'star-half',
+				'exclamation-mark'
+			],
+			'fill'	=> [
+				'heart',
+				'arrow-fat-down',
+				'arrow-fat-up',
+				'star'
+			]
+		]);
+	}
+
+	protected function updateUsed():void
+	{
+		$icons = [];
+		foreach ($this->used as $style => $items) {
+			$temp = array_unique($items);
+			sort($temp);
+			$icons[$style] = $temp;
+		}
+		update_option(BASE . 'used_icons', $icons);
+	}
+
+	protected function checkMap(string $name): string
+	{
+		$result = apply_filters('jvbIconMap', (array_key_exists($name, $this->map)) ? $this->map[$name] : $name, $name);
+		return $result;
+	}
+
+	protected function checkName(string $name) {
+		$name = $this->checkMap($name);
+		$path = (str_contains($name, '.svg')) ? '/assets/icons/' : '/assets/phosphor-icons/regular/';
+		$filename = (str_contains($name, '.svg')) ? $name : $name.'.svg';
+		return file_exists(JVB_DIR . $path . $filename);
+	}
+	public function getIcon(string $name, array $options = []):?string
+	{
+		if (!$this->checkName($name)) {
+			error_log('[Icons]Icon not found for: '.print_r($name, true));
+			return '';
+		}
+		$style = (array_key_exists('style', $options) && in_array($options['style'], $this->styles)) ? $options['style'] : 'regular';
+
+		$update = false;
+		if (!array_key_exists($style, $this->used)) {
+			$this->used[$style] = [];
+			$update = true;
+		}
+		if (!in_array($name, $this->used[$style])) {
+			$this->used[$style][] = $name;
+			$update = true;
+		}
+		if ($update) {
+			$this->updateUsed();
+		}
+
+		$name = $this->checkMap($name);
+		$this->name = str_replace('.svg', '', $name);
+
+
+		// Merge options with defaults and icon config
+		$options = array_merge([
+			'title' => $options['label']??$this->getIconLabel($name),
+			'size' => $options['size'] ?? $this->size,
+			'style' => $style,
+			'class' => '',
+			'wrap'  => true,
+			'color' => 'currentColor'
+		], $options);
+
+		return $this->cache->remember(
+			array_merge($options, ['name' => $name]),
+			function () use ($name, $options) {
+				return $this->buildIcon($name, $options);
+			}
+		);
+	}
+
+	public function getIconsByGroup(string $group):array
+	{
+		if (!isset(self::ICON_GROUPS[$group])) {
+			return [];
+		}
+
+		$icons = [];
+		foreach (self::ICON_GROUPS[$group] as $name => $config) {
+			$icons[$name] = $this->getIcon($name);
+		}
+
+		return array_filter($icons);
+	}
+
+	public function getAllIcons():array
+	{
+		$icons = [];
+		foreach (self::ICON_GROUPS as $group => $groupIcons) {
+			foreach ($groupIcons as $name => $config) {
+				$icons[$name] = $this->getIcon($name);
+			}
+		}
+
+		return array_filter($icons);
+	}
+
+	public function getGroups():array
+	{
+		return array_keys(self::ICON_GROUPS);
+	}
+
+	private function findIconConfig(string $name):?array
+	{
+		foreach (self::ICON_GROUPS as $groupIcons) {
+			if (isset($groupIcons[$name])) {
+				return $groupIcons[$name];
+			}
+		}
+		return null;
+	}
+
+	private function buildIcon(string $name, array $options):?string
+	{
+
+		$filepath = $this->buildFilePath($name, $options['style']);
+		if (!file_exists($filepath)) {
+			error_log("Icon file not found: $filepath");
+			return null;
+		}
+
+		$svg = file_get_contents($filepath);
+		if ($svg === false) {
+			return null;
+		}
+
+		return $this->formatSvg($svg, $options);
+	}
+
+	private function buildFilePath(string $filename, string $style = ''):string
+	{
+		$svg = false;
+		if (str_contains($filename, '.svg')) {
+			$svg = true;
+			$nameExtra = '';
+			$path = '/assets/icons/';
+		} else {
+			$nameExtra = ($style === 'regular') ? '' : '-'.$style;
+			$path = '/assets/phosphor-icons/';
+		}
+
+		$filename = (str_contains($filename, '.svg')) ? $filename : $filename . $nameExtra . '.svg';
+		$style = ($style === '') ? '' : $style .'/';
+		$style = ($svg) ? '' : $style;
+		return JVB_DIR . $path . $style . $filename;
+	}
+
+	private function formatSvg(string $svg, array $options): string
+	{
+		// Clean up SVG
+		$svg = preg_replace("/([\n\t]+)/", ' ', $svg);
+		$svg = preg_replace('/>\s*</', '><', $svg);
+
+		// Add size attributes - FIXED: assign results back to $svg
+		if ($options['size'] !== 32) {
+			$svg = str_replace('width="32"', 'width="' . (int)$options['size'] . '"', $svg);
+			$svg = str_replace('height="32"', 'height="' . (int)$options['size'] . '"', $svg);
+		}
+
+		// Add color if provided - FIXED: assign results back to $svg
+		if ($options['color'] !== 'currentColor') {
+			$svg = str_replace('currentColor', $options['color'], $svg);
+		}
+
+		// Add title if provided
+		if (!empty($options['title'])) {
+			$svg = str_replace('<svg', '<svg title="' . esc_attr($options['title']) . '"', $svg);
+			$svg = str_replace('aria-hidden="true"', '', $svg);
+		}
+
+		$classes = trim('icon row ' . $this->name . ' ' . $options['class']);
+		if (array_key_exists('wrap', $options) && $options['wrap'] === false) {
+			return $svg;
+		}
+		return sprintf(
+			'<i class="%s">%s</i>',
+			esc_attr($classes),
+			$svg
+		);
+	}
+
+	/****************************
+	 * Converts to CSS icon
+	 ***************************/
+	public function getCSSIcon(string $name, array $options = []): ?string
+	{
+		if (!$this->checkName($name)) {
+			return '';
+		}
+		$name = $this->checkMap($name);
+		$style = (array_key_exists('style', $options) && in_array($options['style'], $this->styles)) ? $options['style'] : 'regular';
+
+		$this->name = str_replace('.svg', '', $name);
+
+		// Merge options with defaults and icon config
+		$options = array_merge([
+			'title' => $options['label']??$this->getIconLabel($name),
+			'size' => $options['size'] ?? $this->size,
+			'style' => $style,
+			'class' => '',
+			'wrap'  => true,
+			'color' => 'currentColor'
+		], $options);
+
+		$svg = $this->cache->remember(
+			array_merge($options, ['name' => $name, 'css' => true]),
+			function() use ($name, $options) {
+				return $this->buildRawSvg($name, $options);
+			}
+		);
+		// Convert to base64 data URI
+		$svg = ($svg) ? 'data:image/svg+xml;base64,' . base64_encode($svg) : null;
+		return $svg;
+	}
+
+	private function buildRawSvg(string $filename, array $options): ?string
+	{
+		$filepath = $this->buildFilePath($filename, $options['style']);
+		if (!file_exists($filepath)) {
+			error_log("Icon file not found: $filepath");
+			return null;
+		}
+
+		$svg = file_get_contents($filepath);
+		if ($svg === false) {
+			return null;
+		}
+
+		return $this->formatRawSvg($svg, $options);
+	}
+
+	private function formatRawSvg(string $svg, array $options): string
+	{
+		// Clean up SVG
+		$svg = preg_replace("/([\n\t]+)/", ' ', $svg);
+		$svg = preg_replace('/>\s*</', '><', $svg);
+
+		// Add size attributes - FIXED: assign results back to $svg
+		if ($options['size'] > 0) {
+			$svg = str_replace('width="32"', 'width="' . (int)$options['size'] . '"', $svg);
+			$svg = str_replace('height="32"', 'height="' . (int)$options['size'] . '"', $svg);
+		}
+
+		// Add color if provided and not currentColor - FIXED: assign results back to $svg
+		if ($options['color'] !== 'currentColor') {
+			$svg = str_replace('currentColor', $options['color'], $svg);
+		}
+
+		return $svg;
+	}
+
+	public function localizeIcons():array
+	{
+		if (empty($this->used)) {
+			return [];
+		}
+		$used = [];
+		foreach ($this->used as $style => $icons) {
+			foreach ($icons as $icon) {
+				$used[$icon] = $this->getIcon($icon, ['style' => $style]);
+			}
+		}
+		return $used;
+	}
+
+	public function getIconLabel(string $name): string
+	{
+		$name = str_replace('.svg', '', $name);
+		$label = (array_key_exists($name, $this->labels)) ? $this->labels[$name] : '';
+
+		$result = apply_filters('jvbIconLabel', $label, $name);
+
+		return $result;
+	}
+}
diff --git a/inc/blocks/CustomBlocks.php b/inc/blocks/CustomBlocks.php
index 8e175b8..966276f 100644
--- a/inc/blocks/CustomBlocks.php
+++ b/inc/blocks/CustomBlocks.php
@@ -20,7 +20,10 @@
         $this->cache = CacheManager::for('blocks', WEEK_IN_SECONDS);
 		$this->imgCache = CacheManager::for('images', WEEK_IN_SECONDS);
 
-        add_action('render_block', [$this, 'render'], 10, 3);
+
+		add_filter('render_block', [$this, 'render'], 999, 3);
+
+
         add_action('init', [$this, 'registerBlockStyles']);
     }
 
@@ -67,8 +70,10 @@
 
     public function render(string $content, array $block, WP_Block $instance)
     {
-        $method = 'render_'.$this->sanitizeBlockName($block);
+		$blockName = $this->sanitizeBlockName($block);
+        $method = 'render_'.$blockName;
 		$function = BASE.$method;
+
 		if (function_exists($function)) {
 //			return $this->cache->remember(
 //				$block,
@@ -77,8 +82,7 @@
 //				}
 //			);
 			return $function($block, $content);
-		}
-		if (method_exists($this, $method)) {
+		} else if (method_exists($this, $method)) {
 			return $this->$method($block, $content);
 			//TODO: Recache it
 //			return $this->cache->remember(
@@ -94,9 +98,10 @@
 				'core/post-title',
 				'core/list-item',
 				'core/site-title',
+				'jvb/forms'
 			];
 			if (!in_array($block['blockName'], $ignore)) {
-				jvbDump('No method found for '.print_r($block['blockName'], true));
+//				jvbDump('No method found for '.print_r($block['blockName'], true));
 			}
 		}
         if ($block['blockName'] === 'jvb/feed') {
@@ -220,6 +225,7 @@
         $attrs = $block['attrs'] ?? [];
         $innerContent = $this->innerBlocks($block);
 
+		$position = 'object-position: center;';
 		if (array_key_exists('focalPoint', $attrs)) {
 			$x = (array_key_exists('x', $attrs['focalPoint'])) ? ($attrs['focalPoint']['x'] * 100).'%' : 'center';
 			$y = (array_key_exists('y', $attrs['focalPoint'])) ? ($attrs['focalPoint']['y'] * 100).'%' : 'center';
@@ -231,8 +237,16 @@
         $backgroundType = $attrs['backgroundType'] ?? 'image';
         $background = '';
 
-        if ($backgroundType === 'image' && isset($attrs['id'])) {
-			$background .= str_replace('<img', '<img style="'.$position.'"', $this->image($attrs['id']));
+		$ID = false;
+		if (array_key_exists('useFeaturedImage', $attrs)) {
+			global $post;
+			$ID = get_post_thumbnail_id($post->ID);
+		} else {
+			$ID = (int)$attrs['id'];
+		}
+
+        if ($backgroundType === 'image' && $ID) {
+			$background .= str_replace('<img', '<img style="'.$position.'"', $this->image($ID));
         } elseif ($backgroundType === 'video' && isset($attrs['url'])) {
             $background .= '<video style="'.$position.'"autoplay muted loop playsinline src="' . esc_url($attrs['url']) . '"></video>';
         }
@@ -471,8 +485,8 @@
             aria-label="Open Menu"
             aria-controls="navigation-' .$ID. '"
             aria-expanded="false">'.
-            jvbIcon('menu', ['title'=>'Toggle Menu']).
-            jvbIcon('close', ['title'=>'Toggle Menu']).
+            jvbIcon('list', ['title'=>'Toggle Menu']).
+            jvbIcon('x', ['title'=>'Toggle Menu']).
             '</button>';
         $class = ($toggle === '') ?
             $this->getClassesAndStyles($block['attrs'], ['mobile']) :
@@ -537,7 +551,7 @@
         $linkOpen = $this->build_navigation_link($block['attrs'], $aria);
         $content = '<li'.$classes.'>'.$linkOpen.$block['attrs']['label'].
                    '</a><button class="toggle" data-action="toggle-submenu" title="Toggle Submenu" aria-label="Open '.$block['attrs']['label'].' Submenu" aria-expanded="false" aria-controls="'.$id.'-submenu">'.
-                   jvbIcon('submenu', ['title'=>'Toggle Submenu']).
+                   jvbIcon('caret-down', ['title'=>'Toggle Submenu']).
                    '</button><ul class="submenu" id='.$id.'-submenu">';
 
         $content .= $this->innerBlocks($block);
@@ -607,8 +621,10 @@
     //core_post_excerpt
     protected function render_core_post_featured_image(array $block):string
     {
+		global $post;
+		$ID = get_post_thumbnail_id($post->ID);
         return '<figure'.$this->getClassesAndStyles($block['attrs']).'>'.
-               $this->imageLink(true).
+               $this->image($ID).
                '</figure>';
     }
     //core_post_navigation_link
@@ -767,12 +783,12 @@
 
 		$check = ['header', 'footer'];
 		$isHeaderTemplate = (
-			(array_key_exists('slug', $block['attrs']) && str_contains($block['attrs']['slug'], 'header')) ||
-			(array_key_exists('tagName', $block['attrs']) && str_contains($block['attrs']['tagName'], 'header'))
+			(array_key_exists('slug', $block['attrs']) && str_contains(strtolower($block['attrs']['slug']), 'header')) ||
+			(array_key_exists('tagName', $block['attrs']) && str_contains(strtolower($block['attrs']['tagName']), 'header'))
 		) ? 'header' : false;
 		$isFooterTemplate = (
-			(array_key_exists('slug', $block['attrs']) && str_contains($block['attrs']['slug'], 'footer')) ||
-			(array_key_exists('tagName', $block['attrs']) && str_contains($block['attrs']['tagName'], 'footer'))
+			(array_key_exists('slug', $block['attrs']) && str_contains(strtolower($block['attrs']['slug']), 'footer')) ||
+			(array_key_exists('tagName', $block['attrs']) && str_contains(strtolower($block['attrs']['tagName']), 'footer'))
 		) ? 'footer' : false;
 
 
@@ -788,11 +804,12 @@
 				}
                 $checked = (is_user_logged_in() && current_user_can('prefers_dark_theme', true)) ? ' checked' : '';
                 $title = ($checked == '') ? 'Toggle Dark Mode' : 'Toggle Light Mode';
-                $themeSwitch = '<label title="'.$title.'" id="theme-switch" class="toggle-switch" for="theme-switcher">
+				$showThemeSwitch = (bool)apply_filters('jvb_show_theme_switch', true);
+                $themeSwitch = ($showThemeSwitch) ? '<label title="'.$title.'" id="theme-switch" class="toggle-switch" for="theme-switcher">
                     <input class="theme-switch row" id="theme-switcher" type="checkbox"'.$checked.' data-setting="theme" data-theme role="switch" name="dark-mode"><span class="slider">'.
-					jvbIcon('light', ['title'=> 'Light Mode']).
-					jvbIcon('dark', ['title'=>'Dark Mode']).
-					'</span></label>';
+					jvbIcon('sun-dim', ['title'=> 'Light Mode']).
+					jvbIcon('moon', ['title'=>'Dark Mode']).
+					'</span></label>' : '';
                 $breadcrumbs = jvbBuildBreadcrumbs();
 				$afterHeader = apply_filters('jvbBelowHeader', $afterHeader);
 				if ($afterHeader !== '') {
@@ -859,8 +876,6 @@
                 'id' => get_current_user_id()
             ] : null,
             'from'  => get_the_ID(),
-            'icons' => jvbFeedBlockIcons(),
-
         ]);
     }
 
@@ -876,18 +891,22 @@
 
     public function innerBlocks(array $block, string $before = '', string $after = ''):string
     {
-        $content = '';
-        foreach ($block['innerBlocks'] as $b) {
-            $method = 'render_'.$this->sanitizeBlockName($b);
-            $content .= $before;
-            if (method_exists($this, $method)) {
-                $content .= $this->$method($b, '');
-            } else {
-                $content .= render_block($b);
-            }
-            $content .= $after;
-        }
-        return $content;
+		$content = '';
+		foreach ($block['innerBlocks'] as $b) {
+			$method = 'render_'.$this->sanitizeBlockName($b);
+			$function = BASE.$method;
+
+			$content .= $before;
+			if (function_exists($function)) {
+				$content .= $function($b, '');
+			} else if (method_exists($this, $method)) {
+				$content .= $this->$method($b, '');
+			} else {
+				$content .= render_block($b);
+			}
+			$content .= $after;
+		}
+		return $content;
     }
 
     public function inside(array $block, mixed $tag = false, mixed $o = false):string
@@ -946,8 +965,8 @@
 
     public function imageID(int|string $ID, array $block = []):int|false
     {
-
         if ($ID === '' && !empty($block)) {
+
             if (($block['blockName'] === 'core/post-featured-image' ||
                  (!array_key_exists('attrs', $block) && !array_key_exists('id', $block['attrs'])))) {
                 $ID = get_post_thumbnail_id();
@@ -982,7 +1001,11 @@
         if (!$ID) {
             return '';
         }
-        $imgSlug = get_post($ID)->post_name;
+		$img = get_post($ID);
+		if (!$img) {
+			return '';
+		}
+        $imgSlug = $img->post_name;
 
         $img = '<a class="open-gallery" target="_blank" rel="nofollow" data-opens="gallery-'.$postSlug.'" data-focus="'.$postSlug.'-'.$imgSlug.'">'.$image;
         if ($close) {
@@ -1167,7 +1190,6 @@
     }
     protected function getClass(string $key, string|bool|array|int $value, array $attrs):string|array
     {
-
         switch ($key) {
             //Any additional classes the user adds
             case 'className':
@@ -1177,6 +1199,7 @@
                     default => str_replace('is-style-', '', $value),
                 };
 			case 'contentPosition':
+
 				$classes = [];
 				$pos = explode(' ', $value);
 				foreach($pos as $p) {
@@ -1229,14 +1252,14 @@
 //
 //				}
 				if (!array_key_exists('justifyContent', $value) && !array_key_exists('contentPosition', $attrs)) {
-					$classes[] = 'start';
+					$classes[] = ($type === 'row') ? 'start' : 'a-start';
 				}
 				if (array_key_exists('justifyContent', $value)  && !array_key_exists('contentPosition', $attrs)) {
 					if (in_array($value['justifyContent'], ['left', 'right','space-between'])) {
 //						jvbDump($type);
 						switch ($value['justifyContent']) {
 							case 'right':
-								$classes[] = 'end';
+								$classes[] = ($type === 'row') ? 'end' : 'a-end';
 								break;
 							case 'space-between':
 								$classes[] = 'btw';
@@ -1412,6 +1435,7 @@
 				return '';
             default:
 				$ignore = [
+					'useFeaturedImage',
 					'opacity',
 					'borderColor',
 					'backgroundColor',
@@ -1453,8 +1477,8 @@
 				];
 				if (!is_admin() &&!in_array($key, $ignore)) {
 //					TESTING
-					jvbDump($key, 'getClass');
-					jvbDump($attrs);
+//					jvbDump($key, 'getClass');
+//					jvbDump($attrs);
 				}
 
                 return '';
@@ -1674,6 +1698,7 @@
             // Any other attributes that need direct styling
             default:
 				$ignore = [
+					'useFeaturedImage',
 					'opacity',
 					'textAlign',
 					'minHeightUnit',
@@ -1718,8 +1743,8 @@
 				];
 				if (!is_admin() && !in_array($key, $ignore)) {
 					//TESTING
-					jvbDump($key, 'getStyle');
-					jvbDump($attrs);
+//					jvbDump($key, 'getStyle');
+//					jvbDump($attrs);
 				}
                 // No default inline styles
                 break;
diff --git a/inc/blocks/FeedBlock.php b/inc/blocks/FeedBlock.php
index afb159c..83f8b1d 100644
--- a/inc/blocks/FeedBlock.php
+++ b/inc/blocks/FeedBlock.php
@@ -223,7 +223,7 @@
                         <div class="filter-actions row">
                             <?= jvbRenderToggleTextField('match', 'Match', 'Filters', 'ALL', 'ANY') ?>
                             <button type="button" class="clear-filters row">
-                                <?= jvbIcon('close', ['title'    => 'Clear']) ?>
+                                <?= jvbIcon('x', ['title'    => 'Clear']) ?>
                                 Clear All Filters
                             </button>
                         </div>
@@ -247,7 +247,7 @@
 
                         <input type="radio" id="order-random" class="btn" name="orderby" value="random">
                         <label for="order-random" title="Random Order" class="row">
-                            <?= jvbIcon('random') ?>
+                            <?= jvbIcon('shuffle') ?>
                             <span class="label">Random</span>
                         </label>
                     </div>
@@ -256,12 +256,12 @@
                         <span class="label">ORDER:</span>
                         <input type="radio" id="order-desc" class="btn" name="order" value="desc" checked>
                         <label for="order-desc" title="Newest First" class="row">
-                            <?= jvbIcon('desc') ?>
+                            <?= jvbIcon('sort-descending') ?>
                         </label>
 
                         <input type="radio" id="order-asc" class="btn" name="order" value="asc">
                         <label for="order-asc" title="Oldest First" class="row">
-                            <?= jvbIcon('asc') ?>
+                            <?= jvbIcon('sort-ascending') ?>
                         </label>
                     </div>
                 </div>
@@ -281,9 +281,9 @@
     {
         ?>
         <button type="button" class="load-more">
-            <?= jvbIcon('elbow-left-down', ['title'    =>'More']) ?>
+            <?= jvbIcon('arrow-elbow-left-down', ['title'    =>'More']) ?>
             Show Me More
-            <?= jvbIcon('elbow-right-down', ['title'=> 'More']) ?>
+            <?= jvbIcon('arrow-elbow-right-down', ['title'=> 'More']) ?>
         </button>
 
         <?= jvbLoadingScreen() ?>
diff --git a/inc/forms/PostSelectorOld.php b/inc/forms/PostSelectorOld.php
index c753598..333ecec 100644
--- a/inc/forms/PostSelectorOld.php
+++ b/inc/forms/PostSelectorOld.php
@@ -2,8 +2,6 @@
 namespace JVBase\forms;
 
 use JVBase\forms\TaxonomySelector;
-use JVBase\JVBIcons;
-use JVBase\managers\CacheManager;
 use WP_REST_Request;
 use WP_REST_Response;
 use WP_Term;
diff --git a/inc/forms/TaxonomySelector.php b/inc/forms/TaxonomySelector.php
index 6489e0d..29f7df3 100644
--- a/inc/forms/TaxonomySelector.php
+++ b/inc/forms/TaxonomySelector.php
@@ -187,13 +187,13 @@
 		</template>
 		<template class="termChildrenToggle">
 			<button type="button" class="toggle-children" aria-expanded="false">
-				<?=jvbIcon('add')?>
+				<?=jvbIcon('plus-square')?>
 			</button>
 		</template>
 		<template class="selectedTerm">
 			<div class="selected-item row">
 				<span class="item-name"></span>
-				<button type="button" class="remove-item row"><?=jvbIcon('close')?></button>
+				<button type="button" class="remove-item row"><?=jvbIcon('x')?></button>
 			</div>
 		</template>
 		<template class="termBreadcrumb">
@@ -237,7 +237,7 @@
 					<?= $hasAutocomplete ?>
 					title="Open <?= $this->singular ?> Selector"
 					aria-label="Select <?= esc_attr($this->plural) ?>">
-					<?= jvbIcon('add', ['title' => 'Add ' . $this->title]) ?>
+					<?= jvbIcon('plus-square', ['title' => 'Add ' . $this->title]) ?>
 				</button>
 				<input type="text" id="<?= $this->base ?><?= esc_attr($this->config['name']) ?>-autocomplete" autocomplete="off" data-ignore data-autocomplete>
 				<ul class="autocomplete-dropdown" hidden>
@@ -320,7 +320,7 @@
 			<button type="button"
 					class="remove-item row"
 					aria-label="Remove <?= esc_attr($term->name) ?>">
-				<?= jvbIcon('close') ?>
+				<?= jvbIcon('x') ?>
 			</button>
 		</div>
 		<?php
diff --git a/inc/forms/TaxonomySelectorOld.php b/inc/forms/TaxonomySelectorOld.php
index 32ceef1..7da3336 100644
--- a/inc/forms/TaxonomySelectorOld.php
+++ b/inc/forms/TaxonomySelectorOld.php
@@ -1,8 +1,8 @@
 <?php
 namespace JVBase\forms;
 
-use JVBase\JVBIcons;
 use JVBase\managers\CacheManager;
+use JVBase\managers\IconsManager;
 use WP_REST_Request;
 use WP_REST_Response;
 use WP_Term;
@@ -21,7 +21,7 @@
     protected string $id;
     protected string $name;
     protected string $taxonomyName;
-    protected JVBIcons $icon;
+    protected IconsManager $icon;
     protected string $plural;
 
     protected string $taxonomy;
@@ -43,7 +43,7 @@
         $this->id = sanitize_key($id);
         $this->taxonomy = jvbCheckBase($taxonomy);
         $this->name = str_replace(BASE, '', $taxonomy);
-        $this->icon = new JVBIcons();
+        $this->icon = IconsManager::getInstance();
         $this->cache = CacheManager::for(jvbNoBase($taxonomy), WEEK_IN_SECONDS);
 
         $this->base = $config['base'] ?? '';
diff --git a/inc/helpers/all.php b/inc/helpers/all.php
index 8229c30..0db6425 100644
--- a/inc/helpers/all.php
+++ b/inc/helpers/all.php
@@ -13,7 +13,7 @@
 require(JVB_DIR . '/inc/helpers/email.php');
 require(JVB_DIR . '/inc/helpers/forms.php');
 require(JVB_DIR . '/inc/helpers/formatting.php');
-require(JVB_DIR . '/inc/helpers/icons.php');
+//require(JVB_DIR . '/inc/helpers/icons.php');
 require(JVB_DIR . '/inc/helpers/inlineStyles.php');
 require(JVB_DIR . '/inc/helpers/legacy.php');
 require(JVB_DIR . '/inc/helpers/media.php');
diff --git a/inc/helpers/breadcrumbs.php b/inc/helpers/breadcrumbs.php
index 5085d50..4a42118 100644
--- a/inc/helpers/breadcrumbs.php
+++ b/inc/helpers/breadcrumbs.php
@@ -23,7 +23,7 @@
     $crumbs = [];
     $crumbs[] = [
         'name'  => 'Home',
-        'icon'  => jvbIcon('home'),
+        'icon'  => jvbIcon('house'),
         'url'   => get_home_url(),
     ];
 
diff --git a/inc/helpers/crud.php b/inc/helpers/crud.php
index 3966261..fa0bdbb 100644
--- a/inc/helpers/crud.php
+++ b/inc/helpers/crud.php
@@ -103,7 +103,7 @@
                     </button>
                 </div>
             </div>',
-            jvbIcon('close', ['title'=>'Cancel'])
+            jvbIcon('x', ['title'=>'Cancel'])
         );
     }
     $out .= '</div>';
diff --git a/inc/helpers/formatting.php b/inc/helpers/formatting.php
index dae808e..de3d379 100644
--- a/inc/helpers/formatting.php
+++ b/inc/helpers/formatting.php
@@ -147,13 +147,13 @@
         return [];
     }
     $image = [
-        'tiny' => wp_get_attachment_image_src($imgID, 'tiny')[0],
-        'small' => wp_get_attachment_image_src($imgID, 'medium')[0],
-        'medium' => wp_get_attachment_image_src($imgID, 'large')[0],
-        'large' => wp_get_attachment_image_src($imgID, 'full')[0],
-        'alt'   => get_post_meta($imgID, '_wp_attachment_image_alt', true),
-		'title'	=> get_the_title($imgID),
-		'caption' => get_the_excerpt($imgID),
+        'tiny' 			=> wp_get_attachment_image_src($imgID, 'tiny')[0],
+        'small' 		=> wp_get_attachment_image_src($imgID, 'medium')[0],
+        'medium' 		=> wp_get_attachment_image_src($imgID, 'large')[0],
+        'large' 		=> wp_get_attachment_image_src($imgID, 'full')[0],
+        'image-alt-text'=> get_post_meta($imgID, '_wp_attachment_image_alt', true),
+		'image-title'	=> get_the_title($imgID),
+		'image-caption' => get_the_excerpt($imgID),
     ];
     $cache->set($imgID, $image);
     return $image;
diff --git a/inc/helpers/forms.php b/inc/helpers/forms.php
index 7d76069..c993381 100644
--- a/inc/helpers/forms.php
+++ b/inc/helpers/forms.php
@@ -21,7 +21,7 @@
 			$meta->render('form', $field, $config);
 		}
 		?>
-		<?= (jvbCheck('submit', $options)) ? '<button type="submit">'.jvbIcon('save').'Save</button>' : '' ?>
+		<?= (jvbCheck('submit', $options)) ? '<button type="submit">'.jvbIcon('floppy-disk').'Save</button>' : '' ?>
 	</form>
 	<?php
 	$out = ob_get_clean();
diff --git a/inc/helpers/icons.php b/inc/helpers/icons.php
deleted file mode 100644
index 196a1ce..0000000
--- a/inc/helpers/icons.php
+++ /dev/null
@@ -1,54 +0,0 @@
-<?php
-
-if (!defined('ABSPATH')) {
-	exit;
-}
-
-use JVBase\JVBIcons;
-function jvbIcon(string $icon, array $options = []):string
-{
-	$get = new JVBIcons();
-	return $get->getIcon($icon, $options);
-}
-
-function jvbCSSIcon(string $icon, array $options = []):string
-{
-	$get = new JVBIcons();
-	return $get->getCSSIcon($icon, $options);
-}
-function jvbGetCSSIcons(array $iconSlugs, array $options = []):array
-{
-	$get = new JVBIcons();
-	$return = [];
-	foreach($iconSlugs as $slug) {
-		$icon = $get->getCSSIcon($slug, $options);
-		if ($icon !== '') {
-			$return[$slug] = $icon;
-		}
-	}
-	return $return;
-}
-function jvbFeedBlockIcons():array
-{
-	$icon = new JVBIcons();
-	return array_merge(
-		$icon->getIconsByGroup('content'),
-		[
-			'heart'         => $icon->getIcon('heart',['title'=>'Favourite']),
-			'heart-filled'  => $icon->getIcon('heart',['style' => 'fill','title'=>'Un-Favourite']),
-			'artist'        => $icon->getIcon('artist'),
-			'shop'          => $icon->getIcon('shop'),
-			'tattoo'        => $icon->getIcon('tattoo'),
-			'piercing'      => $icon->getIcon('piercing'),
-			'artwork'       => $icon->getIcon('artwork'),
-			'location'      => $icon->getIcon('location'),
-			'hours'         => $icon->getIcon('hours'),
-			'random'        => $icon->getIcon('random'),
-			'alphabetical'  => $icon->getIcon('alphabetical'),
-			'calendar'      => $icon->getIcon('calendar'),
-			'asc'           => $icon->getIcon('asc'),
-			'desc'          => $icon->getIcon('desc'),
-			'all'           => $icon->getIcon('all'),
-		]
-	);
-}
diff --git a/inc/helpers/legacy.php b/inc/helpers/legacy.php
index c5ff946..79377ab 100644
--- a/inc/helpers/legacy.php
+++ b/inc/helpers/legacy.php
@@ -11,7 +11,9 @@
 
 function jvbRandomFooterText():string
 {
-	return apply_filters('jvbRandomFooterText', '<p>©'.date('Y').' '.get_bloginfo('name').'</p><p>Built with ♡ by <a href="https://jakevan.ca">Jake Van</a>');
+	$privacy = get_privacy_policy_url();
+	$privacy = ($privacy === '') ? '' : ' | <a href="'.$privacy.'">Privacy Policy</a>';
+	return apply_filters('jvbRandomFooterText', '<p class="font-small">©'.date('Y').' <a href="'.get_home_url().'">'.get_bloginfo('name').'</a>'.$privacy.'</p><p class="font-small">Built with ♡ by <a href="https://jakevan.ca">Jake Van</a></p>');
 //
 //	$aOpen = '<a href="https://legacytattooremoval.ca" title="Learn more about Legacy Tattoo Removal">';
 //	$options = array(
diff --git a/inc/helpers/media.php b/inc/helpers/media.php
index 4346f65..11ec253 100644
--- a/inc/helpers/media.php
+++ b/inc/helpers/media.php
@@ -9,13 +9,13 @@
     ?>
     <dialog class="gallery" aria-modal="true" aria-label="Image Gallery">
 		<div class="wrap">
-			<button type="button" class="cancel" aria-label="Close Gallery"> <?= jvbIcon('close') ?></button>
+			<button type="button" class="cancel" aria-label="Close Gallery"> <?= jvbIcon('x') ?></button>
 			<button class="nav prev row" aria-label="Previous image">
-				<?= jvbIcon('prev') ?>
+				<?= jvbIcon('caret-left') ?>
 			</button>
 
 			<button class="nav next row" aria-label="Next image">
-				<?= jvbIcon('next') ?>
+				<?= jvbIcon('caret-right') ?>
 			</button>
 
 			<div class="content row">
diff --git a/inc/helpers/members.php b/inc/helpers/members.php
index 007210e..e4d8386 100644
--- a/inc/helpers/members.php
+++ b/inc/helpers/members.php
@@ -208,13 +208,13 @@
             return 'admin';
         }
     }
-    $user = ($ID === 0) ? wp_get_current_user() : get_userdata($ID);
-
-	if (user_can($user, 'manage_options')) {
+	if ($ID > 0 && user_can($ID, 'manage_options')) {
 		return 'admin';
 	}
+    $user = ($ID === 0) ? wp_get_current_user() : get_userdata($ID);
+	error_log('Current User: '.print_r($user, true));
     return array_values(array_intersect(
-        array_keys(JVB_USER),
+        array_keys(array_merge(JVB_USER, ['administrator'])),
         array_map(function ($role) {
             return jvbNoBase($role);
         },
diff --git a/inc/helpers/renderFields.php b/inc/helpers/renderFields.php
index 16bb348..8786204 100644
--- a/inc/helpers/renderFields.php
+++ b/inc/helpers/renderFields.php
@@ -79,41 +79,41 @@
             $url = $link['url'];
             switch (true) {
                 case strpos($url, 'facebook'):
-                    $i = jvbIcon('facebook');
+                    $i = jvbIcon('facebook-logo');
                     break;
                 case strpos($url, 'instagram'):
-                    $i = jvbIcon('instagram');
+                    $i = jvbIcon('instagram-logo');
                     break;
                 case strpos($url, 'tiktok'):
-                    $i = jvbIcon('tiktok');
+                    $i = jvbIcon('tiktok-logo');
                     break;
                 case strpos($url, 'x.com'):
                     $i = jvbIcon('x');
                     break;
                 case strpos($url, 'youtube'):
-                    $i = jvbIcon('youtube');
+                    $i = jvbIcon('youtube-logo');
                     break;
                 case strpos($url, 'mastadon'):
-                    $i = jvbIcon('mastadon');
+                    $i = jvbIcon('mastadon-logo');
                     break;
                 case strpos($url, 'fediverse'):
                 case strpos($url, 'bluesky'):
-                    $i = jvbIcon('fediverse');
+                    $i = jvbIcon('fediverse-logo');
                     break;
                 case strpos($url, 'linktree'):
-                    $i = jvbIcon('linktree');
+                    $i = jvbIcon('linktree-logo');
                     break;
                 case strpos($url, 'snapchat'):
-                    $i = jvbIcon('snapchat');
+                    $i = jvbIcon('snapchat-logo');
                     break;
                 case strpos($url, 'twitch'):
-                    $i = jvbIcon('twitch');
+                    $i = jvbIcon('twitch-logo');
                     break;
                 case strpos($url, 'threads'):
-                    $i = jvbIcon('threads');
+                    $i = jvbIcon('threads-logo');
                     break;
                 case strpos($url, 'twitter'):
-                    $i = jvbIcon('twitter');
+                    $i = jvbIcon('twitter-logo');
                     break;
                 default:
                     $i = jvbIcon('link');
@@ -158,7 +158,7 @@
             switch ($p) {
                 case 'text':
                     $link = 'sms:+1'.$phone.';?body='.rawurlencode('Hey! I found you on edmonton.ink, and I wanted to reach out.');
-                    $label = jvbIcon('text').'<span>Text</span>';
+                    $label = jvbIcon('chat').'<span>Text</span>';
                     break;
                 case 'call':
                     $link = 'tel:+1'.$phone;
@@ -167,7 +167,7 @@
                 case 'email':
                     $link = 'mailto:'.$meta->getValue('email').'?subject='.rawurlencode('Contact from edmonton.ink').'&body='.rawurlencode('Hey,
         I found you on edmonton.ink, and I wanted to reach out!');
-                    $label = jvbIcon('email').'<span>Email</span>';
+                    $label = jvbIcon('envelope').'<span>Email</span>';
                     break;
             }
             if ($link) {
@@ -373,11 +373,11 @@
 					</div>
 					<div class="group-actions">
 						<button type="button" data-action="add-to-group" title="Add selected uploads to this group">
-							<?= jvbIcon('add') ?>
+							<?= jvbIcon('plus-square') ?>
 							Add Here
 						</button>
 						<button type="button" data-action="delete-group" title="Delete group">
-							<?= jvbIcon('delete') ?>
+							<?= jvbIcon('trash') ?>
 							Delete Group
 						</button>
 					</div>
@@ -407,7 +407,7 @@
 				</div>
 
 				<button type="button" data-action="remove-from-group" title="Remove from Group">
-					<?=jvbIcon('delete')?>
+					<?=jvbIcon('trash')?>
 				</button>
 			</div>
 		</template>
@@ -418,7 +418,7 @@
 				</div>
 				<div class="selection-controls">
 					<button type="button" data-action="add-to-group">
-						<?= jvbIcon('add') ?>
+						<?= jvbIcon('plus-square') ?>
 						New Group
 					</button>
 				</div>
@@ -472,11 +472,11 @@
 						</div>
 						<div class="m-actions row nowrap">
 							<button type="button" data-action="restore">
-								<?= jvbIcon('restore') ?>
+								<?= jvbIcon('arrow-counter-clockwise') ?>
 								Restore Selected
 							</button>
 							<button type="button" data-action="clear-cache" title="Clear cache and close window">
-								<?= jvbIcon('close') ?>
+								<?= jvbIcon('x') ?>
 								Clear Cache
 							</button>
 						</div>
diff --git a/inc/helpers/terms.php b/inc/helpers/terms.php
index 6825ae5..77f8fec 100644
--- a/inc/helpers/terms.php
+++ b/inc/helpers/terms.php
@@ -81,6 +81,10 @@
 add_action('save_post', 'jvbClearListCache', 10, 2);
 function jvbClearListCache(int $post_id, \WP_Post $post):void
 {
+	// SAFETY: Skip attachments and other non-content post types
+	if (in_array($post->post_type, jvbIgnoredPostTypes())) {
+		return;
+	}
 	if (jvbNoSaveIt($post_id, $post)) {
 		return;
 	}
diff --git a/inc/helpers/ui.php b/inc/helpers/ui.php
index a9e310d..85f276d 100644
--- a/inc/helpers/ui.php
+++ b/inc/helpers/ui.php
@@ -21,7 +21,7 @@
 			<div class="refresh row btw">
                 <span class="countdown row" title="Will refresh again...">5</span>
                 <button class="refreshNow row" title="Check now">
-                    <?= jvbIcon('refresh', ['title'=> 'Check now']) ?>
+                    <?= jvbIcon('arrows-clockwise', ['title'=> 'Check now']) ?>
                 </button>
             </div>
             <div class="popup row"><span></span></div>
@@ -62,7 +62,7 @@
 		</div>
     </aside>
 	<button class="qtoggle row" title="Show Queue" aria-controls="queue" hidden>
-		<?= jvbIcon('save') ?>
+		<?= jvbIcon('floppy-disk') ?>
 		<span class="screen-reader-text"></span>
 		<span class="indicator"></span>
 		<span class="count row"></span>
@@ -111,7 +111,7 @@
         ?>
         <li>
             <a href="<?=get_home_url(null, '/dash/')?>" title="Behind the Scenes">
-                <?= jvbIcon('dashboard', ['title' => 'Behind the Scenes'])?>
+                <?= jvbIcon('door', ['title' => 'Behind the Scenes'])?>
                 <span class="screen-reader-text">Go Behind the Scenes</span>
             </a>
         </li>
@@ -180,7 +180,7 @@
         }
 
         $out = '<li class="has-submenu">
-                <button class="toggle quick-help" type="button" title="Toggle Quick Help Menu" aria-expanded="false" aria-controls="quick-help" aria-label="Toggle Quick Help Menu">'.jvbIcon('help', ['title'=> 'Quick Help']).'</button>
+                <button class="toggle quick-help" type="button" title="Toggle Quick Help Menu" aria-expanded="false" aria-controls="quick-help" aria-label="Toggle Quick Help Menu">'.jvbIcon('question', ['title'=> 'Quick Help']).'</button>
                 <ul class="submenu">
                     '.$links.'
                 </ul>
@@ -190,9 +190,9 @@
     }
 
     if (is_user_logged_in()) {
-        $out .= '<li><a href="'.wp_logout_url(get_home_url()).'" title="Logout">'.jvbIcon('logout').'<span class="screen-reader-text">Logout</span></a></li>';
+        $out .= '<li><a href="'.wp_logout_url(get_home_url()).'" title="Logout">'.jvbIcon('sign-out').'<span class="screen-reader-text">Logout</span></a></li>';
     } else {
-        $out .= '<li><a href="'.wp_login_url(null, true).'" title="Login">'.jvbIcon('login').'<span class="screen-reader-text">Login</span></a></li>';
+        $out .= '<li><a href="'.wp_login_url(null, true).'" title="Login">'.jvbIcon('sign-in').'<span class="screen-reader-text">Login</span></a></li>';
     }
     return $out;
 }
@@ -213,7 +213,7 @@
     aria-label="Clear search"
     onclick="this.previousElementSibling.value = \'\'; this.previousElementSibling.focus();"
   >'.jvbIcon('x', ['title'=> 'Clear Search']).'</button>
-  <button type="button" title="Search" class="toggle search" aria-label="Toggles search input visually" onclick="this.parentNode.classList.toggle(\'open\');this.previousElementSibling.previousElementSibling.focus();">'.jvbIcon('search').'</button>
+  <button type="button" title="Search" class="toggle search" aria-label="Toggles search input visually" onclick="this.parentNode.classList.toggle(\'open\');this.previousElementSibling.previousElementSibling.focus();">'.jvbIcon('magnifying-glass').'</button>
 </div>';
 }
 
@@ -222,8 +222,8 @@
 function jvbModalActions()
 {
     return '<div class="m-actions row">
-        <button type="button" class="cancel">'.jvbIcon('close').'<span class="screen-reader-text">Cancel</span></button>
-        <button type="submit" class="save">'.jvbIcon('save').'<span class="screen-reader-text">Save</span></button>
+        <button type="button" class="cancel">'.jvbIcon('x').'<span class="screen-reader-text">Cancel</span></button>
+        <button type="submit" class="save">'.jvbIcon('floppy-disk').'<span class="screen-reader-text">Save</span></button>
     </div>';
 }
 
@@ -278,7 +278,7 @@
             <div class="toast-content row btw">
                 <p></p>
                 <button type="button" class="close-toast" aria-label="Close">
-                    <?= jvbIcon('close') ?>
+                    <?= jvbIcon('x') ?>
                 </button>
             </div>
         </div>
@@ -304,13 +304,13 @@
     <nav id="<?=$id?>" class="on-this-page index">
         <label>Jump to:
             <button type="button" aria-label="Show Index" title="Show Index" class="toggle" aria-expanded="false">
-                <?= jvbIcon('add') ?>
+                <?= jvbIcon('plus-square') ?>
             </button>
         </label>
         <ul>
             <li>
                 <a href="#top" title="Back to Top">
-                    <?= jvbIcon('up') ?>
+                    <?= jvbIcon('caret-circle-up') ?>
                 </a>
             </li>
             <?php
@@ -336,7 +336,7 @@
 		<div class="col">
 			<div class="spinner"></div>
 			<div class="status col">
-				<div class="icon">'.apply_filters('jvbLoadingIcon', jvbIcon('tattoo')).'</div>
+				<div class="icon">'.apply_filters('jvbLoadingIcon', jvbIcon('drop-simple')).'</div>
 				<h3>Loading</h3>
 				<p class="typeText">Please wait...</p>
 			</div>
@@ -449,7 +449,7 @@
 					<span>Start Over</span>
 				</button>
 				<button type="button" data-action="dismiss-restore">
-					'.jvbIcon('close').'
+					'.jvbIcon('x').'
 					<span>Dismiss</span>
 				</button>
 			</div>
diff --git a/inc/integrations/Facebook.php b/inc/integrations/Facebook.php
index e4e3a96..cfaa600 100644
--- a/inc/integrations/Facebook.php
+++ b/inc/integrations/Facebook.php
@@ -563,7 +563,7 @@
 	/**
 	 * Helper: Get user's Facebook pages
 	 */
-	private function getUserPages(string $access_token = null): array
+	private function getUserPages(?string $access_token = null): array
 	{
 		$token = $access_token ?: $this->credentials['access_token'];
 
diff --git a/inc/integrations/GoogleMyBusiness.php b/inc/integrations/GoogleMyBusiness.php
index f6541cc..3260b8c 100644
--- a/inc/integrations/GoogleMyBusiness.php
+++ b/inc/integrations/GoogleMyBusiness.php
@@ -10,9 +10,12 @@
 
 class GoogleMyBusiness extends Integrations
 {
-	private string $access_token;
+	private ?string $access_token = null;
 	protected string $readMask = 'name,title,storefrontAddress,metadata,openInfo,storeCode,categories,phoneNumbers,labels,specialHours';
 	private ?string $location = null;
+	private ?string $refresh_token = null;
+	private ?string $client_id = null;
+	private ?string $client_secret = null;
 	private ?string $account_id = null;
 
 	public function __construct(?int $userID = null)
@@ -134,12 +137,12 @@
 		if (empty($this->credentials)) {
 			$this->loadCredentials();
 		}
-		$this->access_token = $this->credentials['access_token'] ?? '';
-		$this->refresh_token = $this->credentials['refresh_token'] ?? '';
-		$this->client_id = $this->credentials['client_id'] ?? '';
-		$this->client_secret = $this->credentials['client_secret'] ?? '';
-		$this->location = $this->credentials['location'] ?? null;
-		$this->account_id = $this->credentials['account'] ?? null;
+		$this->access_token = (array_key_exists('access_token', $this->credentials)) ? $this->credentials['access_token'] : null;
+		$this->refresh_token = (array_key_exists('refresh_token', $this->credentials)) ? $this->credentials['refresh_token'] : null;
+		$this->client_id = (array_key_exists('client_id', $this->credentials)) ? $this->credentials['client_id'] : null;
+		$this->client_secret = (array_key_exists('client_secret', $this->credentials)) ? $this->credentials['client_secret'] : null;
+		$this->location = (array_key_exists('location', $this->credentials)) ? $this->credentials['location'] : null;
+		$this->account_id = (array_key_exists('account', $this->credentials)) ? $this->credentials['account'] : null;
 
 		if ($this->account_id) {
 			$this->apiEndpoints[] = "/v1/{$this->account_id}/locations";
diff --git a/inc/integrations/Helcim.php b/inc/integrations/Helcim.php
index 4288820..eabff67 100644
--- a/inc/integrations/Helcim.php
+++ b/inc/integrations/Helcim.php
@@ -240,7 +240,7 @@
 		}
 		?>
 		<button type="button" class="toggle-cart row" title="Your Cart" data-action="toggle-cart" aria-label="Open Cart" aria-controls="checkout" aria-expanded="false" hidden>
-			<?= jvbIcon('cart')?><span class="abs"></span><span class="abs count"></span>
+			<?= jvbIcon('shopping-cart')?><span class="abs"></span><span class="abs count"></span>
 		</button>
 		<aside id="cart">
 			<form id="checkout" data-form-id="checkout" data-save="checkout">
@@ -461,7 +461,7 @@
 					<label for="quantity"></label>
 					<div class="quantity field" data-min="0" data-max="50" data-step="1" data-price="" data-id="">
 						<button type="button" class="decrease" aria-label="Decrease quantity">
-							<?= jvbIcon('minus') ?>
+							<?= jvbIcon('minus-square') ?>
 						</button>
 						<input type="number" name="quantity" value="1" min="0" max="50">
 						<button type="button" class="increase" aria-label="Increase quantity">
diff --git a/inc/integrations/Integrations.php b/inc/integrations/Integrations.php
index e3ef859..9510af5 100644
--- a/inc/integrations/Integrations.php
+++ b/inc/integrations/Integrations.php
@@ -3073,7 +3073,7 @@
 					switch ($action) {
 						case 'save_credentials':
 							$title = $label;
-							$label = jvbIcon('save');
+							$label = jvbIcon('floppy-disk');
 							break;
 						case 'clear_credentials':
 							$title = $label;
diff --git a/inc/integrations/PostMark.php b/inc/integrations/PostMark.php
index 41f467c..baac1bc 100644
--- a/inc/integrations/PostMark.php
+++ b/inc/integrations/PostMark.php
@@ -15,6 +15,12 @@
  */
 class PostMark extends Integrations
 {
+	protected ?string $server_token = null;
+	protected string $message_stream = 'outbound';
+	protected string $from_email;
+	protected string $from_name;
+	protected bool $track_open;
+	protected bool $track_links;
 	/**
 	 * Constructor
 	 */
@@ -22,9 +28,10 @@
 	{
 		$this->service_name = 'postmark';
 		$this->title = 'PostMark';
-		$this->description = 'Professional transactional email delivery service';
 		$this->icon = 'envelope-simple';
 
+		$this->cacheName = ($userID)? 'gmb_'.$userID : 'postmark';
+
 		// PostMark API configuration
 		$this->apiBase = 'https://api.postmarkapp.com';
 		$this->apiVersion = 'v1';
@@ -48,12 +55,13 @@
 		];
 
 		// Required credentials
-		$this->requiredCredentials = [
+		$this->fields = [
 			'server_token' => [
 				'label' => 'Server API Token',
-				'type' => 'password',
+				'type' => 'text',
+				'subtype'=> 'password',
 				'placeholder' => 'xxxxxxxx-xxxx-xxxx-xxxx-xxxxxxxxxxxx',
-				'description' => 'Your PostMark Server API Token'
+				'hint' => 'Your PostMark Server API Token'
 			],
 			'message_stream' => [
 				'label' => 'Message Stream ID',
@@ -101,7 +109,7 @@
 		parent::__construct($userID);
 
 		// Hook into WordPress mail system if integration is healthy
-		if ($this->is_healthy && $this->hasValidCredentials()) {
+		if ($this->is_healthy && $this->isSetUp()) {
 			$this->initializeMailHooks();
 		}
 	}
@@ -271,7 +279,7 @@
 			return true;
 
 		} catch (\Exception $e) {
-			$this->handleException($e, 'email');
+			$this->logError('[POSTMARK]', ['method' => 'sendEmail', 'error' => $e->getMessage()]);
 			return new WP_Error(
 				'postmark_send_failed',
 				$e->getMessage()
@@ -459,22 +467,15 @@
 	/**
 	 * Override makeRequest to add PostMark headers
 	 */
-	protected function makeRequest(
-		string $method,
-		string $endpoint,
-		array $params = [],
-		array $headers = []
-	): mixed {
+	protected function getRequestHeaders(): array {
 		$credentials = $this->getCredentials();
 
 		// Add PostMark specific headers
-		$headers = array_merge([
+		return [
 			'Accept' => 'application/json',
 			'Content-Type' => 'application/json',
 			'X-Postmark-Server-Token' => $credentials['server_token'] ?? ''
-		], $headers);
-
-		return parent::makeRequest($method, $endpoint, $params, $headers);
+		];
 	}
 
 	/**
@@ -500,7 +501,7 @@
 				return $stats;
 			}
 		} catch (\Exception $e) {
-			$this->handleException($e, 'stats/outbound');
+			$this->logError('stats/outbound', ['error' => $e->getMessage()]);
 		}
 
 		return [];
@@ -517,7 +518,7 @@
 				'offset' => $offset
 			]);
 		} catch (\Exception $e) {
-			$this->handleException($e, 'bounces');
+			$this->logError('[POSTMARK]', ['method' => 'getBounces', 'error' => $e->getMessage()]);
 			return [];
 		}
 	}
@@ -634,11 +635,14 @@
 
 	protected function initialize(): void
 	{
-		// TODO: Implement initialize() method.
-	}
-
-	protected function getRequestHeaders(): array
-	{
-		// TODO: Implement getRequestHeaders() method.
+		if (empty($this->credentials)) {
+			$this->loadCredentials();
+		}
+		$this->server_token = (array_key_exists('server_token', $this->credentials)) ? $this->credentials['server_token'] : null;
+		$this->from_email = (array_key_exists('from_email', $this->credentials)) ? $this->credentials['from_email'] : get_bloginfo('admin_email');
+		$this->from_name = (array_key_exists('from_name', $this->credentials)) ? $this->credentials['from_name'] : get_bloginfo('name');
+		$this->message_stream = (array_key_exists('message_stream', $this->credentials)) ? $this->credentials['message_stream'] : 'outbound';
+		$this->track_open = (array_key_exists('track_open', $this->credentials)) ? $this->credentials['track_open'] : false;
+		$this->track_links = (array_key_exists('track_links', $this->credentials)) ? $this->credentials['track_links'] : false;
 	}
 }
diff --git a/inc/integrations/Square.php b/inc/integrations/Square.php
index ca3a7aa..62bbeb4 100644
--- a/inc/integrations/Square.php
+++ b/inc/integrations/Square.php
@@ -949,11 +949,11 @@
 					<label for="quantity"></label>
 					<div class="quantity field" data-min="0" data-max="50" data-step="1" data-price="17" data-id="">
 
-						<button type="button" class="decrease"aria-label="Decrease Add to Order">'.jvbIcon('minus').'</button>
+						<button type="button" class="decrease"aria-label="Decrease Add to Order">'.jvbIcon('minus-square').'</button>
 
 						<input type="number" id="quantity" name="quantity" value="0" min="0" max="50" step="1" class="quantity-input">
 
-						<button type="button" class="increase" aria-label="Increase Add to Order">'.jvbIcon('add').'</button>
+						<button type="button" class="increase" aria-label="Increase Add to Order">'.jvbIcon('plus-square').'</button>
 					</div>
 				</td>
 				<td class="price">
@@ -977,7 +977,7 @@
 
 		$actions[] = [
 			'button' => 	'<button type="button" class="toggle-cart row" title="Your Cart" data-action="toggle-cart" aria-label="Open Cart" aria-controls="checkout" aria-expanded="false">
-					'.jvbIcon('cart').'<span class="abs"></span><span class="abs count"></span>
+					'.jvbIcon('shopping-cart').'<span class="abs"></span><span class="abs count"></span>
 				</button>',
 			'content' =>	$form
 		];
diff --git a/inc/managers/AdminPages.php b/inc/managers/AdminPages.php
index a511b78..d0a1f8c 100644
--- a/inc/managers/AdminPages.php
+++ b/inc/managers/AdminPages.php
@@ -417,7 +417,7 @@
                 <a data-action="<?=$action['slug']?>" class="jvb-action">
                     <?= jvbIcon($action['icon']); ?>
                     <span class="jvb-link-title"><?= esc_html($action['label'])?></span>
-                    <span class="loader"><?=jvbIcon('refresh')?><?=jvbIcon('check')?></span>
+                    <span class="loader"><?=jvbIcon('arrows-clockwise')?><?=jvbIcon('check')?></span>
                 </a>
                 <?php
             }
diff --git a/inc/managers/CRUDManager.php b/inc/managers/CRUDManager.php
index 37b3a59..aa4293d 100644
--- a/inc/managers/CRUDManager.php
+++ b/inc/managers/CRUDManager.php
@@ -28,6 +28,13 @@
 	protected array $sections;
 	protected array $stuck;
 
+
+	//For Timeline-specific posts
+	protected bool $isTimeline = false;
+	protected array $nonTimelineFields = [];
+	protected array $timelineSharedFields = [];
+	protected array $timelineUniqueFields = [];
+
 	protected bool $userCanPublish = false;
 
 	public function __construct(string $content)
@@ -43,6 +50,7 @@
 		$this->plural = $this->config['plural'];
 		$this->content = $content;
 		$this->fields = jvbGetFields($this->content, 'post');
+		$this->maybeSetupTimeline();
 		$this->sections = jvbGetSections($this->content, 'post');
 		$this->stuck = [
 			'post_title',
@@ -67,6 +75,34 @@
 
 	}
 
+	protected function maybeSetupTimeline():void {
+		$this->isTimeline = Features::forContent($this->content)->has('is_timeline');
+
+		if (!$this->isTimeline) {
+			return;
+		}
+		$this->timelineSharedFields = array_keys(array_filter($this->fields, function ($field) {
+			if (!array_key_exists('for_all', $field) || $field['for_all'] === false){
+				return true;
+			}
+			return false;
+		}));
+		array_unshift($this->timelineSharedFields, 'post_thumbnail');
+		array_unshift($this->timelineSharedFields, 'post_title');
+
+		$this->timelineUniqueFields = array_keys(array_filter($this->fields, function ($field) {
+			if (array_key_exists('for_all', $field) && $field['for_all'] === true) {
+				return true;
+			}
+			return false;
+		}));
+
+		$all = array_merge($this->timelineUniqueFields, $this->timelineSharedFields);
+		$this->nonTimelineFields = array_filter($this->fields, function ($field) use ($all) {
+			return !in_array($field, $all);
+		}, ARRAY_FILTER_USE_KEY);
+	}
+
 	protected function initTaxonomies():void
 	{
 		$this->taxonomies = array_filter(JVB_TAXONOMY, function ($config) {
@@ -84,41 +120,41 @@
 				],
 				'future'=> [
 					'label'	=> 'Upcoming',
-					'icon'	=> 'future',
+					'icon'	=> 'clock-clockwise',
 				],
 				'past'	=> [
 					'label'	=> 'Past',
-					'icon'	=> 'past',
+					'icon'	=> 'clock-counter-clockwise',
 				],
 				'repeat'=> [
 					'label'	=> 'Recurring',
 					'icon'	=> 'repeat',
 				],
 				'draft'	=> [
-					'icon'	=> 'hide',
+					'icon'	=> 'eye-closed',
 					'label'	=> 'Hidden',
 				],
 				'trash'	=> [
 					'label'	=> 'Scrapped',
-					'icon'	=> 'delete',
+					'icon'	=> 'trash',
 				],
 			] :
 			[
 				'all'	=> [
-					'icon'  => 'all',
+					'icon'  => 'infinity',
 					'label' => 'Everything',
 				],
 				'publish'=> [
-					'icon'	=> 'publish',
+					'icon'	=> 'eye',
 					'label'	=> 'Live',
 				],
 				'draft'	=> [
-					'icon'	=> 'hide',
+					'icon'	=> 'eye-closed',
 					'label'	=> 'Hidden',
 				],
 				'trash'	=> [
 					'label'	=> 'Scrapped',
-					'icon'	=> 'delete',
+					'icon'	=> 'trash',
 				],
 			];
 	}
@@ -147,7 +183,7 @@
 		foreach ($this->taxonomies as $taxonomy=> $config) {
 			$this->filters['taxonomy'][$taxonomy] = [
 				'label'	=> $config['singular'],
-				'icon'	=> $taxonomy
+				'icon'	=> $config['icon']??'folder'
 			];
 		}
 	}
@@ -248,9 +284,9 @@
 		ob_start();
 		?>
 		<div class="empty-state">
-			<h3><?=jvbIcon($this->content)?>Nothing here<?=jvbIcon($this->content)?></h3>
+			<h3><?=jvbIcon($this->config['icon'])?>Nothing here<?=jvbIcon($this->config['icon'])?></h3>
 			<p>It doesn't look like you have any <?=$this->config['plural'] ?> yet.</p>
-			<p><small><i>Add many by uploading images above.</i>, or click the "<?=jvbIcon('add')?>" button to add one at a time.</small></p>
+			<p><small><i>Add many by uploading images above.</i>, or click the "<?=jvbIcon('plus-square')?>" button to add one at a time.</small></p>
 		</div>
 		<?php
 		return ob_get_clean();
@@ -278,7 +314,7 @@
 					$this->renderDateFilters();
 				?>
 				<button type="button" class="clear-filters row" hidden>
-					<?= jvbIcon('close', ['title'    => 'Clear']); ?>
+					<?= jvbIcon('x', ['title'    => 'Clear']); ?>
 					Clear All Filters
 				</button>
 			</div>
@@ -295,12 +331,12 @@
 			<?php
 				$order = [
 					'orderby' => [
-						'date' => 'Order by date created',
+						'calendar' => 'Order by date created',
 						'alphabetical' => 'Order alphabetically'
 					],
 					'order' => [
-						'asc' => 'In ascending order (Z-A, oldest to newest)',
-						'desc' => 'In descending order (A-Z, newest to oldest)'
+						'sort-ascending' => 'In ascending order (Z-A, oldest to newest)',
+						'sort-descending' => 'In descending order (A-Z, newest to oldest)'
 					]
 				];
 
@@ -360,8 +396,8 @@
 
 			<?php
 			$views = [
-				'grid' => ['icon' => 'grid', 'label' => 'Grid View'],
-				'list' => ['icon' => 'list', 'label' => 'List View'],
+				'grid' => ['icon' => 'squares-four', 'label' => 'Grid View'],
+				'list' => ['icon' => 'rows', 'label' => 'List View'],
 				'table' => ['icon' => 'table', 'label' => 'Table View']
 			];
 
@@ -605,7 +641,7 @@
 	{
 		ob_start();
 		?>
-		<form class="edit-form" data-save="content" data-form-id="edit-<?=$this->content?>">
+		<form class="edit-form" data-save="content" data-form-id="edit-<?=$this->content?>" data-autosave<?= ($this->isTimeline) ? ' data-timeline' : ''?>>
 			<?= jvbFormStatus() ?>
 			<input type="hidden" name="form-id" value="<?=uniqid('new-')?>" />
 			<input type="hidden" name="content" value="<?=$this->content?>" />
@@ -637,11 +673,9 @@
 					$tabs = false;
 				}
 
-				$isTimeline = Features::forContent($this->content)->has('is_timeline');
-
 
 				$fields = $this->fields;
-				if (!$isTimeline) {
+				if (!$this->isTimeline) {
 					$first = ['post_thumbnail', 'post_title', 'price'];
 
 					foreach ($first as $f) {
@@ -657,13 +691,11 @@
 					}
 				}
 
-				if ($isTimeline) {
+				if ($this->isTimeline) {
 					$temp = array_filter($fields, function ($field) {
-						if (array_key_exists('for_all', $field) && $field['for_all'] === true) {
-							return true;
-						}
-						return false;
-					});
+						return in_array($field, $this->timelineUniqueFields);
+					}, ARRAY_FILTER_USE_KEY);
+
 					$config = [
 						'type'		=> 'gallery',
 						'subtype'	=> 'timeline',
@@ -673,21 +705,16 @@
 					];
 					$content = '';
 					foreach ($fields as $slug=> $field) {
-						if (!array_key_exists('for_all', $field) || $field['for_all'] === false) {
+						if (in_array($slug, $this->timelineSharedFields)) {
 							$content .= $this->form->render($slug, null, $field, false, true);
 						}
 					}
 
+
 					$content .= $this->meta->render('form', 'timeline', $config, false,true);
 
 					$tabs['progression']['content'] = $content;
-					$this->fields = array_filter($fields, function ($field) {
-						if (array_key_exists('for_all', $field) && $field['for_all'] === true) {
-							return false;
-						}
-						return true;
-					});
-					$fields = $this->fields;
+					$fields = $this->nonTimelineFields;
 				}
 				foreach ($fields as $n => $config) {
 					if ($tabs) {
@@ -731,7 +758,7 @@
 			</div>
 
 			<button type="button" class="add-repeater-row btn secondary">
-				<?= jvbIcon('add') ?>
+				<?= jvbIcon('plus-square') ?>
 				<span>Add Progress Step</span>
 			</button>
 		</div>
@@ -769,7 +796,7 @@
 				if ($status === 'future') {
 					$status = 'publish';
 					$config = [
-						'icon'	=> 'publish',
+						'icon'	=> 'eye',
 						'label'	=> 'Live',
 					];
 				} else {
@@ -870,16 +897,13 @@
 		$this->renderGridView();
 		$this->renderTableView();
 		$this->renderTableRow();
-		if (Features::forContent($this->content)->has('is_timeline')) {
+		if ($this->isTimeline) {
 			$temp = array_filter($this->fields, function ($field) {
-				if (array_key_exists('for_all', $field) && $field['for_all'] === true) {
-					return true;
-				}
-				return false;
-			});
+				return in_array($field, $this->timelineUniqueFields);
+			}, ARRAY_FILTER_USE_KEY);
 			$form = new MetaForm();
-			echo '<template class="uploadTimeline">';
-			$form->renderImagePreview(null,$temp);
+			echo '<template class="timelineItem">';
+			$form->renderImagePreview(null,['fields' => $temp]);
 			echo '</template>';
 		}
 		echo jvbGetEmptyStateTemplate();
@@ -917,11 +941,11 @@
 		?>
 		<div class="item-actions">
 			<button type="button" class="action" data-action="edit" title="Edit <?= $this->singular ?>">
-				<?=jvbIcon('edit')?>
+				<?=jvbIcon('pencil-simple')?>
 				<span class="screen-reader-text">Edit <?= $this->singular ?></span>
 			</button>
 			<button type="button" class="action" data-action="trash" title="Scrap <?= $this->singular ?>">
-				<?=jvbIcon('delete')?>
+				<?=jvbIcon('trash')?>
 				<span class="screen-reader-text">Scrap <?= $this->singular ?></span>
 			</button>
 <!--			<button type="button" class="action" data-action="toggle-status">-->
@@ -1060,7 +1084,7 @@
 				if ($status === 'future') {
 					$status = 'publish';
 					$config = [
-						'icon' => 'publish',
+						'icon' => 'eye',
 						'label' => 'Live'
 					];
 				} elseif ($status === 'past') {
@@ -1118,12 +1142,12 @@
 				'vertical',
 				'TAB NAV:',
 				'',
-				jvbIcon('down'),
-				jvbIcon('right')
+				jvbIcon('caret-double-down'),
+				jvbIcon('caret-double-right')
 			) ?>
 
 			<button type="button" class="add-row" title="Add new row">
-				<?= jvbIcon('add') ?>
+				<?= jvbIcon('plus-square') ?>
 				<span>Add Row</span>
 			</button>
 		</div>
@@ -1137,7 +1161,7 @@
 		$this->renderCreateModal();
 		$content = ob_get_clean();
 		$create = [
-			'button'	=> '<button type="button" class="create-item row" title="Create New '.$this->singular.'">'.jvbIcon('add').'<span class="screen-reader-text">Create New '.$this->singular.'</span></button>',
+			'button'	=> '<button type="button" class="create-item row" title="Create New '.$this->singular.'">'.jvbIcon('plus-square').'<span class="screen-reader-text">Create New '.$this->singular.'</span></button>',
 			'content'	=> $content,
 		];
 		$actions[] = $create;
diff --git a/inc/managers/DashboardManager.php b/inc/managers/DashboardManager.php
index 276d3c1..10a8a4a 100644
--- a/inc/managers/DashboardManager.php
+++ b/inc/managers/DashboardManager.php
@@ -23,13 +23,12 @@
     public function __construct()
     {
         $this->cache = CacheManager::for('dashboard', WEEK_IN_SECONDS);
-		$this->cache->invalidate();
         add_action('init', [$this, 'registerDashboard']);
         if (!$this->isRegistered()) {
             add_action('init', [$this, 'buildDashboard']);
         }
         $this->user = wp_get_current_user();
-        $this->role = jvbUserRole();
+        $this->role = jvbUserRole($this->user->ID);
         $this->userLink = (int)get_user_meta($this->user->ID, BASE.'link', true);
 
 
@@ -307,6 +306,12 @@
 
         // Get current page/section
         $page = $this->getCurrentPage();
+		$config = $this->getConfig($page);
+		if(!empty($config)) {
+			add_filter('jvbLoadingIcon', function() use ($config) {
+				return jvbIcon($config['icon']);
+			});
+		}
 		$integrationSlugs = array_map(function($name) {
 			return sanitize_title(str_replace('_', '-', $name));
 		}, array_keys(JVB()->getAvailableServices(false)));
@@ -316,13 +321,14 @@
 			// Pass along to the Integrations template handler which knows to check for subpages
 			$page = 'integrations';
 		}
-
-		echo $this->cache->remember(
-			$page,
-		 	function() use ($page) {
-				return $this->renderDashboard($page);
-		 	}
-		);
+		echo $this->renderDashboard($page);
+		//TODO: Reenable
+//		echo $this->cache->remember(
+//			$page,
+//		 	function() use ($page) {
+//				return $this->renderDashboard($page);
+//		 	}
+//		);
 
 		return '';
     }
@@ -614,8 +620,8 @@
             $title = ($checked == '') ? 'Toggle Dark Mode' : 'Toggle Light Mode';
             echo '<label title="'.$title.'" id="theme-switch" class="toggle-switch" for="theme-switcher">
                     <input class="theme-switch row" id="theme-switcher" type="checkbox"'.$checked.' data-setting="theme" data-theme role="switch" name="dark-mode"><span class="slider">'.
-					jvbIcon('light', ['title'=> 'Light Mode']).
-					jvbIcon('dark', ['title'=>'Dark Mode']).
+					jvbIcon('sun-dim', ['title'=> 'Light Mode']).
+					jvbIcon('moon', ['title'=>'Dark Mode']).
 					'</span></label>';
             ?>
             <p class="title">
@@ -630,7 +636,7 @@
 						}
 					}
 					if ($out == '') {
-						$out =jvbIcon('home');
+						$out =jvbIcon('house');
 					}
                     ?><?= $out ?>
                 </a>
@@ -659,7 +665,6 @@
                 <?php
                 $current_page = $this->getCurrentPage();
                 $pages = $this->getUserAllowedPages()?:[];
-				error_log('PageS: '.print_r($pages, true));
 
                 echo '<ul>';
                 foreach ($pages as $page) {
@@ -712,7 +717,7 @@
         echo '<p>Welcome back!</p>';
 
         $pages = $this->getUserAllowedPages();
-
+		error_log('Pages: '.print_r($pages, true));
 
         echo '<h2>What would you like to do today?</h2>';
 
@@ -722,7 +727,7 @@
 				continue;
 			}
             $title = $this->getTitle($page);
-            $url = sanitize_title($title);
+
             $description = $this->getDescription($page);
 			$icon = $page;
 			if (!is_numeric($slug)) {
@@ -730,10 +735,12 @@
 				if (array_key_exists('icon', $config)) {
 					$icon = $config['icon'];
 				}
+			} else {
+				$slug = sanitize_title($page);
 			}
             if ($title !== '') {
-                echo '<li><p><a href="'.get_home_url(null, '/dash/'.$url.'/').'"
-                    data-page="'.$url.'" data-dash>'.jvbIcon($icon).ucwords($title).'</a></p></li>';
+                echo '<li><p><a href="'.get_home_url(null, '/dash/'.$slug.'/').'"
+                    data-page="'.$slug.'" data-dash>'.jvbIcon($icon).ucwords($title).'</a></p></li>';
             }
 
         }
@@ -781,7 +788,7 @@
 		if ($content !== '') {
 			echo $content;
 		} else {
-		jvbRenderSections($this->user->ID, 'user', jvbUserRole());
+			jvbRenderSections($this->user->ID, 'user', jvbUserRole());
 		}
 
     }
@@ -851,16 +858,16 @@
         <div class="approvals container">
             <nav class="tabs row start" role="tablist">
                 <button type="button" class="tab active" data-tab="summary" role="tab" aria-selected="true">
-                    <h2><?= jvbIcon('all')?>All</h2>
+                    <h2><?= jvbIcon('infinity')?>All</h2>
                 </button>
                 <button type="button" class="tab" data-tab="artists" role="tab" aria-selected="false">
-                    <h2><?= jvbIcon('artists')?>Artists</h2>
+                    <h2><?= jvbIcon('users-three')?>Artists</h2>
                 </button>
                 <button type="button" class="tab" data-tab="terms" role="tab" aria-selected="false">
-                    <h2><?= jvbIcon('style')?>Terms</h2>
+                    <h2><?= jvbIcon('hash')?>Terms</h2>
                 </button>
                 <button type="button" class="tab" data-tab="yours" role="tab" aria-selected="false">
-                    <h2><?= jvbIcon('artist')?>Yours</h2>
+                    <h2><?= jvbIcon('user')?>Yours</h2>
                 </button>
             </nav>
         </div>
@@ -964,8 +971,8 @@
             'vertical',
             'TAB NAV:',
             '',
-            jvbIcon('down'),
-            jvbIcon('right'))?>
+            jvbIcon('caret-double-down'),
+            jvbIcon('caret-double-right'))?>
 
     </div>
     <div class="items-container">
@@ -1024,18 +1031,18 @@
         <template class="<?= $type ?>Row">
             <tr>
                 <td>
-                     <?= jvbIcon('grab') ?>
+                     <?= jvbIcon('dots-six-vertical') ?>
                  </td>
                  <td data-id="actions" class="col">
                      <?= jvbRenderToggleTextField(
                          'public',
                          '',
                          '',
-                         jvbIcon('show'),
-                         jvbIcon('hide'))
+                         jvbIcon('eye'),
+                         jvbIcon('eye-closed'))
                      ?>
                      <button type="button" data-action="edit">
-                         <?= jvbIcon('edit') ?>
+                         <?= jvbIcon('pencil-simple') ?>
                     </button>
                 </td>
                 <?php
diff --git a/inc/managers/DirectoryManager.php b/inc/managers/DirectoryManager.php
index 6b5f4c9..a8b7a43 100644
--- a/inc/managers/DirectoryManager.php
+++ b/inc/managers/DirectoryManager.php
@@ -47,7 +47,7 @@
                 'not_found' => "No {$plural} found",
                 'not_found_in_trash' => "No {$plural} found in Trash"
             ],
-            'menu_icon'             => jvbCSSIcon('asc'),
+            'menu_icon'             => jvbCSSIcon('sort-ascending'),
             'public'                => true,
             'publicly_queryable'    => true,
             'show_in_menu'          => true,
diff --git a/inc/managers/EmailManager.php b/inc/managers/EmailManager.php
index e351a99..7f2315e 100644
--- a/inc/managers/EmailManager.php
+++ b/inc/managers/EmailManager.php
@@ -24,17 +24,15 @@
 class EmailManager
 {
 
-    // Brand colors
-    private string $action_color = '#FF0080';
-    private string $bg_light = '#F9F9F9';
-    private string $bg_dark = '#1B1B1B';
-    private string $text_color = '#151515';
-    private string $text_light = '#F9F9F9';
+   private array $colours = JVB_EMAIL['colours'];
+   private string $title = JVB_EMAIL['content']['title'];
+   private string $prefix = JVB_EMAIL['content']['subjectPrefix'];
+   private string $signature = JVB_EMAIL['content']['signature'];
+   private string $footer;
 
     // Site info
     private string $site_name;
     private string $site_url;
-    private string $signature;
 
     /**
      * Constructor - sets up all filters
@@ -43,7 +41,7 @@
     {
         $this->site_name = wp_specialchars_decode(get_option('blogname'), ENT_QUOTES);
         $this->site_url = get_site_url();
-        $this->signature = jvbSignature();
+		$this->footer = (is_array(JVB_EMAIL['content']['footer'])) ? implode('', JVB_EMAIL['content']['footer']) : JVB_EMAIL['content']['footer'];
 
         add_filter('wp_mail_content_type', [$this, 'setHtmlContentType']);
         // User registration emails
@@ -109,12 +107,11 @@
      */
     private function getEmailTemplate(string $content, string $header = ''):string
     {
-        // Get logo URL (replace with your actual logo path)
-        $logo_url = esc_url(wp_get_attachment_image_url(get_theme_mod('custom_logo'), 'medium'));
+        $logo = get_custom_logo();
 
         // Default header if none provided
         if (empty($header)) {
-            $header = 'edmonton.ink';
+            $header = $this->title;
         }
 
         return '<!DOCTYPE html>
@@ -122,17 +119,17 @@
         <head>
             <meta charset="UTF-8">
             <meta name="viewport" content="width=device-width, initial-scale=1.0">
-            <title>' . $this->site_name . '</title>
+            <title>' . $this->title . ' | ' . $this->site_name .'</title>
             <style>
                 body, table, td, p, a, li, blockquote {
                     line-height: 1.5;
                 }
 
                 body {
-                    background-color: ' . $this->bg_light . ';
+                    background-color: ' . $this->colours['light'] . ';
                     margin: 0;
                     padding: 0;
-                    color: ' . $this->text_color . ';
+                    color: ' . $this->colours['dark'] . ';
                 }
 
                 .email-container {
@@ -147,10 +144,10 @@
                 }
 
                 .header {
-                    background-color: ' . $this->bg_dark . ';
+                    background-color: ' . $this->colours['dark'] . ';
                     padding: 5px;
                     text-align: center;
-                    color: ' . $this->text_light . ';
+                    color: ' . $this->colours['light'] . ';
                     font-size: 24px;
                     font-weight: 900;
                     letter-spacing: 1.5px;
@@ -169,7 +166,7 @@
                 }
 
                 .content {
-                    background-color: #ffffff;
+                    background-color: ' . $this->colours['light-50'] . ';
                     padding: 30px;
                     border-bottom-left-radius: 5px;
                     border-bottom-right-radius: 5px;
@@ -178,8 +175,8 @@
                 .button {
                     display: inline-block;
                     padding: 12px 24px;
-                    background-color: ' . $this->action_color . ';
-                    color: ' . $this->text_light . ' !important;
+                    background-color: ' . $this->colours['action-0'] . ';
+                    color: ' . $this->colours['action-contrast'] . ' !important;
                     text-decoration: none;
                     border-radius: 3px;
                     font-weight: bold;
@@ -189,7 +186,7 @@
                 }
 
                 .text-link {
-                    color: ' . $this->action_color . ';
+                    color: ' . $this->colours['action-0'] . ';
                     text-decoration: underline;
                 }
 
@@ -197,13 +194,14 @@
                     text-align: center;
                     margin-top: 20px;
                     font-size: 12px;
-                    color: #666666;
+                    backgorund-color: ' . $this->colours['light-200'] . ';
+                    color: ' . $this->colours['dark-200'] . ';
                     padding: 10px;
                     line-height: 1.6;
                 }
 
                 .divider {
-                    border-top: 1px solid #dddddd;
+                    border-top: 1px solid ' . $this->colours['dark-200'] . ';
                     margin: 25px 0;
                 }
 
@@ -282,25 +280,23 @@
 
 /* Dark mode support for clients that support it */
 @media (prefers-color-scheme: dark) {
-    .email-container {
-        background-color: #1B1B1B !important;
+    .body {
+        background-color: ' . $this->colours['dark'] . ';
+        color: ' . $this->colours['light'] . ';
+    }
+
+    .header {
+    	background-color: ' . $this->colours['light'] . ';
+    	color: ' . $this->colours['dark'] . ';
     }
 
     .content {
-        background-color: #2A2A2A !important;
-        color: #F9F9F9 !important;
-    }
-
-    h1, h2, h3, p, li, a.text-link {
-        color: #F9F9F9 !important;
+    	background-color: ' . $this->colours['dark-50'] . ';
     }
 
     .footer {
-        color: #999999 !important;
-    }
-
-    .divider {
-        border-color: #444444 !important;
+    	background-color: ' . $this->colours['dark-200'] . ';
+    	color: ' . $this->colours['light-200'] . ';
     }
 }
             </style>
@@ -310,7 +306,7 @@
 
                 <div class="header">
                     <a href="' . $this->site_url . '">
-                        <img src="' . $logo_url . '" alt="Edmonton Ink">
+                        ' . $logo . '
                     </a>
                     <p>' . $header . '</p>
                 </div>
@@ -318,8 +314,7 @@
                     ' . $content . '
                 </div>
                 <div class="footer">
-                    <p>&copy; ' . date('Y') . ' edmonton.ink — Your tattoo scene on your screen.</p>
-                    <p><a href="' . $this->site_url . '" class="text-link">edmonton.ink</a></p>
+                    ' . $this->footer . '
                 </div>
             </div>
         </body>
@@ -331,54 +326,24 @@
      */
     public function customizeNewUserEmail($wp_new_user_notification_email, $user, $blogname)
     {
-        $user_login = $user->user_login;
+		$message = sprintf(
+			'<p>Hi %s!</p>
+					<p>Thanks for signing up for an account on %s.</p>
+					<p><b>Your username:</b> %s</p>
+					<p><b>Your password:</b> <i>Your chosen password.</i></p>',
+			$user->display_name,
+			get_bloginfo('name'),
+			$user->user_login,
+		);
+		$message = apply_filters('jvbNewUserEmail', $message, $user);
 
-        // Only create the password key if the user can change their password
-        $key = get_password_reset_key($user);
-        if (!is_wp_error($key)) {
-            $reset_url = network_site_url("wp-login.php?action=rp&key=$key&login=" . rawurlencode($user_login), 'login');
-
-            $message = sprintf('<p>Hey %s!</p>', $user->first_name);
-            $message .= '<p>Thanks for joining Edmonton\'s tattoo scene. Here\'s your login information:</p>';
-            $message .= sprintf('<p><strong>Username:</strong> %s</p>', $user_login);
-            $message .= '<p>To set your password and access your account, click the button below:</p>';
-            $message .= sprintf('<p style="text-align: center;"><a href="%s" class="button">Set Your Password</a></p>', $reset_url);
-            $message .= '<p>Or copy and paste this link into your browser:</p>';
-            $message .= sprintf('<p style="user-select:all;">%s</p>', $reset_url);
-            $message .= '<p>This link will expire in 24 hours, for security reasons.</p>';
-            if (in_array('jvb_artist', array_values($user->roles))) {
-                $message .= '<div class="divider"></div>';
-                $message .= '<h3>NOTE:</h3>
-                    <p>Once you set your password, you\'ll have access to your custom dashboard where you can:</p>
-                    <ul>
-                        <li>Manage your profile information</li>
-                        <li>Upload tattoos/piercings, and artwork</li>
-                    </ul>
-                    <p>Nothing will be published until you\'ve been approved by 3 already approved artists, or the admin.</p>
-                    <p>Admins check every day or three, but, if you are in a rush, you can contact us directly by replying to this email, or texting us at 825-925-9916.</p>';
-            } elseif (in_array('jvb_partner', array_values($user->roles))) {
-                $message .= '<div class="divider"></div>';
-                $message .= '<h3>NOTE:</h3>
-                    <p>Once you set your password, you\'ll have access to your custom dashboard where you can:</p>
-                    <ul>
-                        <li>Manage your profile information</li>
-                        <li>Create offers for enthusiasts or partners or both</li>
-                    </ul>
-                    <p>Nothing will be published until you\'ve been approved by the admin.</p>
-                    <p>Admins check every day or three, but, if you are in a rush, you can contact us directly by replying to this email, or texting us at 825-925-9916.</p>
-                    <p><strong>Note:</strong>Even after approval by admin, your ability to publish depends on your karmic standing by artists. Artists each have a vote they can cast (UP or DOWN) - if your karmic score dips too far in the negative, you account is subject to reconsideration or even a ban.</p>';
-            }
-            $message .= '<div class="divider"></div>';
-            $message = apply_filters(BASE.'new_user_email_content', $message, $user);
-
-			$message .= '<p>If you didn\'t create this account, please ignore this email and the link will expire.</p>';
-            $message .= sprintf('<p>Ink on, %s</p>', $user->first_name);
-            $message .= $this->signature;
-            $wp_new_user_notification_email['message'] = $this->getEmailTemplate($message, 'Welcome to edmonton.ink');
-        }
+		$message .= $this->signature;
+		$wp_new_user_notification_email['message'] = $this->getEmailTemplate($message, JVB_EMAIL['subjects']['newUser']);
 
         // Change the subject line
-        $wp_new_user_notification_email['subject'] = sprintf('Welcome to %s! Confirm your account', $this->site_name);
+		$prefix = JVB_EMAIL['types']['newUser']['showPrefix']??true;
+		$prefix = ($prefix) ? $this->prefix : '';
+        $wp_new_user_notification_email['subject'] = $prefix.JVB_EMAIL['types']['newUser']['subject'];
 
         return $wp_new_user_notification_email;
     }
@@ -394,12 +359,12 @@
     public function customizeNewUserEmailAdmin(array $emailData, WP_User $user, string $blogname):array
     {
         $message = '<p>A new user has registered on <strong>' . $this->site_name . '</strong>:</p>';
-        $message .= '<p><strong>Username:</strong> ' . $user->user_login . '<br>';
-        $message .= '<strong>Email:</strong> ' . $user->user_email . '<br>';
-        $message .= '<strong>Role:</strong> ' . str_replace(BASE, '', array_values($user->roles)[0]).'</p>';
+        $message .= '<p><strong>Username:</strong> ' . $user->user_login . '</p>';
+        $message .= '<p><strong>Email:</strong> ' . $user->user_email . '</p>';
+		$message = apply_filters('jvbNewUserAdminEmail', $message, $user);
         $message .= $this->signature;
         $emailData['message'] = $this->getEmailTemplate($message, 'New User Registration');
-        $emailData['subject'] = '[' . $this->site_name . '] New ' . str_replace(BASE, '', array_values($user->roles)[0]).': '.$user->display_name;
+        $emailData['subject'] = $this->prefix .'New ' . str_replace(BASE, '', array_values($user->roles)[0]).': '.$user->display_name;
 
         return $emailData;
     }
@@ -413,21 +378,27 @@
      *
      * @return string
      */
-    public function customizePasswordResetEmail(string $message, string $key, string $user_login, WP_User $user_data):string
+    public function customizePasswordResetEmail(string $message, string $key, string $user_login, WP_User $user):string
     {
-        $reset_url = network_site_url("wp-login.php?action=rp&key=$key&login=" . rawurlencode($user_login), 'login');
-
-        $content = '<p>Hey bud,</p>';
-        $content = '<p>We received a request to reset the password for an account associated with this email:</p>';
-        $content .= '<p><strong>Username:</strong> ' . $user_login . '</p>';
-        $content .= '<p>If you didn\'t make this request, you can safely ignore this email and nothing will happen to your account.</p>';
-        $content .= '<p>To reset your password, click the button below:</p>';
-        $content .= '<p style="text-align: center;"><a href="' . $reset_url . '" class="button">Reset Password</a></p>';
-        $content .= '<p>Or copy and paste this link into your browser:</p>';
-        $content .= '<p style="user-select:all;">' . $reset_url . '</p>';
-        $content .= '<div class="divider"></div>';
-        $content .= '<p>This password reset link is only valid for 24 hours.</p>';
-            $content .= $this->signature;
+		$reset_url = network_site_url("wp-login.php?action=rp&key=$key&login=" . rawurlencode($user_login), 'login');
+		$content = sprintf(
+			'<p>Hi %s!</p>
+			<p>We received a request to reset the password for an account associated with this email:</p>
+			<p><strong>Username:</strong> %s</p>
+			<p>If you didn\'t make this request, you can safely ignore this email and nothing will happen to your account.</p>
+			<p>To reset your password, click the button below:</p>
+			%s
+			<p>Or copy and paste this link into your browser:</p>
+			%s
+			<div class="divider"></div>
+			<p>This password reset link is only valid for 24 hours.</p>',
+			$user->display_name,
+			$user_login,
+			jvbMailButton($reset_url,'Reset Password'),
+			jvbEmailLink($reset_url)
+		);
+		$content = apply_filters('jvbPasswordResetEmail', $content, $user_login, $user, $reset_url);
+		$content .= $this->signature;
         return $this->getEmailTemplate($content, 'Password Reset');
     }
 
@@ -440,29 +411,42 @@
      */
     public function customizePasswordResetTitle(string $title, string $user_login, WP_User $user_data):string
     {
-        return '[' . $this->site_name . '] Password Reset Request';
-    }
+		$prefix = JVB_EMAIL['types']['resetPass']['showPrefix']??true;
+		$prefix = ($prefix) ? $this->prefix : '';
+		return $prefix.JVB_EMAIL['types']['resetPass']['subject'];
+
+	}
 
     /**
      * Email change notification to admin
      * @param array $email_change_email
-     * @param array $user
-     * @param array $userdata
+     * @param array $oldUser
+     * @param array $newUser
      *
      * @return array
      */
-    public function customizeEmailChangeEmail(array $email_change_email, array $user, array $userdata):array
+    public function customizeEmailChangeEmail(array $email_change_email, array $oldUser, array $newUser):array
     {
-        $content = sprintf('<p>Hi %s,</p>', $user['first_name']);
-        $content .= '<p>Ideally you already know this: you asked to change your email, and here we are.</p>';
-        $content .= '<p><strong>Old Email:</strong> ' . $email_change_email['to'] . '<br>';
-        $content .= '<strong>New Email:</strong> ' . $user['user_email'] . '</p>';
-
-        $content .= '<p>If this is news, or you did not change your email, please contact us immediately. You can <a href="sms:+18258239916">text us</a> or reply to this email."></a></p>';
-        $content .= '<p style="text-align: center;"><a href="' . wp_login_url() . '" class="button">Log In to Your Account</a></p>';
+        $content = sprintf(
+			'<p>Hi %s,</p>
+        <p>Ideally you already know this: someone asked to change the email for your account.</p>
+        <p><strong>Old Email:</strong> %s</p>
+        <p><strong>New Email:</strong> %s</p>
+        <div class="divider"></div>
+        <p>If this is news to you, or you did not request this - please contact us immediately. You can <a href="sms:+18258239916">text us</a> or reply to this email."></a></p>
+        %s',
+			$newUser['first_name'],
+			$oldUser['user_email'],
+			$newUser['user_email'],
+			jvbMailButton(wp_login_url(), 'Log In To Your Account')
+		);
+		$content = apply_filters('jvbEmailChangeRequestEmail', $content, $oldUser, $newUser);
         $content .= $this->signature;
         $email_change_email['message'] = $this->getEmailTemplate($content, 'Email Address Changed');
-        $email_change_email['subject'] = '[' . $this->site_name . '] Email Address Changed';
+		$prefix = JVB_EMAIL['types']['emailChange']['showPrefix']??true;
+		$prefix = ($prefix) ? $this->prefix : '';
+		$email_change_email['subject'] = $prefix.JVB_EMAIL['types']['emailChange']['subject'];
+
 
         return $email_change_email;
     }
@@ -474,21 +458,23 @@
      *
      * @return string
      */
-    public function customizeNewUserEmailContent(string $email_text, array $user):string
+    public function customizeNewUserEmailContent(string $email_text, array $new_user_email):string
     {
+		$confirm_url = esc_url( add_query_arg('newuseremail', $new_user_email['hash'], self_admin_url('profile.php')));
+		$content = sprintf(
+			'<p>Hey,</p>
+			<p>There was a request to change the email address associated with your account to this one.</p>
+			<p>This is just a friendly email to ensure you would like this change.</p>
+			<p>You can confirm this change by clicking the button below:</p>
+			%s
+			<p>Or copy and paste this link into your browser:</p>
+			%s',
+			jvbMailButton($confirm_url, 'Confirm this Email'),
+			jvbEmailLink($confirm_url)
+		);
 
-        // Extract the confirmation link from the default message
-        $pattern = '/###CONFIRM_URL### (.*?)(?=\n|\r|$)/';
-                                                                         preg_match($pattern, $email_text, $matches);
-        $confirm_url = isset($matches[1]) ? $matches[1] : admin_url('profile.php?newuseremail=' . $user['hash']);
+		$content = apply_filters('jvbEmailChangedEmail', $content, $confirm_url);
 
-        $content = '<p>Hey human,</p>';
-        $content .= '<p>Seems you want to change the email associated with your account.</p>';
-        $content .= '<p>You\'d like to change your email to: <strong>' . $user['newemail'] . '</strong></p>';
-        $content .= '<p>Please confirm this change by clicking the button below:</p>';
-        $content .= '<p style="text-align: center;"><a href="' . $confirm_url . '" class="button">Confirm Email Change</a></p>';
-        $content .= '<p>Or copy and paste this link into your browser:</p>';
-        $content .= '<p style="user-select:all">' . $confirm_url . '</p>';
         $content .= '<div class="divider"></div>';
         $content .= '<p>If you did not request this change, you can safely ignore this email and nothing will change.</p>';
         $content .= $this->signature;
@@ -499,23 +485,29 @@
         /**
          * Password change notification
          * @param array $pass_change_email
-         * @param array $user
-         * @param array $userdata
+         * @param array $oldUser
+         * @param array $newUser
          *
          * @return array
      */
-    public function customizePasswordChangeEmail(array $pass_change_email, array $user, array $userdata):array
+    public function customizePasswordChangeEmail(array $pass_change_email, array $oldUser, array $newUser):array
     {
-
-        $content = '<p>Hey bud,</p>';
-        $content .= '<p>Ideally, you\'re expecting this email. You wanted to change your password, and this is to let you know that it\'s definitely updated.</p>';
-        $content .= '<p>If you did not change your password, please contact us immediately.</p>';
-        $content .= '<p>You can <a href="sms:+18259257398">text us</a>, or reply to this email.</p>';
-        $content .= '<p style="text-align: center;"><a href="' . wp_login_url() . '" class="button">Log In to Your Account</a></p>';
+		$content = sprintf(
+			'<p>Hi %s,</p>
+			<p>This is a confirmation email to let you know your password has successfully been changed.</p>
+			<p>If you\'re not expecting this email, and did not change your password - please <strong>contact us immediately</strong></p>
+			<p>You can <a href="sms:+18259257398">text us</a>, or reply to this email.</p>
+			%s',
+			$oldUser['first_name'],
+			jvbMailButton(wp_login_url(), 'Log In to Your Account')
+		);
+		$content = apply_filters('jvbPasswordChangeEmail', $content, $oldUser, $newUser);
         $content .= $this->signature;
 
         $pass_change_email['message'] = $this->getEmailTemplate($content, 'Password Changed');
-        $pass_change_email['subject'] = '[' . $this->site_name . '] Password Changed';
+		$prefix = JVB_EMAIL['types']['passwordChange']['showPrefix']??true;
+		$prefix = ($prefix) ? $this->prefix : '';
+		$pass_change_email['subject'] = $prefix.JVB_EMAIL['types']['passwordChange']['subject'];
 
         return $pass_change_email;
     }
@@ -527,16 +519,11 @@
      *
      * @return string
      */
-    public function customizeUserRequestEmail(string $content, array $request_data):string
+    public function customizeUserRequestEmail(string $content, array $email_data):string
     {
 
-        // Extract the confirmation URL from the content
-        $pattern = '/<a href="([^"]+)"[^>]*>/';
-        preg_match($pattern, $content, $matches);
-        $confirm_url = isset($matches[1]) ? $matches[1] : '';
-
-        $request_type = $request_data['action_name'];
-        $request_name = '';
+       	$confirm_url = $email_data['confirm_url'];
+        $request_type = $email_data['action_name'];
 
         switch ($request_type) {
             case 'export_personal_data':
@@ -549,13 +536,20 @@
                 $request_name = 'Data Request';
         }
 
-        $message = '<p>Hi privacy enthusiast,</p>';
-        $message .= '<p>A request has been made to perform the following action on your account:</p>';
-        $message .= '<p><strong>' . $request_name . '</strong></p>';
-        $message .= '<p>If you made this request, you can confirm it by clicking the button below:</p>';
-        $message .= '<p style="text-align: center;"><a href="' . $confirm_url . '" class="button">Confirm Request</a></p>';
-        $message .= '<p>Or copy and paste this link into your browser:</p>';
-        $message .= '<p style="user-select:all;">' . $confirm_url . '</p>';
+
+		$message = sprintf(
+			'<p>Hi,</p>
+			<p>You\'re receiving this email because a request has been made to <strong>%s</strong></p>
+			<p>If you\'re the one who made this request, you can confirm it by clicking the button below:</p>
+			%s
+			<p>Or copy and paste this link into your browser:</p>
+			%s',
+			$request_name,
+			jvbMailButton($confirm_url, 'Confirm'),
+			jvbEmailLink($confirm_url)
+		);
+		$message = apply_filters('jvbPersonalDataExport', $message, $request_type, $confirm_url, $email_data);
+
         $message .= '<div class="divider"></div>';
         $message .= '<p>If you did not make this request, you can safely ignore this email.</p>';
         $message .= $this->signature;
@@ -573,19 +567,23 @@
      */
     public function customizePersonalDataEmail(string $content, int $request_id, array $email_data):string
     {
+        $download_url = $email_data['export_file_url'];
+		$expiresAt = $email_data['expiration_date'];
 
-        // Extract the download link
-        $pattern = '/<a href="([^"]+)"[^>]*>/';
-        preg_match($pattern, $content, $matches);
-        $download_url = isset($matches[1]) ? $matches[1] : '';
-
-        $message = '<p>Your request for an export of personal data has been completed.</p>';
-        $message .= '<p>You can download your personal data by clicking the button below.</p>';
-        $message .= '<p style="text-align: center;"><a href="' . $download_url . '" class="button">Download Personal Data</a></p>';
-        $message .= '<p>Or copy and paste this link into your browser:</p>';
-        $message .= '<p style="user-select:all;">' . $download_url . '</p>';
-        $message .= '<div class="divider"></div>';
-        $message .= '<p><strong>Important:</strong> For privacy reasons, this link will expire in ' . $email_data['expiration'] . '.</p>';
+		$message = sprintf(
+			'<p>Hi,</p>
+			<p>You\'re receiving this email because you requested an export of your personal data.</p>
+			<p>You can download your personal data by clicking the button below:</p>
+			%s
+			<p>Or you can copy and paste this link into your browser:</p>
+			%s
+			<div class="divider"></div>
+			<p><strong>Important:</strong> For privacy and security, this link will expire at %s.</p>',
+			jvbMailButton($download_url, 'Download Your Data'),
+			jvbEmailLink($download_url),
+			$expiresAt
+		);
+		$message = apply_filters('jvbPersonalDataExported', $message, $download_url, $expiresAt, $email_data);
         $message .= $this->signature;
 
         return $this->getEmailTemplate($message, 'Your Personal Data Export');
diff --git a/inc/managers/IconsManager.php b/inc/managers/IconsManager.php
new file mode 100644
index 0000000..d3710d0
--- /dev/null
+++ b/inc/managers/IconsManager.php
@@ -0,0 +1,434 @@
+<?php
+namespace JVBase\managers;
+
+use JVBase\utility\Features;
+
+if (!defined('ABSPATH')) {
+	exit;
+}
+
+class IconsManager
+{
+	protected static ?IconsManager $instance = null;
+	protected CacheManager $cache;
+	protected string $style = 'regular';
+	protected array $styles = ['regular', 'bold', 'duotone', 'fill', 'light', 'thin'];
+	// Custom icons registered via filter
+	protected array $customIcons = [];
+	protected array $usedIcons = [];
+	protected array $map = [];
+
+	/**
+	 * Get singleton instance
+	 */
+	public static function getInstance(): IconsManager
+	{
+		if (self::$instance === null) {
+			self::$instance = new self();
+		}
+		return self::$instance;
+	}
+	private function __construct()
+	{
+		$this->cache = CacheManager::for('icons', WEEK_IN_SECONDS);
+		$this->style = (array_key_exists('icons', JVB_SITE) && in_array(JVB_SITE['icons'], $this->styles))
+			? JVB_SITE['icons']
+			: 'regular';
+
+		$this->addMap();
+
+		// Allow custom icon registration
+		$this->customIcons = apply_filters('jvbRegisterCustomIcons', [
+			'syncing'		=> JVB_DIR .'/assets/icons/cloud-sync-thin.svg',
+			'alphabetical'	=> JVB_DIR.'/assets/icons/alphabetical.svg'
+		]);
+
+
+		$this->usedIcons = get_option(BASE.'used_icons', []);
+		$this->includeIcons();
+		// Register hooks only once
+		$this->registerHooks();
+	}
+
+	/**
+	 * Include icons via filter (for JS usage, etc.)
+	 */
+	protected function includeIcons():array
+	{
+		$icons = get_option(BASE.'includeIcons');
+//		$icons = false;
+		if (!$icons) {
+			$icons = $this->addIncludeIcons();
+		}
+		$include = apply_filters('jvbIncludeIcons', []);
+		$add = array_filter($include, function($addIt) use ($icons) {
+			return !in_array($addIt, $icons);
+		});
+		error_log('Adding icons: '.print_r($add, true));
+		if (!empty($add)) {
+			$this->addIncludeIcons($add);
+		}
+
+		return $icons;
+	}
+
+	protected function addIncludeIcons(array $add = []) {
+		$icons = get_option(BASE.'includeIcons');
+		if (!$icons) {
+			$icons = [
+				'check-circle',
+				'close-circle',
+				'cloud-slash',
+				'exclamation-mark',
+				'cloud-arrow-down',
+				'cloud-arrow-up',
+				'cloud-check',
+				'cloud-slash',
+				'cloud-warning',
+				'syncing',
+				'cloud-x',
+				'arrows-clockwise',
+				'share-fat',
+				'trash',
+				'star',
+				['name' => 'star-half', 'style' => 'fill'],
+				['name' => 'star', 'style' => 'fill'],
+				//FORMATTING
+				'copy',
+				'paragraph',
+				'text-h-one',
+				'text-h-two',
+				'text-h-three',
+				'text-h-four',
+				'text-h-five',
+				'text-h-six',
+				['name'	=>'text-b', 'style' => 'fill'],
+				'text-italic',
+				'text-underline',
+				'text-strikethrough',
+				'list-dashes',
+				'list-numbers',
+				'text-align-left',
+				'text-align-center',
+				'text-align-right',
+//			'text-align-justify',
+				'link',
+				//FILE ICONS
+				'file-pdf',
+				'file-csv',
+				'file-doc',
+				'file-txt',
+				'file-xls',
+			];
+			$check = [JVB_CONTENT, JVB_TAXONOMY, JVB_USER];
+			foreach ($check as $constant) {
+				foreach ($constant as $key => $value) {
+					if (array_key_exists('icon', $value) && !in_array($value['icon'], $icons)) {
+						$icons[] = $value['icon'];
+					}
+				}
+			}
+			$icons = apply_filters('jvbIncludeIcons', $icons);
+
+			update_option(BASE.'includeIcons', $icons);
+			update_option(BASE.'icons_needs_update', true);
+		}
+		$add = apply_filters('jvbIncludeIcons', $add);
+		$add = array_filter($add, function($addIt) use ($icons) {
+			return !in_array($addIt, $icons);
+		});
+		if (!empty($add)) {
+			$icons = array_merge($add, $icons);
+			update_option(BASE.'usedIcons', $icons);
+			update_option(BASE.'icons_needs_update', true);
+		}
+		return $icons;
+	}
+
+	protected function addMap():void
+	{
+		$map = get_option(BASE.'iconMap');
+		if (!$map) {
+			$map = [];
+			if (Features::forSite()->has('referrals')){
+				$map['referrals'] = 'hand-heart';
+			}
+			if (Features::forSite()->has('dashboard')){
+				$map['dash'] = 'door';
+			}
+			if (Features::forSite()->has('magicLink')){
+				$map['magicLink'] = 'magic-wand';
+			}
+			if (Features::hasAnyIntegration()) {
+				$map['integrations'] = 'plugs-connected';
+			}
+			update_option(BASE.'iconMap', $map);
+		}
+
+		$this->map = apply_filters('jvbMapIcons', $map);
+	}
+
+	/**
+	 * Register WordPress hooks
+	 */
+	protected function registerHooks(): void
+	{
+		add_action('init', [$this, 'checkCSS']);
+		add_action('wp_enqueue_scripts', [$this, 'enqueueIconStyles']);
+		add_action('admin_enqueue_scripts', [$this, 'enqueueIconStyles']);
+	}
+
+	public function checkCSS():void
+	{
+//		update_option(BASE.'icons_needs_update', true);
+		if (get_option(BASE.'icons_needs_update', false)) {
+			error_log('Regenerating CSS');
+			delete_option(BASE.'icons_needs_update');
+			$this->regenerateCSS();
+		}
+	}
+
+	protected function regenerateCSS(): void
+	{
+		error_log('[IconsManager]:regenerateCSS');
+		$css = $this->generateIconCSS();
+		$css_path = JVB_DIR.'/assets/css/icons.css';
+
+		if (file_put_contents($css_path, $css) !== false) {
+			CacheManager::updateTimestamp('icons');
+		} else {
+			error_log('[IconsManager]Could not write css.');
+		}
+	}
+
+	/**
+	 * Prevent cloning
+	 */
+	private function __clone() {}
+
+	/**
+	 * Prevent unserialization
+	 */
+	public function __wakeup()
+	{
+		throw new \Exception("Cannot unserialize singleton");
+	}
+
+	/**
+	 * Get an icon element
+	 *
+	 * @param string $name Icon name (e.g., 'heart', 'calendar')
+	 * @param array $options Options array:
+	 *   - 'style' => 'regular'|'bold'|'fill'|etc.
+	 *   - 'label' => 'Accessible label' (for standalone icons)
+	 *   - 'decorative' => true (for icons next to text)
+	 *   - 'class' => 'additional classes'
+	 *   - 'size' => 24 (for custom sizing via inline style)
+	 * @return string HTML icon element
+	 */
+	public function getIcon(string $name, array $options = []): string
+	{
+		if (!array_key_exists('style', $options)) {
+			$options['style'] = $this->style;
+		}
+		$name = (array_key_exists($name, $this->map)) ? $this->map[$name] : $name;
+
+		// Validate icon exists
+		if (!$this->iconExists($name, $options['style'])) {
+			error_log('[IconsManager] Icon not found: ' . $name);
+			return '';
+		}
+
+		$style = $options['style'] ?? $this->style;
+
+		// Track icon usage
+		$this->trackIconUsage($name, $style);
+
+		// Build classes
+		$classes = ['icon', 'icon-' . $name];
+		if (!empty($options['class'])) {
+			$classes[] = $options['class'];
+		}
+
+
+		$attrs = ['class="' . esc_attr(implode(' ', $classes)) . '"'];
+		$attrs[] = 'aria-hidden="true"';
+
+
+
+		return '<i ' . implode(' ', $attrs) . '></i>';
+	}
+
+	/**
+	 * Track icon usage for CSS generation
+	 */
+	protected function trackIconUsage(string $name, string $style): void
+	{
+		if (!array_key_exists($style, $this->usedIcons)) {
+			$this->usedIcons[$style] = [];
+		}
+
+		if (!in_array($name, $this->usedIcons[$style])) {
+			$this->usedIcons[$style][] = $name;
+			update_option(BASE.'used_icons', $this->usedIcons);
+			// Flag for regeneration on next init
+			update_option(BASE.'icons_needs_update', true);
+
+			// Clear cache
+			$this->cache->delete('icon_styles_css');
+		}
+	}
+
+	/**
+	 * Check if icon file exists
+	 */
+	protected function iconExists(string $name, ?string $style = null): bool
+	{
+		if (!$style) {
+			$style = $this->style;
+		}
+		// Check custom icons first
+		if (array_key_exists($name, $this->customIcons)) {
+			return file_exists($this->customIcons[$name]);
+		}
+
+		// Check standard icons
+		$filepath = $this->buildFilePath($name, $style);
+		return file_exists($filepath);
+	}
+
+	/**
+	 * Build file path for icon
+	 */
+	protected function buildFilePath(string $name, ?string $style = null): string
+	{
+		if (!$style) {
+			$style = $this->style;
+		}
+		// Custom icons (absolute path provided)
+		if (array_key_exists($name, $this->customIcons)) {
+			return $this->customIcons[$name];
+		}
+
+		// Standard SVG icons in /assets/icons/
+		if (str_ends_with($name, '.svg')) {
+			return JVB_DIR . '/assets/icons/' . $name;
+		}
+		$name = ($style === 'regular') ? $name : $name . '-' . $style;
+
+		// Phosphor icons with style variants
+		return JVB_DIR . '/assets/phosphor-icons/' . $style . '/' . $name . '.svg';
+	}
+
+	/**
+	 * Get raw SVG content for CSS mask-image
+	 */
+	protected function getRawSvg(string $name, ?string $style = null): ?string
+	{
+		if (!$style) {
+			$style = $this->style;
+		}
+		$filepath = $this->buildFilePath($name, $style);
+
+		if (!file_exists($filepath)) {
+			return null;
+		}
+
+		$svg = file_get_contents($filepath);
+		if ($svg === false) {
+			return null;
+		}
+
+		// Clean up SVG for CSS usage
+		$svg = preg_replace("/([\n\t]+)/", ' ', $svg);
+		$svg = preg_replace('/>\s*</', '><', $svg);
+		$svg = trim($svg);
+
+		return $svg;
+	}
+
+
+	/**
+	 * Enqueue icon styles via REST endpoint
+	 */
+	public function enqueueIconStyles(): void
+	{
+		$timestamp = CacheManager::getTimestamp('icons');
+
+		wp_enqueue_style(
+			'jvb-icons',
+			JVB_URL.'assets/css/icons.css',
+			[],
+			$timestamp
+		);
+	}
+
+	/**
+	 * Generate CSS from icon list
+	 */
+	protected function generateIconCSS(): string
+	{
+		$css = '';
+		foreach ($this->usedIcons as $style => $icons) {
+			$styleClass = ($style !== $this->style) ? '-'.substr($style, 0,2) : '';
+			foreach ($icons as $icon) {
+
+				$svg = $this->getEncodedSVG($icon, $style);
+				if ($svg !== '') {
+					$css .= ".icon-{$icon}{$styleClass}{";
+					$css .= "--icon:url('data:image/svg+xml;base64,{$svg}');";
+					$css .= "}";
+				}
+			}
+		}
+		return $this->minifyCss($css);
+	}
+
+	protected function minifyCSS(string $css): string
+	{
+		// Remove comments
+		$css = preg_replace('!/\*[^*]*\*+([^/][^*]*\*+)*/!', '', $css);
+		// Remove whitespace
+		$css = preg_replace('/\s+/', ' ', $css);
+		// Remove spaces around specific characters
+		$css = preg_replace('/\s*([:;{}])\s*/', '$1', $css);
+
+		return trim($css);
+	}
+	public function getCSSIcon(string $icon, ?string $style=null):string
+	{
+		if (!$style) {
+			$style = $this->style;
+		}
+		$svg = $this->getEncodedSVG($icon, $style);
+		if ($svg !== '') {
+			return "data:image/svg+xml;base64,{$svg}";
+		}
+		return '';
+	}
+	public function getEncodedSVG(string $icon, ?string $style = null):string
+	{
+		if (!$style) {
+			$style = $this->style;
+		}
+		return $this->cache->remember($style.$icon,
+		function () use ($icon, $style) {
+			$svg = $this->getRawSvg($icon, $style);
+			if ($svg) {
+				return base64_encode($svg);
+			}
+			return '';
+		});
+
+	}
+
+	/**
+	 * Clear icon cache (useful for development/debugging)
+	 */
+	public function clearIconCache(): void
+	{
+		delete_option(BASE . 'icon_usage_list'); // Clear DB option
+		$this->cache->delete('icon_styles_css');
+		CacheManager::updateTimestamp('icons');
+	}
+}
diff --git a/inc/managers/LoginManager.php b/inc/managers/LoginManager.php
index dd12a81..7ded3ba 100644
--- a/inc/managers/LoginManager.php
+++ b/inc/managers/LoginManager.php
@@ -21,6 +21,7 @@
 	protected ?MetaForm $metaForm = null;
 	protected EmailManager $emailManager;
 	protected AjaxRateLimiter $rateLimiter;
+	protected CacheManager $cache;
 
 
 	protected array $forms =[];
@@ -30,7 +31,6 @@
 	protected string $title = '';
 
 	// Token handlers registry
-	protected array $tokenHandlers = [];
 	protected array $messageHandlers = [];
 
 	private array $allowed_file_types = [
@@ -44,12 +44,10 @@
 	public function __construct()
 	{
 		$this->siteFeatures = Features::forSite();
-		$this->metaForm = new MetaForm();
 		$this->emailManager = new EmailManager();
 		$this->rateLimiter = new AjaxRateLimiter();
 
-		// Register default token handlers
-		$this->registerDefaultHandlers();
+		$this->cache = CacheManager::for('login');
 
 		// Initialize magic link support if enabled
 		if ($this->siteFeatures->has('magicLink')) {
@@ -66,12 +64,6 @@
 
         add_action('wp_enqueue_scripts', [$this, 'enqueueScripts'], 15);
 
-		// Handle form submissions via AJAX
-		add_action('wp_ajax_nopriv_jvb_login', [$this, 'handleAjaxLogin']);
-		add_action('wp_ajax_nopriv_jvb_register', [$this, 'handleAjaxRegister']);
-		add_action('wp_ajax_nopriv_jvb_lostpassword', [$this, 'handleAjaxLostPassword']);
-		add_action('wp_ajax_nopriv_jvb_resetpass', [$this, 'handleAjaxResetPassword']);
-
 		// Login success handling
 		add_action('wp_login', [$this, 'handleSuccessfulLogin'], 10, 2);
 
@@ -88,6 +80,11 @@
 	 */
 	public function redirectToCustomLogin(): void
 	{
+		// Handle interim login
+		if (isset($_GET['interim-login'])) {
+			// Don't redirect - let WP handle it
+			return;
+		}
 		// Don't redirect if AJAX or REST
 		if ((defined('DOING_AJAX') && DOING_AJAX) || (defined('REST_REQUEST') && REST_REQUEST)) {
 			return;
@@ -115,19 +112,27 @@
 			$select = [];
 			//Basic fields, for any
 			$fields = [
-				'name'	=> [
+				'user_name'	=> [
 					'type'		=> 'text',
 					'required'	=> true,
 					'label'		=> 'Your Name',
 					'placeholder'=> 'Mister Meseeks'
 				],
-				'email'	=> [
+				'user_email'	=> [
 					'type'		=> 'email',
 					'required'	=> true,
 					'label'		=> 'Your Email',
 					'placeholder'=> 'look@me.com'
 				]
 			];
+			if (Features::forSite()->has('referrals')) {
+				$fields['referral_code'] = [
+					'type'	=> 'text',
+					'required'=> false,
+					'label'	=> 'Referral Code',
+					'hint'	=> 'Have a referral code? Paste it here!'
+				];
+			}
 			if (count(JVB_USER) > 1) {
 				foreach (JVB_USER as $slug => $config) {
 					if (!array_key_exists('can_register', $config) || !$config['can_register']) {
@@ -184,6 +189,7 @@
 				$fields = $this->getRegistrationFormFields();
 				break;
 			case 'lostpassword':
+			case 'magic':
 				$fields = [
 					'user_email' => [
 						'type' => 'email',
@@ -297,108 +303,6 @@
 		return $self->isLoginPage();
 	}
 
-	/**************************************************************************
-		TOKEN & MESSAGE HANDLERS
- 			Extensible by other classes
-	**************************************************************************/
-	public function registerTokenHandler(string $token_key, callable $handler, int $priority = 10): void
-	{
-		if (!isset($this->tokenHandlers[$priority])) {
-			$this->tokenHandlers[$priority] = [];
-		}
-
-		$this->tokenHandlers[$priority][$token_key] = $handler;
-		ksort($this->tokenHandlers);
-	}
-
-	public function registerMessageHandler(string $type, callable $handler, ?callable $condition = null): void
-	{
-		$this->messageHandlers[$type] = [
-			'handler' => $handler,
-			'condition' => $condition
-		];
-	}
-
-	protected function registerDefaultHandlers(): void
-	{
-		// Invitation handler
-		if ($this->siteFeatures->has('invitations')) {
-			$this->registerTokenHandler('invite', function($token, $email, $user_id) {
-				if (isset($_POST['invite_token'])) {
-					JVB()->routes('invites')->acceptInvitation(
-						sanitize_text_field($_POST['invite_token']),
-						sanitize_email($_POST['invite_email']),
-						$user_id
-					);
-				}
-			});
-
-			$this->registerMessageHandler('invitation',
-				function() {
-					$data = JVB()->routes('invites')->verifyInvitation(
-						sanitize_text_field($_GET['invite']),
-						sanitize_email($_GET['email'])
-					);
-					$name = $data->name;
-					$inviters = json_decode($data->inviters, true);
-					$names = [];
-
-					foreach ($inviters as $inviter) {
-						$artist = jvbContentFromUser((int)$inviter['user_id']);
-						$names[] = ($artist['name'] === '') ? $artist['display_name'] : $artist['name'];
-					}
-
-					$message = (count($names) > 1)
-						? 'are already here, and have invited you to join in!'
-						: ' is already here, and invited you to join in!';
-
-					return '<h2>Join the Scene, '.$name.'</h2>
-                    <p style="text-align:center;">'.jvbCommaList($names).$message.'</p>';
-				},
-				function() {
-					return isset($_GET['invite']) && isset($_GET['email']);
-				}
-			);
-		}
-
-		// List sharing handler (Favourites)
-		if ($this->siteFeatures->has('favourites')) {
-			$this->registerTokenHandler('list_token', function($token, $email, $user_id) {
-				if (!empty($_GET['list_token']) && !empty($_GET['email'])) {
-					JVB()->routes('favourites')->acceptListInvitation(
-						sanitize_text_field($_GET['list_token']),
-						sanitize_email($_GET['email']),
-						$user_id
-					);
-				}
-			});
-
-			$this->registerMessageHandler('favourites',
-				function() {
-					return '<h2>'.(JVB_LOGIN['login_from_favourite_header'] ?? 'Save your Favourites').'</h2>';
-				},
-				function() {
-					return isset($_GET['type']) && $_GET['type'] === 'favourites';
-				}
-			);
-		}
-
-		// Referral handler - FIXED VERSION
-		$this->registerTokenHandler('referral_code', function($code, $email, $user_id) {
-			// $code is already sanitized from processTokenHandlers
-			if (session_status() === PHP_SESSION_NONE) {
-				session_start();
-			}
-			$_SESSION[BASE . 'referral_code'] = $code;
-			setcookie(
-				BASE . 'referral_code',
-				$code,
-				time() + (86400 * 30),
-				'/'
-			);
-		}, 5);
-	}
-
 	protected function initMagicLinkSupport(): void
 	{
 		if (!Features::forSite()->has('magicLink')) {
@@ -407,8 +311,6 @@
 		$this->magicLink = new MagicLinkManager();
 	}
 
-
-
 	/*********************************************************************
 		RENDERING
 	*********************************************************************/
@@ -418,6 +320,18 @@
 			return $template;
 		}
 		$this->setup();
+		$page = $this->cache->remember(
+				$this->getAction(),
+				function() {
+					return $this->renderPage();
+				},
+				5
+			);
+
+		echo $page;
+		return '';
+	}
+	protected function renderPage() {
 		ob_start();
 		jvbInlineStyles('nav');
 		jvbInlineStyles('dash');
@@ -428,11 +342,10 @@
 		$this->renderForms();
 		$this->renderFooter();
 
-		echo  ob_get_clean();
-		return '';
+		return ob_get_clean();
 	}
 
-	protected function setup():void
+	protected function getAction():string
 	{
 		if (array_key_exists('action', $_GET)) {
 			switch ($_GET['action']){
@@ -450,8 +363,23 @@
 		} else {
 			$action = 'login';
 		}
+		return $action;
+	}
 
-		$this->action = $action;
+	protected function setup():void
+	{
+		$this->action = $this->getAction();
+		if (in_array($this->action, ['logout']) || array_key_exists('loggedout', $_GET)) {
+			wp_logout();
+			wp_redirect(esc_attr($_GET['redirect_to'] ?? get_home_url()));
+			exit;
+		}
+		if (in_array($this->action, ['rp', 'resetpass']) && !is_user_logged_in()) {
+			wp_redirect(wp_login_url());
+			exit;
+		} elseif (is_user_logged_in()) {
+			wp_redirect(get_home_url(null, '/dash/'));
+		}
 		$this->setupLabels();
 		$this->setupFields();
 		$this->setupTitle();
@@ -496,6 +424,12 @@
 				justify-content: center;
 				position: relative;
 			}
+			.login .fstatus.fstatus {
+				--wrap: nowrap;
+				top:0;
+				bottom:unset;
+				right: 0;
+			}
 			.login main::before {
 				background-size: 20vw;
 				inset: 0;
@@ -567,21 +501,23 @@
 
 	protected function renderForms():void
 	{
-
+		$this->metaForm = new MetaForm();
 		$form = $this->action.'form';
-
 		?>
 		<section class="login-box col btw">
 			<h1><?=$this->labels['title']?></h1>
 			<?= $this->labels['description'] ?>
+
+
 			<form name="<?=$form?>" method="post" data-action="jvb_<?=$this->action?>">
+				<?= jvbFormStatus() ?>
 				<?php wp_nonce_field('jvb_'.$this->action, '_wpnonce'); ?>
 				<input type="hidden" name="action" value="jvb_<?=$this->action?>">
 				<input type="hidden" name="redirect_to" value="<?= esc_attr($_GET['redirect_to'] ?? '') ?>">
 				<input type="hidden" name="request_id" value="<?= wp_generate_password(16, false) ?>">
-
+				<?= ($this->action === 'magic') ? '<input type="hidden" name="type" value="login">' : '' ?>
 				<?php
-				$this->addHiddenTokenFields();
+				do_action('jvb_add_token_inputs', $this->action);
 
 				foreach ($this->fields as $name => $config) {
 					$this->metaForm->render($name, '', $config);
@@ -590,7 +526,7 @@
 				$this->maybeTurnstile();
 				 ?>
 				 <div class="row btw nowrap">
-					<button type="submit" class="button button-primary button-large">Log In</button>
+					<button type="submit" class="button button-primary button-large"><?=$this->labels['submit']?></button>
 					<?php $this->maybeMagicLink(); ?>
 				</div>
 			</form>
@@ -620,7 +556,8 @@
 						<a href="<?= add_query_arg('action', 'lostpassword', get_the_permalink()) ?>">Forgot Password?</a>
 						<?php
 						break;
-					case 'lostpassword': ?>
+					case 'lostpassword':
+					case 'magic': ?>
 						<a href="<?= get_the_permalink() ?>">Login Instead</a>
 						<a href="<?= add_query_arg('action', 'register', get_the_permalink()) ?>">Create Account</a>
 						<?php
@@ -661,8 +598,8 @@
             $title = ($checked == '') ? 'Toggle Dark Mode' : 'Toggle Light Mode';
             echo '<label title="'.$title.'" id="theme-switch" class="toggle-switch" for="theme-switcher">
                     <input class="theme-switch row" id="theme-switcher" type="checkbox"'.$checked.' data-setting="theme" data-theme role="switch" name="dark-mode"><span class="slider">'.
-					jvbIcon('light', ['title'=> 'Light Mode']).
-					jvbIcon('dark', ['title'=>'Dark Mode']).
+					jvbIcon('sun-dim', ['title'=> 'Light Mode']).
+					jvbIcon('moon', ['title'=>'Dark Mode']).
 					'</span></label>';
             ?>
             <p class="title">
@@ -677,7 +614,7 @@
 						}
 					}
 					if ($out == '') {
-						$out =jvbIcon('home');
+						$out =jvbIcon('house');
 					}
                     ?><?= $out ?>
                 </a>
@@ -709,339 +646,6 @@
         <?php
     }
 
-	protected function addHiddenTokenFields(): void
-	{
-		foreach ($this->tokenHandlers as $priority => $handlers) {
-			foreach ($handlers as $token_key => $handler) {
-				if (isset($_GET[$token_key])) {
-					$value = sanitize_text_field($_GET[$token_key]);
-					echo '<input type="hidden" name="' . esc_attr($token_key) . '" value="' . esc_attr($value) . '">';
-				}
-			}
-		}
-
-		if (isset($_GET['email'])) {
-			echo '<input type="hidden" name="token_email" value="' . esc_attr(sanitize_email($_GET['email'])) . '">';
-		}
-	}
-
-	/*************************************************************************
-		AJAX HANDLERS
-	*************************************************************************/
-	public function handleAjaxLogin(): void
-	{
-		check_ajax_referer('jvb_login', '_wpnonce');
-
-		// Rate limiting
-		if (!$this->checkAjaxRateLimit('login')) {
-			wp_send_json_error([
-				'message' => 'Too many attempts. Please wait a moment.',
-				'code' => 'rate_limit'
-			], 429);
-		}
-
-		// Duplicate submission check
-		if (!$this->checkRequestId()) {
-			wp_send_json_error([
-				'message' => 'Duplicate request detected',
-				'code' => 'duplicate_request'
-			], 409);
-		}
-
-		$email = sanitize_email($_POST['user_email'] ?? '');
-		$password = $_POST['user_password'] ?? '';
-		$remember = !empty($_POST['remember_me']);
-
-		if (empty($email) || empty($password)) {
-			wp_send_json_error([
-				'message' => 'Please fill in all fields',
-				'field' => empty($email) ? 'user_email' : 'user_password',
-				'code' => 'missing_fields'
-			]);
-		}
-
-		// Verify Turnstile if enabled
-		if (!$this->verifyTurnstile()) {
-			wp_send_json_error([
-				'message' => 'Security verification failed',
-				'code' => 'turnstile_failed'
-			]);
-		}
-
-		$user = get_user_by('email', $email);
-		if (!$user) {
-			wp_send_json_error([
-				'message' => 'Unknown email address',
-				'field' => 'user_email',
-				'code' => 'invalid_email'
-			]);
-		}
-
-		$user = wp_authenticate($user->user_login, $password);
-
-		if (is_wp_error($user)) {
-			wp_send_json_error([
-				'message' => $user->get_error_message(),
-				'field' => 'user_password',
-				'code' => $user->get_error_code()
-			]);
-		}
-
-		wp_clear_auth_cookie();
-		wp_set_current_user($user->ID);
-		wp_set_auth_cookie($user->ID, $remember);
-
-		do_action('wp_login', $user->user_login, $user);
-
-		$redirect = $_POST['redirect_to'] ?? home_url('/dash');
-		wp_send_json_success(['redirect' => $redirect]);
-	}
-
-	public function handleAjaxRegister(): void
-	{
-		check_ajax_referer('jvb_register', '_wpnonce');
-
-		// Rate limiting
-		if (!$this->checkAjaxRateLimit('register')) {
-			wp_send_json_error([
-				'message' => 'Too many attempts. Please wait a moment.',
-				'code' => 'rate_limit'
-			], 429);
-		}
-
-		// Duplicate submission check
-		if (!$this->checkRequestId()) {
-			wp_send_json_error([
-				'message' => 'Duplicate request detected',
-				'code' => 'duplicate_request'
-			], 409);
-		}
-
-		// Verify Turnstile
-		if (!$this->verifyTurnstile()) {
-			wp_send_json_error([
-				'message' => 'Security verification failed',
-				'code' => 'turnstile_failed'
-			]);
-		}
-
-		$name = sanitize_text_field($_POST['name'] ?? '');
-		$email = sanitize_email($_POST['email'] ?? '');
-		$user_type = sanitize_text_field($_POST['user_select'] ?? 'subscriber');
-
-		// Spam prevention - if subscriber is selected and there are other options
-		if ($user_type === 'subscriber' && count(JVB_USER) > 0) {
-			$registerable = array_filter(JVB_USER, fn($config) => $config['can_register'] ?? false);
-			if (!empty($registerable)) {
-				wp_send_json_error([
-					'message' => 'Please select a valid account type',
-					'field' => 'user_select',
-					'code' => 'invalid_user_type'
-				]);
-			}
-		}
-
-		// Validate fields
-		if (empty($name)) {
-			wp_send_json_error([
-				'message' => 'Name is required',
-				'field' => 'name',
-				'code' => 'missing_name'
-			]);
-		}
-
-		if (empty($email)) {
-			wp_send_json_error([
-				'message' => 'Email is required',
-				'field' => 'email',
-				'code' => 'missing_email'
-			]);
-		}
-
-		// Check if role can register
-		if ($user_type !== 'subscriber') {
-			if (!isset(JVB_USER[$user_type]) || empty(JVB_USER[$user_type]['can_register'])) {
-				wp_send_json_error([
-					'message' => 'Invalid account type',
-					'field' => 'user_select',
-					'code' => 'invalid_user_type'
-				]);
-			}
-		}
-
-		// Check if email exists
-		if (email_exists($email)) {
-			wp_send_json_error([
-				'message' => 'Email already registered',
-				'field' => 'email',
-				'code' => 'duplicate_email'
-			]);
-		}
-
-		// Create user
-		$user_id = wp_create_user($email, wp_generate_password(), $email);
-
-		if (is_wp_error($user_id)) {
-			wp_send_json_error([
-				'message' => $user_id->get_error_message(),
-				'code' => 'user_creation_failed'
-			]);
-		}
-
-		// Update user data
-		wp_update_user([
-			'ID' => $user_id,
-			'display_name' => $name,
-			'first_name' => $name
-		]);
-
-		// Set role
-		$user = new WP_User($user_id);
-		if ($user_type === 'subscriber') {
-			$user->set_role('subscriber');
-		} else {
-			$role = JVB_USER[$user_type]['role'] ?? 'subscriber';
-			$user->set_role($role);
-
-			// Check if needs approval
-			if (Features::forMembership()->has('memberVerified') &&
-				in_array($role, JVB_MEMBERSHIP['memberVerified'] ?? [])) {
-				$user->add_cap('skip_moderation', false);
-				update_user_meta($user_id, BASE . 'pending_approval', true);
-			}
-		}
-
-		// Save additional fields
-		update_user_meta($user_id, BASE . 'user_type', $user_type);
-
-		// Process additional fields from form
-		foreach ($_POST as $key => $value) {
-			if (in_array($key, ['name', 'email', 'action', '_wpnonce', 'request_id', 'user_select'])) {
-				continue;
-			}
-			update_user_meta($user_id, BASE . $key, sanitize_text_field($value));
-		}
-
-		// Handle token handlers
-		$this->processTokenHandlers($user_id, $email);
-
-		// Send welcome email with password setup link
-		$this->sendWelcomeEmail($user_id);
-
-		// Trigger registration action for other systems
-		do_action('jvbAfterUserRegistration', $user_id, $user_type, $_POST);
-
-		wp_send_json_success([
-			'message' => 'Registration successful! Check your email.',
-			'title' => $this->labels['successTitle'] ?? 'Success!',
-			'description' => $this->labels['successDescription'] ?? 'Check your email for next steps',
-			'user_id' => $user_id // Important for file upload dependencies!
-		]);
-	}
-
-	public function handleAjaxLostPassword(): void
-	{
-		check_ajax_referer('jvb_lostpassword', '_wpnonce');
-
-		// Rate limiting
-		if (!$this->checkAjaxRateLimit('lostpassword')) {
-			wp_send_json_error([
-				'message' => 'Too many attempts. Please wait a moment.',
-				'code' => 'rate_limit'
-			], 429);
-		}
-
-		$email = sanitize_email($_POST['user_email'] ?? '');
-
-		if (empty($email)) {
-			wp_send_json_error([
-				'message' => 'Email required',
-				'field' => 'user_email',
-				'code' => 'missing_email'
-			]);
-		}
-
-		// Verify Turnstile
-		if (!$this->verifyTurnstile()) {
-			wp_send_json_error([
-				'message' => 'Security verification failed',
-				'code' => 'turnstile_failed'
-			]);
-		}
-
-		// Use WordPress's built-in function
-		$result = retrieve_password($email);
-
-		if (is_wp_error($result)) {
-			wp_send_json_error([
-				'message' => $result->get_error_message(),
-				'code' => $result->get_error_code()
-			]);
-		}
-
-		wp_send_json_success(['message' => 'Check your email for reset link']);
-	}
-
-	public function handleAjaxResetPassword(): void
-	{
-		check_ajax_referer('jvb_resetpass', '_wpnonce');
-
-		// Rate limiting
-		if (!$this->checkAjaxRateLimit('resetpass')) {
-			wp_send_json_error([
-				'message' => 'Too many attempts. Please wait a moment.',
-				'code' => 'rate_limit'
-			], 429);
-		}
-
-		$key = sanitize_text_field($_POST['key'] ?? $_GET['key'] ?? '');
-		$login = sanitize_text_field($_POST['login'] ?? $_GET['login'] ?? '');
-		$pass1 = $_POST['pass1'] ?? '';
-		$pass2 = $_POST['pass2'] ?? '';
-
-		if (empty($key) || empty($login)) {
-			wp_send_json_error([
-				'message' => 'Invalid reset link',
-				'code' => 'invalid_key'
-			]);
-		}
-
-		if (empty($pass1) || empty($pass2)) {
-			wp_send_json_error([
-				'message' => 'Please enter a password',
-				'field' => empty($pass1) ? 'pass1' : 'pass2',
-				'code' => 'missing_password'
-			]);
-		}
-
-		if ($pass1 !== $pass2) {
-			wp_send_json_error([
-				'message' => 'Passwords do not match',
-				'field' => 'pass2',
-				'code' => 'password_mismatch'
-			]);
-		}
-
-		// Verify reset key
-		$user = check_password_reset_key($key, $login);
-
-		if (is_wp_error($user)) {
-			wp_send_json_error([
-				'message' => 'Invalid or expired reset link',
-				'code' => 'invalid_key'
-			]);
-		}
-
-		// Reset password
-		reset_password($user, $pass1);
-
-		wp_send_json_success([
-			'message' => 'Password reset successfully',
-			'redirect' => home_url('/login')
-		]);
-	}
-
-
 	/**********************************************************************
 		TOKEN PROCESSING
 	**********************************************************************/
@@ -1057,40 +661,6 @@
 		}
 	}
 
-
-	/***********************************************************************
-		EMAIL SENDING
-	***********************************************************************/
-	protected function sendWelcomeEmail(int $user_id): void
-	{
-		$user = get_userdata($user_id);
-		if (!$user) {
-			return;
-		}
-
-		// Generate password reset key
-		$key = get_password_reset_key($user);
-		if (is_wp_error($key)) {
-			error_log('Failed to generate password reset key: ' . $key->get_error_message());
-			return;
-		}
-
-		$reset_url = add_query_arg([
-			'action' => 'rp',
-			'key' => $key,
-			'login' => rawurlencode($user->user_login)
-		], home_url('/login'));
-
-		$subject = $this->labels['email'] ?? 'Welcome to ' . get_bloginfo('name');
-
-		$message = '<h2>Welcome, ' . esc_html($user->display_name) . '!</h2>';
-		$message .= '<p>Your account has been created. Click the button below to set your password and get started:</p>';
-		$message .= jvbMailButton($reset_url, 'Set Your Password');
-		$message .= '<p>This link expires in 24 hours.</p>';
-
-		$this->emailManager->sendEmail($user->user_email, $subject, $message);
-	}
-
 	/*************************************************************************
  	*	SECURITY & VALIDATION
 	*************************************************************************/
@@ -1151,18 +721,35 @@
 	protected function setupLabels(): void
 	{
 		$default = $this->getDefaultLabels();
-		$this->labels = apply_filters('jvbLoginLabels', $default, $_GET);
+		$default = apply_filters('jvbLoginLabels', $default, $_GET);
 
-		foreach (['description', 'footer', 'extra'] as $location) {
-			$text = (!is_array($this->labels[$location])) ? [$this->labels[$location]] : $this->labels[$location];
-			if (!empty($text)) {
-				$this->labels[$location] = '<div class="'.$location.'">';
-				foreach ($text as $d) {
-					$this->labels[$location] .= '<p>'.$d.'</p>';
+		if(array_key_exists('type', $_GET) && $_GET['type'] === 'favourites') {
+			if (array_key_exists('favourites', JVB_LOGIN)) {
+				foreach (JVB_LOGIN['favourites'] as $key => $value) {
+					$default[$key] = $value;
 				}
-				$this->labels[$location] .= '</div>';
 			}
 		}
+
+		foreach (['description', 'footer', 'extra'] as $location) {
+			if ($default[$location] === '') {
+				continue;
+			}
+			if (empty($default[$location])) {
+				$default[$location] = '';
+				continue;
+			}
+			$text = (!is_array($default[$location])) ? [$default[$location]] : $default[$location];
+
+			if (!empty($text)) {
+				$default[$location] = '<div class="'.$location.'">';
+				foreach ($text as $d) {
+					$default[$location] .= '<p>'.$d.'</p>';
+				}
+				$default[$location] .= '</div>';
+			}
+		}
+		$this->labels = $default;
 	}
 
 	protected function getDefaultLabels(): array
@@ -1197,6 +784,23 @@
 					'footer' => JVB_LOGIN['reset_pass']['footer'] ?? '',
 					'submit' => JVB_LOGIN['reset_pass']['submit'] ?? 'Reset Password',
 				];
+			case 'logout':
+				return [
+					'title' => JVB_LOGIN['logout']['title'] ?? 'Logged Out!',
+					'description' => JVB_LOGIN['logout']['description'] ?? [],
+					'extra' => JVB_LOGIN['logout']['extra'] ?? [],
+					'footer' => JVB_LOGIN['logout']['footer'] ?? '',
+					'submit' => JVB_LOGIN['logout']['submit'] ?? '',
+				];
+			case 'magic':
+				return [
+					'title' => JVB_LOGIN['magic']['title'] ?? 'Log in with Magic Link',
+					'description' => JVB_LOGIN['magic']['description'] ?? ['Enter your email.','You\'ll get an email with a magic link.','Click it, and you\'re logged in!'],
+					'extra' => JVB_LOGIN['magic']['extra'] ?? [],
+					'footer' => JVB_LOGIN['magic']['footer'] ?? '',
+					'submit' => JVB_LOGIN['magic']['submit'] ?? jvbIcon('magic-wand').'Send Magic Link',
+
+				];
 			case 'login':
 			default:
 				return [
@@ -1215,33 +819,10 @@
 			return;
 		}
 		?>
-		<button type="button" id="magic-link-btn" class="button button-secondary button-large">
-			<?= jvbIcon('email', ['size' => 20]); ?>
-			Get Login Link
-		</button>
-		<script type="text/javascript">
-			document.getElementById('magic-link-btn')?.addEventListener('click', function(e) {
-				e.preventDefault();
-				const email = document.querySelector('input[name="user_email"]')?.value;
-				if (!email) {
-					alert('Please enter your email address first');
-					return;
-				}
-
-				fetch('<?= rest_url('jvb/v1/magic-link'); ?>', {
-					method: 'POST',
-					headers: {
-						'Content-Type': 'application/json',
-						'X-WP-Nonce': '<?= wp_create_nonce('wp_rest') ?>'
-					},
-					body: JSON.stringify({ email: email, type: 'login' })
-				})
-				.then(r => r.json())
-				.then(data => {
-					alert(data.success ? 'Check your email!' : (data.message || 'Failed to send link'));
-				});
-			});
-		</script>
+		<a class="button" href="<?= add_query_arg('action', 'magic', wp_login_url()) ?>" title="Email yourself a link to log you in auto-magically!">
+			<?= jvbIcon('magic-wand'); ?>
+			Magic Link
+		</a>
 		<?php
 	}
 
@@ -1257,20 +838,159 @@
 
 		$this->maybeTurnstileScripts();
 		wp_enqueue_script('jvb-form');
-
-		$script = "
+		$action = $this->getAction();
+		ob_start();
+		?>
+		<script type="text/javascript">
+		window.checkedEmails = new Set();
 		document.addEventListener('DOMContentLoaded', () => {
 			const form = document.querySelector('.login form');
-			if (form && window.jvbForm) {
-				let controller = new window.jvbForm();
-				controller.registerForm(form, {
-					autosave: false,
-					endpoint: false
-				});
-			} else if (form && !window.jvbForm) {
+			if (!form) return;
+
+			if (!window.jvbForm) {
 				console.error('jvbForm not loaded');
+				return;
 			}
-		});";
+
+			window.LoginController = new window.jvbForm();
+			window.LoginController.registerForm(form, {
+				autosave: false,
+				endpoint: <?= "'{$action}'" ?>,
+				formStatus: false,
+				cache: false,
+			});
+
+			window.LoginController.subscribe((event, data) => {
+				if (event === 'form-submit') {
+					handleFormSubmission(data);
+				} else if (event === 'field-validated' && data.name === 'user_email') {
+					if (!window.checkedEmails.has(data.value)){
+						checkEmail(data.value, data);
+					}
+				}
+			});
+
+
+			async function handleFormSubmission(data) {
+				const { formId, config, data: formData } = data;
+				const form = config.element;
+				const submit = form.querySelector('[type=submit]');
+				let oldText = submit.textContent;
+				// Show uploading status
+				window.LoginController.showFormStatus(formId, 'uploading');
+
+				try {
+					submit.disabled = true;
+					submit.textContent = 'Loading...';
+					const response = await fetch(`${jvbSettings.api}<?=($action === 'magic') ? $action : 'auth/'.$action?>`, {
+						method: 'POST',
+						headers: {
+							'Content-Type': 'application/json',
+							'X-WP-Nonce': jvbSettings.nonce
+						},
+						body: JSON.stringify(formData)
+					});
+
+					const result = await response.json();
+
+					// Handle errors
+					if (!response.ok) {
+						window.LoginController.showFormStatus(formId, 'error');
+						window.LoginController.handleFormError(form, result);
+						return;
+					}
+
+					// Handle success
+					window.LoginController.showFormStatus(formId, 'submitted');
+
+					// Show success message briefly before redirect
+					if (result.message) {
+						window.LoginController.handleFormSuccess(form, result);
+					}
+
+					// Handle redirect
+					if (result.redirect) {
+						setTimeout(() => {
+							window.location.href = result.redirect;
+						}, 500); // Brief delay to show success message
+					}
+
+				} catch (error) {
+					console.error('Form submission error:', error);
+					window.LoginController.showFormStatus(formId, 'error');
+					window.LoginController.handleFormError(form, {
+						message: 'Network error. Please check your connection and try again.',
+						code: 'network_error'
+					});
+				} finally {
+					submit.textContent = oldText;
+					submit.disabled = false;
+				}
+			}
+			async function checkEmail(email, input) {
+				window.checkedEmails.add(email);
+				let wrapper = input.closest('.field');
+				let submit = input.closest('form').querySelector('[type=submit]');
+				try {
+					submit.disabled = true;
+					window.LoginController.showSuccess(wrapper, 'Checking our records...');
+					const response = await fetch(`${jvbSettings.api}auth/email`, {
+						method: 'POST',
+						headers: {
+							'Content-Type': 'application/json',
+							'X-WP-Nonce': jvbSettings.nonce
+						},
+						body: JSON.stringify({ email: email })
+					});
+
+					const result = await response.json();
+
+					if (!response.ok) {
+						return; // On error, allow to proceed (fail open)
+					}
+
+					if (result.exists) {
+						<?php
+						switch ($action) {
+							case 'register':
+								echo 'window.LoginController.showError(wrapper,\'This email is already registered. Log in instead?\');';
+								break;
+							case 'login':
+							case 'lostpassword':
+							case 'magic':
+								echo 'window.LoginController.showSuccess(wrapper,\'Email exists in our system.\');';
+								break;
+						}
+						?>
+					} else {
+						<?php
+						switch ($action) {
+							case 'register':
+								echo 'window.LoginController.showSuccess(wrapper,\'Email is available!\');';
+								break;
+							case 'login':
+							case 'lostpassword':
+							case 'magic':
+								echo 'window.LoginController.showError(wrapper,\'This email doesn\\\'t seem to exist in our system. Create account instead?\');';
+								break;
+						}
+						?>
+					}
+
+					return true; // Email is available
+				} catch (error) {
+					console.error('Email check failed:', error);
+					return true; // On network error, allow to proceed
+				}finally {
+					submit.disabled = false;
+				}
+			}
+		});
+
+
+		</script>
+		<?php
+		$script = ob_get_clean();
 
 		wp_add_inline_script('jvb-form', $script);
 	}
diff --git a/inc/managers/LoginManagerOld.php b/inc/managers/LoginManagerOld.php
index 066e744..a2fa1b7 100644
--- a/inc/managers/LoginManagerOld.php
+++ b/inc/managers/LoginManagerOld.php
@@ -97,7 +97,7 @@
 		<script type="text/javascript">
 			document.addEventListener('DOMContentLoaded', function() {
 				let loginLabel = document.querySelector('label[for="user_login"');
-				loginLabel.innerHTML = '<?= jvbIcon('email', ['size' => 20]); ?> Your Email';
+				loginLabel.innerHTML = '<?= jvbIcon('envelope', ['size' => 20]); ?> Your Email';
 
 				let passwordLabel = document.querySelector('label[for="user_pass"');
 				passwordLabel.innerHTML = '<?= jvbIcon('password', ['size' => 20]); ?> Your Password';
@@ -580,9 +580,9 @@
 			<input type="radio" id="enthusiast" name="user_type" value="enthusiast" required <?= ($this->fromFavourites()) ? 'checked' : '' ?>>
 			<label for="enthusiast"><?=jvbIcon('heart', ['title' =>'Enthusiast', 'size'=>40])?><h4>Enthusiast</h4><p>Start here.</p></label>
 			<input type="radio" id="artist" name="user_type" value="artist" required>
-			<label for="artist"><?=jvbIcon('tattoo', ['title'=> 'Artist', 'size'=> 40])?><h4>Artist</h4><p>Show your talent.</p></label>
+			<label for="artist"><?=jvbIcon('drop-simple', ['title'=> 'Artist', 'size'=> 40])?><h4>Artist</h4><p>Show your talent.</p></label>
 			<input type="radio" id="partner" name="user_type" value="partner" required>
-			<label for="partner"><?=jvbIcon('partner', ['title'=>'Partner', 'size' => 40])?><h4>Partner</h4><p>Support the community.</p></label>
+			<label for="partner"><?=jvbIcon('currency-circle-dollar', ['title'=>'Partner', 'size' => 40])?><h4>Partner</h4><p>Support the community.</p></label>
 			<p class="enthusiast">Save your favourites. Get notified.</p>
 			<p class="artist">Show off your work.</p>
 			<p class="partner">Support the community.</p>
diff --git a/inc/managers/MagicLinkManager.php b/inc/managers/MagicLinkManager.php
index 6b91d7a..466d3b9 100644
--- a/inc/managers/MagicLinkManager.php
+++ b/inc/managers/MagicLinkManager.php
@@ -38,9 +38,7 @@
 		// Hook into WordPress auth flow
 		add_action('template_redirect', [$this, 'handleMagicLinkClick']);
 		add_action('wp_login_failed', [$this, 'handleFailedLogin']);
-
-		// NOTE: LoginManager now handles the login form UI
-		// If magic_link integration is enabled, LoginManager will call addMagicLinkOption()
+		add_action('jvb_process_login_tokens', [$this, 'processRegistrationToken'], 10, 3);
 	}
 
 	/**
@@ -105,7 +103,7 @@
 	/**
 	 * Verify a token
 	 */
-	protected function verifyToken(string $token, string $email): array|WP_Error
+	public function verifyToken(string $token, string $email): array|WP_Error
 	{
 		$token_data = $this->cache->get($token);
 
@@ -171,7 +169,7 @@
 
 		$magic_url = add_query_arg([
 			'magic_token' => $token,
-			'email' => urlencode($email),
+			'email' => rawurlencode($email),
 			'action' => 'magic_login'
 		], home_url('/'));
 
@@ -207,7 +205,7 @@
 
 		$magic_url = add_query_arg([
 			'magic_token' => $token,
-			'email' => urlencode($email),
+			'email' => rawurlencode($email),
 			'action' => 'magic_signup'
 		], home_url('/'));
 
@@ -238,7 +236,7 @@
 
 		$magic_url = add_query_arg([
 			'magic_token' => $token,
-			'email' => urlencode($email),
+			'email' => rawurlencode($email),
 			'action' => 'magic_referral'
 		], home_url('/'));
 
@@ -269,7 +267,7 @@
 
 		$magic_url = add_query_arg([
 			'magic_token' => $token,
-			'email' => urlencode($email),
+			'email' => rawurlencode($email),
 			'action' => 'magic_reset'
 		], home_url('/'));
 
diff --git a/inc/managers/NotificationManager.php b/inc/managers/NotificationManager.php
index abac97a..db4a598 100644
--- a/inc/managers/NotificationManager.php
+++ b/inc/managers/NotificationManager.php
@@ -435,6 +435,10 @@
      */
     public function trackContentCreation(int $post_id, WP_POST $post, bool $update):void
     {
+		// SAFETY: Skip attachments and other non-content post types
+		if (in_array($post->post_type, jvbIgnoredPostTypes())) {
+			return;
+		}
         // Skip if not a published post
         if ($post->post_status !== 'publish') {
             return;
diff --git a/inc/managers/OperationQueue.php b/inc/managers/OperationQueue.php
index b4e3c9c..07f2104 100644
--- a/inc/managers/OperationQueue.php
+++ b/inc/managers/OperationQueue.php
@@ -3,6 +3,7 @@
 
 use JVBase\managers\CacheManager;
 use Exception;
+use JVBase\utility\Features;
 use WP_Error;
 use WP_REST_Response;
 use WP_REST_Request;
@@ -598,7 +599,9 @@
 	}
 
 	protected function updateLastModified(int $user_id) {
-		JVB()->routes('queue')->updateUserQueueTimestamp($user_id);
+		if (Features::forSite()->has('dashboard')) {
+			JVB()->routes('queue')->updateUserQueueTimestamp($user_id);
+		}
 	}
 
 	protected function deepMerge(array $existing, array $new): array
diff --git a/inc/managers/ReferralManager.php b/inc/managers/ReferralManager.php
index 1bac16a..3ac4511 100644
--- a/inc/managers/ReferralManager.php
+++ b/inc/managers/ReferralManager.php
@@ -24,6 +24,7 @@
 	protected MagicLinkManager $magic_link;
 	protected CacheManager $cache;
 	protected string $referrals_table;
+	protected ?int $referralPage = null;
 	protected string $rewards_table;
 
 	// Default reward settings
@@ -36,6 +37,8 @@
 		'referee_reward_applies_to' => 'first_order'  // 'first_order' or 'all_orders'
 	];
 
+	protected array $settings;
+
 	public function __construct()
 	{
 		global $wpdb;
@@ -45,8 +48,13 @@
 		$this->rewards_table = BASE . 'referral_rewards';
 		$this->magic_link = new MagicLinkManager();
 
-		// Hook into user registration to track referrals
-		add_action('user_register', [$this, 'processReferral'], 10, 1);
+		$this->referralPage = $this->getReferralPageId();
+		$this->settings = $this->getRewardSettings();
+
+
+		add_action('jvbUserRegistered', [$this, 'processRegistrationToken'], 10, 3);
+		add_action('jvb_add_token_inputs', [$this, 'addLoginInputs'], 10, 1);
+		add_action('jvbUserRegistered', [$this, 'processReferral'], 10, 1);
 
 		// Add meta boxes for admin to manage referrals
 		add_action('show_user_profile', [$this, 'displayUserReferralInfo']);
@@ -56,7 +64,7 @@
 		add_action('personal_options_update', [$this, 'saveUserReferralCode']);
 		add_action('edit_user_profile_update', [$this, 'saveUserReferralCode']);
 
-		add_filter(BASE.'new_user_email_content', [$this, 'addReferralToWelcomeEmail'], 99, 2);
+		add_filter('jvbNewUserEmail', [$this, 'addReferralToWelcomeEmail'], 99, 2);
 
 
 		add_filter('jvbAdditionalActions', [$this, 'outputShareWidget']);
@@ -66,7 +74,7 @@
 		$this->registerCronJobs();
 
 		// Register admin subpage
-		add_filter('jvbAdminSubpages', [$this, 'addSubpage'], 10, 1);
+//		add_filter('jvbAdminSubpages', [$this, 'addSubpage'], 10, 1);
 
 		// Add admin bar label for referral page
 		add_action('admin_bar_menu', [$this, 'addReferralPageLabel'], 999);
@@ -81,6 +89,37 @@
 		add_action('admin_init', [$this, 'registerSettings']);
 	}
 
+	public function addLoginInputs(string $action):void
+	{
+		if (array_key_exists('ref', $_GET)) {
+			echo '<input type="hidden" name="referral_code" value="'.$_GET['ref'].'">';
+		}
+	}
+
+	public function modifyLoginText(array $defaults):array
+	{
+		if (!array_key_exists('ref', $_GET)){
+			return $defaults;
+		}
+
+		$code = $_GET['ref'];
+		$user = $this->getUserByReferralCode($code);
+
+		$desc = ($user) ? strtok($user->display_name, ' ') . ' invited you ' : 'You\'ve been invited ';
+
+		$defaults['title'] = 'Register your Account. Get Your Reward.';
+		$defaults['description'] = [
+			$desc.' to see the difference with us.',
+			'Oh, and you\'ll get 20% off your first treatment!',
+			'Finish this account creation (you\'ll still need to make an account on Jane App to book), or let us know when you come in for your appointment.'
+		];
+		$defaults['extra'] = [
+			'Once you\'re in, you\'ll get your own code you can share.',
+			'Get a $25 credit for <b>each</b> person you send our way who comes in for their first treatment!'
+		];
+		return $defaults;
+	}
+
 	public function enqueueScripts():void
 	{
 		$requirements = [
@@ -205,17 +244,47 @@
 		return !empty($users);
 	}
 
+	public function processRegistrationToken(int $user_id, string $email, array $data): void
+	{
+		// Check for referral code in data
+		$code = $data['referral_code'] ?? '';
+		if (empty($code)) {
+			return;
+		}
+
+		// Store in session/cookie for processReferral to pick up
+		if (session_status() === PHP_SESSION_NONE) {
+			session_start();
+		}
+		$_SESSION[BASE . 'referral_code'] = sanitize_text_field($code);
+		setcookie(
+			BASE . 'referral_code',
+			sanitize_text_field($code),
+			time() + (86400 * 30),
+			'/'
+		);
+	}
 	/**
 	 * Track a new referral when user registers
 	 *
 	 * @param int $user_id
 	 */
-	public function processReferral(int $user_id): void
+	public function processReferral(int $user_id, string $email, array $data): void
 	{
 		// Check if user was created via referral magic link
-		$referral_code = get_user_meta($user_id, BASE . 'pending_referral_code', true);
+		// Try to get code from multiple sources
+    	$referral_code = $data['referral_code'] ??
+		get_user_meta($user_id, BASE . 'pending_referral_code', true);
 
-		if (!$referral_code) {
+		// Check session/cookie if not in data
+		if (empty($referral_code)) {
+			if (session_status() === PHP_SESSION_NONE) {
+				session_start();
+			}
+			$referral_code = $_SESSION[BASE . 'referral_code'] ?? $_COOKIE[BASE . 'referral_code'] ?? '';
+		}
+
+		if (empty($referral_code)) {
 			return;
 		}
 
@@ -254,7 +323,7 @@
 	 * @param string $code
 	 * @return int|false
 	 */
-	protected function createReferral(int $referrer_id, int $referee_id, string $code)
+	public function createReferral(int $referrer_id, int $referee_id, string $code)
 	{
 		$user = get_user_by('ID', $referee_id);
 
@@ -282,13 +351,17 @@
 	 */
 	public function getUserByReferralCode(string $code): ?WP_User
 	{
-		$users = get_users([
-			'meta_key' => BASE . 'referral_code',
-			'meta_value' => $code,
-			'number' => 1
-		]);
-
-		return $users[0] ?? null;
+		return $this->cache->remember(
+			$code,
+			function () use ($code) {
+				$users = get_users([
+					'meta_key' => BASE . 'referral_code',
+					'meta_value' => $code,
+					'number' => 1
+				]);
+				return $users[0] ?? null;
+			}
+		);
 	}
 
 	/**
@@ -353,8 +426,6 @@
 			return;
 		}
 
-		$settings = $this->getRewardSettings();
-
 		// Create referrer reward
 		$this->wpdb->insert(
 			$this->rewards_table,
@@ -362,7 +433,7 @@
 				'referral_id' => $referral_id,
 				'user_id' => $referral->referrer_id,
 				'reward_type' => 'referrer',
-				'amount' => $settings['referrer_reward_amount'],
+				'amount' => $this->settings['referrer_reward_amount'],
 				'status' => 'available',
 				'created_at' => current_time('mysql')
 			],
@@ -370,9 +441,9 @@
 		);
 
 		// Create referee reward
-		$referee_amount = $settings['referee_reward_type'] === 'percentage'
-			? $settings['referee_reward_amount']  // Store as percentage
-			: $settings['referee_reward_amount'];  // Store as fixed amount
+		$referee_amount = $this->settings['referee_reward_type'] === 'percentage'
+			? $this->settings['referee_reward_amount']  // Store as percentage
+			: $this->settings['referee_reward_amount'];  // Store as fixed amount
 
 		$this->wpdb->insert(
 			$this->rewards_table,
@@ -381,7 +452,7 @@
 				'user_id' => $referral->referee_id,
 				'reward_type' => 'referee',
 				'amount' => $referee_amount,
-				'reward_calculation' => $settings['referee_reward_type'],
+				'reward_calculation' => $this->settings['referee_reward_type'],
 				'status' => 'available',
 				'created_at' => current_time('mysql')
 			],
@@ -998,9 +1069,8 @@
 			return '';
 		}
 
-		$settings = $this->getRewardSettings();
-		$reward_amount = $settings['referee_reward_amount'] ?? 20;
-		$reward_type = $settings['referee_reward_type'] ?? 'percentage';
+		$reward_amount = $this->settings['referee_reward_amount'] ?? 20;
+		$reward_type = $this->settings['referee_reward_type'] ?? 'percentage';
 
 		$reward_text = $reward_type === 'percentage'
 			? $reward_amount . '% off'
@@ -1036,7 +1106,7 @@
 		return ob_get_clean();
 	}
 
-	function getLoggedInReferral(int $user_id):string
+	public function getLoggedInReferral(int $user_id):string
 	{
 		// Logged-in user widget
 		$referral_code = get_user_meta($user_id, BASE . 'referral_code', true);
@@ -1049,7 +1119,7 @@
 			}
 		}
 
-		$share_url = home_url('/?ref=' . $referral_code);
+		$share_url = $this->getShareURL($referral_code);
 
 		ob_start();
 		?>
@@ -1069,14 +1139,14 @@
 			   target="_blank"
 			   rel="noopener noreferrer"
 			   class="share-btn facebook-share">
-				<?php echo jvbIcon('facebook', ['size' => 20]); ?>
+				<?php echo jvbIcon('facebook-logo', ['size' => 20]); ?>
 				Facebook
 			</a>
 			<a href="https://twitter.com/intent/tweet?url=<?php echo urlencode($share_url); ?>&text=<?php echo urlencode('Check this out!'); ?>"
 			   target="_blank"
 			   rel="noopener noreferrer"
 			   class="share-btn twitter-share">
-				<?php echo jvbIcon('twitter', ['size' => 20]); ?>
+				<?php echo jvbIcon('twitter-logo', ['size' => 20]); ?>
 				Twitter
 			</a>
 		</div>
@@ -1129,27 +1199,52 @@
 			return $content;
 		}
 
-		$settings = get_option(BASE . 'referral_settings', []);
-		$reward_amount = $settings['referee_reward_amount'] ?? 20;
-		$reward_type = $settings['referee_reward_type'] ?? 'percentage';
+		$reward_amount = $this->settings['referee_reward_amount'] ?? 20;
+		$reward_type = $this->settings['referee_reward_type'] ?? 'percentage';
 
 		$reward_text = $reward_type === 'percentage'
 			? $reward_amount . '% off'
 			: '$' . number_format($reward_amount, 2) . ' off';
 
-		$bonus_content = '<div style="background: #e7f5ff; padding: 20px; border-radius: 8px; margin: 20px 0;">';
-		$bonus_content .= '<h3 style="margin-top: 0; color: #2271b1;">🎉 Welcome Bonus!</h3>';
-		$bonus_content .= '<p>Since you were referred by a friend, you\'ve earned <strong>' . $reward_text . '</strong> your first booking!</p>';
-		$bonus_content .= '<p>Your reward will be automatically applied when you book.</p>';
-		$bonus_content .= '</div>';
+		$bonus_content = sprintf(
+			'
+			<h3>Thanks for the ♡</h3>
+			<p>Since you were referred by a friend, you\'ve earned <strong>%s</strong> your first booking!</p>
+			<p>Your reward will be automatically applied when you book.</p>',
+			$reward_text,
+		);
 
-		// Insert bonus content after the first paragraph
-		$parts = explode('</p>', $content, 2);
-		if (count($parts) === 2) {
-			return $parts[0] . '</p>' . $bonus_content . $parts[1];
+
+		$code = $this->getUserReferralCode($user->ID);
+		$yourCode = '';
+		if (!is_wp_error($code)) {
+			$share_url = $this->getShareURL($code);
+			$yourCode = sprintf(
+				'<div class="callout">
+			<h3>Share the ♡ with Friends</h3>
+			<p>If you find you love what we can do for you, you can share your own code!</p>
+			<p>Your Referral Code: <strong>%s</strong></p>
+			<p>Or click the button below:</p>
+			%s
+			</div>',
+				jvbEmailLink($code),
+				jvbMailButton($share_url, 'Share Your Code')
+			);
 		}
 
-		return $content . $bonus_content;
+
+		return $content . $bonus_content . $yourCode;
+	}
+
+	public function getShareURL(string $code):string
+	{
+		return add_query_arg(
+			[
+				'ref' => $code,
+				'action'	=> 'register'
+			],
+			wp_login_url()
+		);
 	}
 
 	/**
@@ -1197,10 +1292,9 @@
 		}
 
 		// Get reward text for email
-		$settings = $this->getRewardSettings();
-		$reward_text = $settings['referee_reward_type'] === 'percentage'
-			? "Get {$settings['referee_reward_amount']}% off your first treatment!"
-			: "Get \${$settings['referee_reward_amount']} off your first treatment!";
+		$reward_text = $this->settings['referee_reward_type'] === 'percentage'
+			? "Get {$this->settings['referee_reward_amount']}% off your first treatment!"
+			: "Get \${$this->settings['referee_reward_amount']} off your first treatment!";
 
 		// Record the invitation attempt (for tracking)
 		$this->recordInvitationAttempt($user_id, $invitee_email, $invitee_name);
@@ -1555,9 +1649,6 @@
 			echo '<div class="notice notice-success is-dismissible"><p>Settings saved successfully.</p></div>';
 		}
 
-		$referral_page_id = $this->getReferralPageId();
-		$settings = $this->getRewardSettings();
-
 		echo $this->renderAdminHTML();
 	}
 
@@ -1585,7 +1676,7 @@
 								wp_dropdown_pages([
 									'name' => BASE . 'referral_page_id',
 									'id' => BASE . 'referral_page_id',
-									'selected' => $referral_page_id,
+									'selected' => $this->referralPage,
 									'show_option_none' => __('— Select —', 'jvbase'),
 									'option_none_value' => '0'
 								]);
@@ -1611,8 +1702,8 @@
 							</th>
 							<td>
 								<select name="referrer_reward_type" id="referrer_reward_type">
-									<option value="fixed" <?php selected($settings['referrer_reward_type'], 'fixed'); ?>>Fixed Amount</option>
-									<option value="percentage" <?php selected($settings['referrer_reward_type'], 'percentage'); ?>>Percentage</option>
+									<option value="fixed" <?php selected($this->settings['referrer_reward_type'], 'fixed'); ?>>Fixed Amount</option>
+									<option value="percentage" <?php selected($this->settings['referrer_reward_type'], 'percentage'); ?>>Percentage</option>
 								</select>
 							</td>
 						</tr>
@@ -1624,7 +1715,7 @@
 								<input type="number"
 									   name="referrer_reward_amount"
 									   id="referrer_reward_amount"
-									   value="<?= esc_attr($settings['referrer_reward_amount']) ?>"
+									   value="<?= esc_attr($this->settings['referrer_reward_amount']) ?>"
 									   step="0.01"
 									   min="0">
 								<p class="description">Amount in dollars or percentage</p>
@@ -1636,8 +1727,8 @@
 							</th>
 							<td>
 								<select name="referrer_reward_applies_to" id="referrer_reward_applies_to">
-									<option value="per_user" <?php selected($settings['referrer_reward_applies_to'], 'per_user'); ?>>Per User Referred</option>
-									<option value="flat_total" <?php selected($settings['referrer_reward_applies_to'], 'flat_total'); ?>>Flat Total</option>
+									<option value="per_user" <?php selected($this->settings['referrer_reward_applies_to'], 'per_user'); ?>>Per User Referred</option>
+									<option value="flat_total" <?php selected($this->settings['referrer_reward_applies_to'], 'flat_total'); ?>>Flat Total</option>
 								</select>
 							</td>
 						</tr>
@@ -1651,8 +1742,8 @@
 							</th>
 							<td>
 								<select name="referee_reward_type" id="referee_reward_type">
-									<option value="percentage" <?php selected($settings['referee_reward_type'], 'percentage'); ?>>Percentage</option>
-									<option value="fixed" <?php selected($settings['referee_reward_type'], 'fixed'); ?>>Fixed Amount</option>
+									<option value="percentage" <?php selected($this->settings['referee_reward_type'], 'percentage'); ?>>Percentage</option>
+									<option value="fixed" <?php selected($this->settings['referee_reward_type'], 'fixed'); ?>>Fixed Amount</option>
 								</select>
 							</td>
 						</tr>
@@ -1664,7 +1755,7 @@
 								<input type="number"
 									   name="referee_reward_amount"
 									   id="referee_reward_amount"
-									   value="<?= esc_attr($settings['referee_reward_amount']) ?>"
+									   value="<?= esc_attr($this->settings['referee_reward_amount']) ?>"
 									   step="0.01"
 									   min="0">
 								<p class="description">Amount in dollars or percentage</p>
@@ -1676,8 +1767,8 @@
 							</th>
 							<td>
 								<select name="referee_reward_applies_to" id="referee_reward_applies_to">
-									<option value="first_order" <?php selected($settings['referee_reward_applies_to'], 'first_order'); ?>>First Order Only</option>
-									<option value="all_orders" <?php selected($settings['referee_reward_applies_to'], 'all_orders'); ?>>All Orders</option>
+									<option value="first_order" <?php selected($this->settings['referee_reward_applies_to'], 'first_order'); ?>>First Order Only</option>
+									<option value="all_orders" <?php selected($this->settings['referee_reward_applies_to'], 'all_orders'); ?>>All Orders</option>
 								</select>
 							</td>
 						</tr>
@@ -1744,16 +1835,14 @@
 			return;
 		}
 
-		$referral_page_id = $this->getReferralPageId();
-
-		if (!$referral_page_id) {
+		if (!$this->referralPage) {
 			return;
 		}
 
 		global $pagenow, $post;
 
 		// Check if we're editing the referral page
-		if ('post.php' === $pagenow && $post && $post->ID === $referral_page_id) {
+		if ('post.php' === $pagenow && $post && $post->ID === $this->referralPage) {
 			$wp_admin_bar->add_node([
 				'id' => 'referral-page',
 				'parent' => 'top-secondary',
@@ -1787,9 +1876,7 @@
 			return;
 		}
 
-		$referral_page_id = $this->getReferralPageId();
-
-		if ($post->ID === $referral_page_id) {
+		if ($post->ID === $this->referralPage) {
 			echo '<div class="notice notice-info">';
 			echo '<p>' . __('This page is designated as the <strong>Referral Page</strong>.', 'jvbase') . '</p>';
 			echo '</div>';
diff --git a/inc/managers/RegisterManager.php b/inc/managers/RegisterManager.php
index f60fc30..185a1d1 100644
--- a/inc/managers/RegisterManager.php
+++ b/inc/managers/RegisterManager.php
@@ -746,9 +746,9 @@
             <input type="radio" id="enthusiast" name="user_type" value="enthusiast" required <?= ($this->fromFavourites()) ? 'checked' : '' ?>>
             <label for="enthusiast"><?=jvbIcon('heart', ['title' =>'Enthusiast', 'size'=>40])?><h4>Enthusiast</h4><p>Start here.</p></label>
             <input type="radio" id="artist" name="user_type" value="artist" required>
-            <label for="artist"><?=jvbIcon('tattoo', ['title'=> 'Artist', 'size'=> 40])?><h4>Artist</h4><p>Show your talent.</p></label>
+            <label for="artist"><?=jvbIcon('drop-simple', ['title'=> 'Artist', 'size'=> 40])?><h4>Artist</h4><p>Show your talent.</p></label>
             <input type="radio" id="partner" name="user_type" value="partner" required>
-            <label for="partner"><?=jvbIcon('partner', ['title'=>'Partner', 'size' => 40])?><h4>Partner</h4><p>Support the community.</p></label>
+            <label for="partner"><?=jvbIcon('currency-circle-dollar', ['title'=>'Partner', 'size' => 40])?><h4>Partner</h4><p>Support the community.</p></label>
             <p class="enthusiast">Save your favourites. Get notified.</p>
             <p class="artist">Show off your work.</p>
             <p class="partner">Support the community.</p>
diff --git a/inc/managers/TaxonomyRelationships.php b/inc/managers/TaxonomyRelationships.php
index 9ce35f5..2f466a7 100644
--- a/inc/managers/TaxonomyRelationships.php
+++ b/inc/managers/TaxonomyRelationships.php
@@ -4,6 +4,7 @@
 use JVBase\JVB;
 use JVBase\managers\CacheManager;
 use WP_Error;
+use WP_Post;
 
 if (!defined('ABSPATH')) {
     exit; // Exit if accessed directly
@@ -42,8 +43,8 @@
      */
     public function init():void
     {
-        add_action('save_post', [$this, 'updatePostRelationships']);
-        add_action('before_delete_post', [$this, 'updatePostRelationships']);
+        add_action('save_post', [$this, 'updatePostRelationships'], 10, 2);
+        add_action('before_delete_post', [$this, 'updatePostRelationships'], 10, 2);
         add_action('delete_term', [$this, 'deleteTermRelationships']);
 
         add_filter(BASE.'handle_bulk_operation', [$this, 'processOperation'], 10, 3);
@@ -57,13 +58,13 @@
      *
      * @return void
      */
-    public function updatePostRelationships(int $post_id):void
+    public function updatePostRelationships(int $post_id, WP_Post $post):void
     {
-        $this->cache->invalidate();
-        $post_type = get_post_type($post_id);
-		if (in_array($post_type, [BASE.'directory', BASE.'dash'])) {
+		$post_type = $post->post_type;
+		if (in_array($post_type, jvbIgnoredPostTypes())) {
 			return;
 		}
+        $this->cache->invalidate();
         // Get all taxonomies for this post type
         $taxonomies = get_object_taxonomies($post_type);
 
diff --git a/inc/managers/UserTermsManager.php b/inc/managers/UserTermsManager.php
index 5eb29f2..b900dff 100644
--- a/inc/managers/UserTermsManager.php
+++ b/inc/managers/UserTermsManager.php
@@ -64,6 +64,10 @@
         if (wp_is_post_autosave($post_id) || wp_is_post_revision($post_id)) {
             return;
         }
+		// SAFETY: Skip attachments and other non-content post types
+		if (in_array($post->post_type, jvbIgnoredPostTypes())) {
+			return;
+		}
 
         // Skip non-custom post types
         $post_type = get_post_type($post);
@@ -409,12 +413,13 @@
         $this->wpdb->query("TRUNCATE TABLE {$this->table_name}");
 
         // Get all users with posts
-        $users = $this->wpdb->get_col("
-            SELECT DISTINCT post_author
-            FROM {$this->wpdb->posts}
-            WHERE post_status = 'publish'
-            AND post_type LIKE '" . BASE . "%'
-        ");
+		$users = $this->wpdb->get_col($this->wpdb->prepare(
+			"SELECT DISTINCT post_author
+			FROM {$this->wpdb->posts}
+			WHERE post_status = 'publish'
+			AND post_type LIKE %s",
+			$this->wpdb->esc_like(BASE) . '%'
+		));
 
         JVB()->queue()->queueOperation(
             'rebuild_user_term_index',
@@ -490,14 +495,14 @@
                 $this->clearUserCache($user_id);
 
                 // Get all the user's published posts
-                $posts = $this->wpdb->get_col($this->wpdb->prepare(
-                    "SELECT ID FROM {$this->wpdb->posts}
-            WHERE post_author = %d
-            AND post_status = 'publish'
-            AND post_type LIKE '%s'",
-                    $user_id,
-					BASE
-                ));
+				$posts = $this->wpdb->get_col($this->wpdb->prepare(
+					"SELECT ID FROM {$this->wpdb->posts}
+					WHERE post_author = %d
+					AND post_status = 'publish'
+					AND post_type LIKE %s",
+					$user_id,
+					$this->wpdb->esc_like(BASE) . '%'
+				));
 
                 $processed_terms = 0;
 
diff --git a/inc/managers/_setup.php b/inc/managers/_setup.php
index f128055..5124262 100644
--- a/inc/managers/_setup.php
+++ b/inc/managers/_setup.php
@@ -1,8 +1,9 @@
 <?php
 use JVBase\utility\Features;
 
+
 require(JVB_DIR . '/inc/managers/CacheManager.php');
-require(JVB_DIR . '/icons.php');
+require(JVB_DIR . '/inc/managers/IconsManager.php');
 require(JVB_DIR . '/inc/managers/ErrorHandler.php');
 require(JVB_DIR . '/inc/managers/OperationQueue.php');
 require(JVB_DIR . '/inc/managers/EmailManager.php');
@@ -15,9 +16,9 @@
 
 
 //IF SITE HAS DASHBOARD AND FEED BLOCK
-if (Features::forSite()->hasAny(['dashboard', 'feed_block'])) {
+//if (Features::forSite()->hasAny(['dashboard', 'feed_block'])) {
 	require(JVB_DIR . '/inc/forms/TaxonomySelector.php');
-}
+//}
 
 //IF SITE HAS DASHBOARD
 if (Features::forSite()->has('dashboard')) {
diff --git a/inc/meta/MetaForm.php b/inc/meta/MetaForm.php
index cf1e401..21895b9 100644
--- a/inc/meta/MetaForm.php
+++ b/inc/meta/MetaForm.php
@@ -426,7 +426,7 @@
 						class="decrease"
 						title="<?= array_key_exists('remove', $field) ? $field['remove'] : 'Decrease amount' ?>"
 						aria-label="Decrease <?= esc_attr($field['label']) ?>">
-					<?= jvbIcon('minus') ?>
+					<?= jvbIcon('minus-square') ?>
 				</button>
 
 				<input type="number"
@@ -445,7 +445,7 @@
 						class="increase"
 						title="<?= array_key_exists('add', $field) ? $field['add'] : 'Increase amount' ?>"
 						aria-label="Increase <?= esc_attr($field['label']) ?>">
-					<?= jvbIcon('add') ?>
+					<?= jvbIcon('plus-square') ?>
 				</button>
 			</div>
 
@@ -678,11 +678,11 @@
 		<div class="repeater-row" data-index="<?= esc_attr($index); ?>">
 			<details <?= (is_string($index)) ? 'open' : ''; ?>>
 				<summary class="repeater-row-header row btw">
-					<span class="drag-handle"><?= jvbIcon('grab'); ?></span>
+					<span class="drag-handle"><?= jvbIcon('dots-six-vertical'); ?></span>
 					<span class="row-number">#<?= esc_html($display_number); ?></span>
 					<span class="row-title"><?= esc_html($this->getRowTitle($fields, $values, $rowTitle)); ?></span>
 					<button type="button" class="remove-row" title="Remove">
-						<?= jvbIcon('delete', ['title'=>'Remove']); ?>
+						<?= jvbIcon('trash', ['title'=>'Remove']); ?>
 					</button>
 				</summary>
 				<div class="repeater-row-content">
@@ -931,18 +931,18 @@
 
 							<div class="selection-actions row btw" hidden>
 								<button type="button" data-action="add-to-group">
-									<?= jvbIcon('add') ?>
+									<?= jvbIcon('plus-square') ?>
 									Group
 								</button>
 								<button type="button" data-action="delete-upload">
-									<?= jvbIcon('delete') ?>
+									<?= jvbIcon('trash') ?>
 									Delete
 								</button>
 							</div>
 						</div>
 
 						<button type="button" data-action="upload" class="submit-uploads">
-							<?= jvbIcon('upload') ?> Upload <?= esc_html($plural ?? 'Content'); ?>
+							<?= jvbIcon('cloud-arrow-up') ?> Upload <?= esc_html($plural ?? 'Content'); ?>
 						</button>
 					</div>
 					<?php endif; ?>
@@ -959,7 +959,7 @@
 					</div>
 
 					<?php if ($destination === 'post_group') : ?>
-					<p class="hint"><?= jvbIcon('elbow-left-up') ?>  These will become individual <?= $plural ?>  <?= jvbIcon('elbow-right-up')?></p>
+					<p class="hint"><?= jvbIcon('arrow-elbow-left-up') ?>  These will become individual <?= $plural ?>  <?= jvbIcon('arrow-elbow-right-up')?></p>
 				</div>
 				<div class="sidebar flex col">
 					<div class="header">
@@ -971,7 +971,7 @@
 							<p>Drag here to create a new <?= $singular ?>!</p>
 						</div>
 					</div>
-					<p class="hint"><?= jvbIcon('elbow-left-up') ?>  Each group will become its own <?= $singular ?>  <?= jvbIcon('elbow-right-up')?></p>
+					<p class="hint"><?= jvbIcon('arrow-elbow-left-up') ?>  Each group will become its own <?= $singular ?>  <?= jvbIcon('arrow-elbow-right-up')?></p>
 				</div>
 			</div>
 		<?php endif; ?>
@@ -1087,12 +1087,12 @@
 						</div>
 
 						<button type="button" data-action="delete-upload" title="Remove from Group">
-							<?=jvbIcon('delete')?>
+							<?=jvbIcon('trash')?>
 						</button>
 					</div>
 				</div>
-				<details>';
-					<summary class="row btw"><?=jvbIcon('edit')?><span>Edit Info</span></summary>
+				<details>
+					<summary class="row btw"><?=jvbIcon('pencil-simple')?><span>Edit Info</span></summary>
 
 		<?php
 
@@ -1127,7 +1127,10 @@
 			]
 		], $fields);
 
-		$this->render('upload_data', null, $fields);
+		$meta = new MetaManager($id);
+		foreach ($fields as $field => $config) {
+			$meta->render('form', $field, $config);
+		}
 		?>
 				</details>
 		</div>
@@ -1162,12 +1165,12 @@
 						</div>
 
 						<button type="button" data-action="delete-upload" title="Remove from Group">
-							<?=jvbIcon('delete')?>
+							<?=jvbIcon('trash')?>
 						</button>
 					</div>
 				</div>
 				<details>';
-					<summary class="row btw"><?=jvbIcon('edit')?><span>Edit Info</span></summary>
+					<summary class="row btw"><?=jvbIcon('pencil-simple')?><span>Edit Info</span></summary>
 
 		<?php
 		$fields = array_key_exists('fields', $config) ? $config['fields'] : [];
@@ -1235,12 +1238,12 @@
 						</div>
 
 						<button type="button" data-action="delete-upload" title="Remove from Group">
-							<?=jvbIcon('delete')?>
+							<?=jvbIcon('trash')?>
 						</button>
 					</div>
 				</div>
 				<details>';
-					<summary class="row btw"><?=jvbIcon('edit')?><span>Edit Info</span></summary>
+					<summary class="row btw"><?=jvbIcon('pencil-simple')?><span>Edit Info</span></summary>
 
 		<?php
 		$fields = array_key_exists('fields', $config) ? $config['fields'] : [];
diff --git a/inc/meta/MetaFormOld.php b/inc/meta/MetaFormOld.php
index 1c29732..7682a19 100644
--- a/inc/meta/MetaFormOld.php
+++ b/inc/meta/MetaFormOld.php
@@ -267,7 +267,7 @@
 						 class="decrease"
 						 title="<?= array_key_exists('remove', $field) ? $field['remove'] : 'Decrease amount' ?>"
 						 aria-label="Decrease <?= esc_attr($field['label']); ?>">
-					 <?= jvbIcon('minus')?>
+					 <?= jvbIcon('minus-square')?>
 				 </button>
 
 				 <input type="number"
@@ -286,7 +286,7 @@
 						 class="increase"
 						 title="<?= array_key_exists('add', $field) ? $field['add'] : 'Increase amount' ?>"
 						 aria-label="Increase <?= esc_attr($field['label']); ?>">
-					 <?= jvbIcon('add')?>
+					 <?= jvbIcon('plus-square')?>
 				 </button>
 			 </div>
 			 <?php $this->renderDescription($description, $name);  ?>
@@ -469,11 +469,11 @@
         <div class="repeater-row" data-index="<?= esc_attr($index); ?>">
             <details <?= (is_string($index)) ? 'open' : ''; ?>>
                 <summary class="repeater-row-header row btw">
-                    <span class="drag-handle"><?= jvbIcon('grab'); ?></span>
+                    <span class="drag-handle"><?= jvbIcon('dots-six-vertical'); ?></span>
                     <span class="row-number">#<?= esc_html($display_number); ?></span>
                     <span class="row-title"><?= esc_html($this->getRowTitle($fields, $values, $rowTitle)); ?></span>
                     <button type="button" class="remove-row" title="Remove">
-                        <?= jvbIcon('delete', ['title'=>'Remove']); ?>
+                        <?= jvbIcon('trash', ['title'=>'Remove']); ?>
                     </button>
                 </summary>
                 <div class="repeater-row-content">
@@ -605,7 +605,7 @@
 				<button title="Add <?= esc_attr(ucfirst($field['post_type'])) ?>"
 						class="add-item-btn"
 						type="button">
-					<?= jvbIcon('add', ['title' => "Add " . ucfirst($field['post_type'])]) ?>
+					<?= jvbIcon('plus-square', ['title' => "Add " . ucfirst($field['post_type'])]) ?>
 				</button>
 			</div>
 
@@ -843,7 +843,7 @@
                         <?php endif; ?>
 
                         <button type="button" class="search-button">
-                            <?= jvbIcon('search', ['title' => 'Search']); ?>
+                            <?= jvbIcon('magnifying-glass', ['title' => 'Search']); ?>
                         </button>
                     </div>
                 </div>
@@ -925,7 +925,7 @@
                                         <span class="item-title"><?= esc_html($item_title); ?></span>
                                         <span class="item-type"><?= esc_html(ucfirst($item_object)); ?></span>
                                         <button type="button" class="remove-item row">
-                                            <?= jvbIcon('close', ['title' => 'Remove']); ?>
+                                            <?= jvbIcon('x', ['title' => 'Remove']); ?>
                                         </button>
                                     </li>
                                     <?php
@@ -1049,7 +1049,7 @@
                             const removeBtn = document.createElement('button');
                             removeBtn.type = 'button';
                             removeBtn.className = 'remove-item';
-                            removeBtn.innerHTML = '<?= jvbIcon('close', ['title' => 'Remove']); ?>';
+                            removeBtn.innerHTML = '<?= jvbIcon('x', ['title' => 'Remove']); ?>';
                             removeBtn.addEventListener('click', function() {
                                 removeItem(id, clone);
                             });
@@ -1434,18 +1434,18 @@
 
 							<div class="selection-actions row btw" hidden>
 								<button type="button" data-action="add-to-group">
-									<?= jvbIcon('add') ?>
+									<?= jvbIcon('plus-square') ?>
 									Group
 								</button>
 								<button type="button" data-action="delete-upload">
-									<?= jvbIcon('delete') ?>
+									<?= jvbIcon('trash') ?>
 									Delete
 								</button>
 							</div>
 						</div>
 
 						<button type="button" data-action="upload" class="submit-uploads">
-							<?= jvbIcon('upload') ?> Upload <?= esc_html($plural ?? 'Content'); ?>
+							<?= jvbIcon('cloud-arrow-up') ?> Upload <?= esc_html($plural ?? 'Content'); ?>
 						</button>
 					</div>
 					<?php endif; ?>
@@ -1462,7 +1462,7 @@
 					</div>
 
 					<?php if ($destination === 'post_group') : ?>
-					<p class="hint"><?= jvbIcon('elbow-left-up') ?>  These will become individual <?= $plural ?>  <?= jvbIcon('elbow-right-up')?></p>
+					<p class="hint"><?= jvbIcon('arrow-elbow-left-up') ?>  These will become individual <?= $plural ?>  <?= jvbIcon('arrow-elbow-right-up')?></p>
 				</div>
 				<div class="sidebar flex col">
 					<div class="header">
@@ -1474,7 +1474,7 @@
 							<p>Drag here to create a new <?= $singular ?>!</p>
 						</div>
 					</div>
-					<p class="hint"><?= jvbIcon('elbow-left-up') ?>  Each group will become its own <?= $singular ?>  <?= jvbIcon('elbow-right-up')?></p>
+					<p class="hint"><?= jvbIcon('arrow-elbow-left-up') ?>  Each group will become its own <?= $singular ?>  <?= jvbIcon('arrow-elbow-right-up')?></p>
 				</div>
 			</div>
 		<?php endif; ?>
@@ -1764,18 +1764,18 @@
 							<!-- Selection actions (hidden by default) -->
 							<div class="selection-actions" hidden>
 								<button type="button" class="create-from-selection">
-									<?= jvbIcon('add') ?>
+									<?= jvbIcon('plus-square') ?>
 									Create New <?= $singular ?>
 								</button>
 								<button type="button" class="remove-selection">
-									<?= jvbIcon('delete') ?>
+									<?= jvbIcon('trash') ?>
 									Remove
 								</button>
 							</div>
 						</div>
 
 						<button type="button" class="submit-uploads">
-							<?= jvbIcon('upload') ?> Upload <?= esc_html($plural ?? 'Content'); ?>
+							<?= jvbIcon('cloud-arrow-up') ?> Upload <?= esc_html($plural ?? 'Content'); ?>
 						</button>
 					</div>
 					<?php endif; ?>
@@ -1789,7 +1789,7 @@
 					</div>
 
 					<?php if ($groupable) : ?>
-					<p class="hint"><?= jvbIcon('elbow-left-up') ?>  These will become individual <?= $plural ?>  <?= jvbIcon('elbow-right-up')?></p>
+					<p class="hint"><?= jvbIcon('arrow-elbow-left-up') ?>  These will become individual <?= $plural ?>  <?= jvbIcon('arrow-elbow-right-up')?></p>
 				</div>
 				<div class="sidebar">
 					<div class="header">
@@ -1798,7 +1798,7 @@
 						<p class="hint">Select multiple images and click "Add to <?= $singular?>" or create new <?= $plural ?>.</p>
 					</div>
 					<button type="button" class="create-group-from-selection">
-						<?= jvbIcon('add') ?>
+						<?= jvbIcon('plus-square') ?>
 						Create New <?= $singular ?>
 					</button>
 					<div class="item-grid groups">
@@ -1806,7 +1806,7 @@
 							<p>Drag here to create a new <?= $singular ?>!</p>
 						</div>
 					</div>
-					<p class="hint"><?= jvbIcon('elbow-left-up') ?>  Each group will become its own <?= $singular ?>  <?= jvbIcon('elbow-right-up')?></p>
+					<p class="hint"><?= jvbIcon('arrow-elbow-left-up') ?>  Each group will become its own <?= $singular ?>  <?= jvbIcon('arrow-elbow-right-up')?></p>
 				</div>
 			</div>
 		<?php endif; ?>
@@ -1963,7 +1963,7 @@
                     <?= !empty($field['required']) ? 'required' : ''; ?>
                     data-format="<?= esc_attr($format); ?>"
                 >
-                <?= jvbIcon('event') ?>
+                <?= jvbIcon('calendar') ?>
             </div>
 			 <?php if (array_key_exists('hint', $field)) { $this->renderHint($field['hint']); }  ?>
 			 <?php if (array_key_exists('description', $field)) { $this->renderDescription($field['description'], $name); }  ?>
@@ -2061,7 +2061,7 @@
 					<?= !empty($field['max']) ? 'max="' . esc_attr($field['max']) . '"' : ''; ?>
 					<?= !empty($field['step']) ? 'step="' . esc_attr($field['step']) . '"' : ''; ?>
 				>
-				<?= jvbIcon('event') ?>
+				<?= jvbIcon('calendar') ?>
 			</div>
 			<?php if (array_key_exists('hint', $field)) { $this->renderHint($field['hint']); }  ?>
 			<?php if (array_key_exists('description', $field)) { $this->renderDescription($field['description'], $name); }  ?>
@@ -2107,7 +2107,7 @@
 	{
 		$id = $name.'-help';
 		$out = '<div class="has-tooltip">
-		<span class="tt-toggle">'.jvbIcon('help').'</span>
+		<span class="tt-toggle">'.jvbIcon('question').'</span>
 		<div role="tooltip" id="'.$id.'"><p>'.$description.'</p></div>
 		</div>';
 		echo $out;
diff --git a/inc/meta/MetaManager.php b/inc/meta/MetaManager.php
index 380ac9a..f8081aa 100644
--- a/inc/meta/MetaManager.php
+++ b/inc/meta/MetaManager.php
@@ -763,7 +763,7 @@
 		}
 
 
-		echo (jvbCheck('submit', $options)) ? '<button type="submit">'.jvbIcon('save').'Save</button>' : '';
+		echo (jvbCheck('submit', $options)) ? '<button type="submit">'.jvbIcon('floppy-disk').'Save</button>' : '';
 		echo '</form>';
 
         $out = ob_get_clean();
@@ -807,7 +807,7 @@
 				$out = '<p class="'.$name.'">'.jvbIcon('calendar').'<span></span></p>';
 				break;
 			case 'time':
-				$out = '<p class="'.$name.'">'.jvbIcon('time').'<time></time></p>';
+				$out = '<p class="'.$name.'">'.jvbIcon('clock').'<time></time></p>';
 				break;
 			case 'true_false':
 				$out = '<p class="'.$name.'"></p>';
diff --git a/inc/meta/MetaRenderer.php b/inc/meta/MetaRenderer.php
index 263c46e..fa70635 100644
--- a/inc/meta/MetaRenderer.php
+++ b/inc/meta/MetaRenderer.php
@@ -59,7 +59,7 @@
         $link = 'mailto:'.$value.'?subject='.rawurlencode('Contact from edmonton.ink').'&body='.rawurlencode('Hey,
         I found you on edmonton.ink, and I wanted to reach out!');
 
-        return '<a href="'.$link.'" title="Send an Email">'.jvbIcon('email').'<span>Email</span></a>';
+        return '<a href="'.$link.'" title="Send an Email">'.jvbIcon('envelope').'<span>Email</span></a>';
 
     }
     protected function renderUrlField(string $name, string $value, array $field):string
@@ -422,7 +422,7 @@
 
 		// Add icon if configured
 		$show_icon = $field['show_icon'] ?? true;
-		$icon_html = $show_icon ? jvbIcon('time') : '';
+		$icon_html = $show_icon ? jvbIcon('clock') : '';
 
 		return sprintf(
 			'<div class="time-field-display">%s<span class="time-value">%s</span></div>',
@@ -458,7 +458,7 @@
 
 		// Add icon if configured
 		$show_icon = $field['show_icon'] ?? true;
-		$icon_html = $show_icon ? jvbIcon('event') : '';
+		$icon_html = $show_icon ? jvbIcon('calendar') : '';
 
 		return sprintf(
 			'<div class="datetime-field-display">%s<span class="datetime-value">%s</span></div>',
@@ -484,7 +484,7 @@
 
 		// Add icon if configured
 		$show_icon = $field['show_icon'] ?? true;
-		$icon_html = $show_icon ? jvbIcon('event') : '';
+		$icon_html = $show_icon ? jvbIcon('calendar') : '';
 
 		return sprintf(
 			'<div class="date-field-display">%s<span class="date-value">%s</span></div>',
diff --git a/inc/meta/_setup.php b/inc/meta/_setup.php
index d8eab14..5af0e09 100644
--- a/inc/meta/_setup.php
+++ b/inc/meta/_setup.php
@@ -1,6 +1,4 @@
 <?php
-if (!empty(JVB_CONTENT) || !empty(JVB_TAXONOMY) || !empty(JVB_USER)) {
-
 	require(JVB_DIR . '/inc/meta/MetaTypeManager.php');
 	require(JVB_DIR . '/inc/meta/MetaManager.php');
 	require(JVB_DIR . '/inc/meta/MetaRegistry.php');
@@ -8,4 +6,3 @@
 	require(JVB_DIR . '/inc/meta/MetaValidator.php');
 	require(JVB_DIR . '/inc/meta/MetaRenderer.php');
 	require(JVB_DIR . '/inc/meta/MetaForm.php');
-}
diff --git a/inc/registry/CheckCustomTables.php b/inc/registry/CheckCustomTables.php
index ed5166a..8980dd6 100644
--- a/inc/registry/CheckCustomTables.php
+++ b/inc/registry/CheckCustomTables.php
@@ -128,9 +128,9 @@
 			}
 
 			try {
-				if (array_key_exists('dashboard', $this->JVB_SITE) && $this->JVB_SITE['dashboard']) {
+//				if (array_key_exists('dashboard', $this->JVB_SITE) && $this->JVB_SITE['dashboard']) {
 					$tables = array_merge($tables, $this->queueTables(), $this->errorLogTables());
-				}
+//				}
 			} catch (Exception $e) {
 				error_log("JVB: Error in dashboard section: " . $e->getMessage());
 			}
@@ -144,7 +144,7 @@
 			}
 			// RE-ENABLE favourites tables
 			try {
-				if ($this->JVB_SITE['favourites']) {
+				if (array_key_exists('favourites', $this->JVB_SITE)) {
 					error_log('JVB: Creating favourites tables...');
 					$favouriteTables = $this->favouriteTables();
 					error_log('JVB: Favourites tables created: ' . count($favouriteTables));
diff --git a/inc/rest/RestRouteManager.php b/inc/rest/RestRouteManager.php
index 8f1b1ba..96ccfb7 100644
--- a/inc/rest/RestRouteManager.php
+++ b/inc/rest/RestRouteManager.php
@@ -114,6 +114,19 @@
         return true;
     }
 
+	public function checkRateLimit(WP_REST_Request $request):bool|WP_Error
+	{
+		if (!$this->rate_limiter->checkLimit($request)) {
+			error_log('Rate Limit Reached');
+			return new WP_Error(
+				'rate_limit',
+				'Too many attempts. Please wait a moment before trying again.',
+				['status' => 429]
+			);
+		}
+		return true;
+	}
+
 	protected function checkContent(string $content, bool $bool = false):string|bool
 	{
 		$result = JVB_CONTENT[$content]??JVB_TAXONOMY[$content]??JVB_USER[$content]??'';
@@ -520,6 +533,379 @@
 
 		return $this->checkHeaders($request, $types, ['user_id' => $user_id]);
 	}
+
+	/**
+	 * Helper to return error response
+	 */
+	protected function error(string $message, string $code, int $status = 400, ?string $field = null): WP_REST_Response
+	{
+		$data = [
+			'message' => $message,
+			'code' => $code
+		];
+
+		if ($field) {
+			$data['field'] = $field;
+		}
+
+		return new WP_REST_Response($data, $status);
+	}
+	/**
+	 * Helper to return success response
+	 */
+	protected function success(array $data, int $status = 200): WP_REST_Response
+	{
+		return new WP_REST_Response($data, $status);
+	}
+
+	/************************************************************
+		SESSION FINGERPRINT
+	 ************************************************************/
+	/**
+	 * Store session fingerprint for hijacking detection
+	 */
+	protected function storeSessionFingerprint(int $user_id, WP_REST_Request $request): void
+	{
+		if (!defined('JVB_SESSION_FINGERPRINT') || !JVB_SESSION_FINGERPRINT) {
+			return;
+		}
+
+		$fingerprint = $this->generateSessionFingerprint($request);
+		update_user_meta($user_id, BASE . 'session_fingerprint', $fingerprint);
+		update_user_meta($user_id, BASE . 'session_timestamp', time());
+	}
+	/**
+	 * Generate session fingerprint for hijacking detection
+	 *
+	 * @param WP_REST_Request $request The REST request
+	 * @return string Hashed fingerprint
+	 */
+	protected function generateSessionFingerprint(WP_REST_Request $request): string
+	{
+		return hash('sha256', implode('|', [
+			$request->get_header('User-Agent') ?? '',
+			// Use IP class instead of full IP to allow for mobile network changes
+			$this->getIPClass(
+				$request->get_header('X-Forwarded-For')
+					?: $request->get_header('X-Real-IP')
+					?: $_SERVER['REMOTE_ADDR'] ?? ''
+			)
+		]));
+	}
+
+	/**
+	 * Get IP class (first 3 octets) for session validation
+	 * This allows for minor IP changes (common with mobile networks)
+	 *
+	 * @param string $ip IP address
+	 * @return string First 3 octets
+	 */
+	protected function getIPClass(string $ip): string
+	{
+		$parts = explode('.', $ip);
+		return implode('.', array_slice($parts, 0, 3));
+	}
+
+	/**
+	 * Validate session fingerprint against stored value
+	 *
+	 * @param int $user_id User ID to validate
+	 * @param WP_REST_Request $request Current request
+	 * @return bool True if valid, false if potential hijacking
+	 */
+	protected function validateSessionFingerprint(int $user_id, WP_REST_Request $request): bool
+	{
+		// Only enforce if enabled in config
+		if (!defined('JVB_SESSION_FINGERPRINT') || !JVB_SESSION_FINGERPRINT) {
+			return true;
+		}
+
+		$stored = get_user_meta($user_id, BASE . 'session_fingerprint', true);
+		$current = $this->generateSessionFingerprint($request);
+
+		if (empty($stored)) {
+			// First request - store fingerprint
+			update_user_meta($user_id, BASE . 'session_fingerprint', $current);
+			update_user_meta($user_id, BASE . 'session_timestamp', time());
+			return true;
+		}
+
+		// Compare using timing-safe comparison
+		return hash_equals($stored, $current);
+	}
+
+	/**
+	 * Clear session fingerprint (call on logout)
+	 *
+	 * @param int $user_id User ID
+	 * @return void
+	 */
+	protected function clearSessionFingerprint(int $user_id): void
+	{
+		delete_user_meta($user_id, BASE . 'session_fingerprint');
+		delete_user_meta($user_id, BASE . 'session_timestamp');
+	}
+
+	/******************************************************************
+	 *  CSRF PROTECTION
+	******************************************************************/
+	/**
+	 * Generate CSRF token for a user
+	 *
+	 * @param int $user_id User ID
+	 * @return string CSRF token
+	 */
+	protected function generateCSRFToken(int $user_id): string
+	{
+		$token = wp_generate_password(32, false);
+		set_transient(BASE . 'csrf_' . $user_id, $token, HOUR_IN_SECONDS);
+		return $token;
+	}
+
+	/**
+	 * Validate CSRF token from request header
+	 *
+	 * @param WP_REST_Request $request The REST request
+	 * @return bool True if valid or not required
+	 */
+	protected function validateCSRFToken(WP_REST_Request $request): bool
+	{
+		// Only for authenticated requests
+		if (!is_user_logged_in()) {
+			return true;
+		}
+
+		// Only for state-changing operations
+		if (in_array($request->get_method(), ['GET', 'HEAD', 'OPTIONS'])) {
+			return true;
+		}
+
+		$user_id = get_current_user_id();
+		$token = $request->get_header('X-CSRF-Token');
+		$stored = get_transient(BASE . 'csrf_' . $user_id);
+
+		if (empty($stored) || empty($token)) {
+			return false;
+		}
+
+		return hash_equals($stored, $token);
+	}
+
+	/**
+	 * Get current CSRF token for user (for frontend to use)
+	 *
+	 * @param int $user_id User ID
+	 * @return string|null CSRF token or null
+	 */
+	protected function getCSRFToken(int $user_id): ?string
+	{
+		$token = get_transient(BASE . 'csrf_' . $user_id);
+
+		if (!$token) {
+			$token = $this->generateCSRFToken($user_id);
+		}
+
+		return $token;
+	}
+
+	/***********************************************************
+	 * REQUEST SIGNATURE VERIFICATION
+	***********************************************************/
+	/**
+	 * Verify request signature for sensitive operations
+	 * Adds an additional layer of security for critical endpoints
+	 *
+	 * @param WP_REST_Request $request The REST request
+	 * @param array $sensitive_params Params to include in signature
+	 * @return bool True if valid signature
+	 */
+	protected function verifyRequestSignature(WP_REST_Request $request, array $sensitive_params): bool
+	{
+		$signature = $request->get_header('X-Request-Signature');
+		if (empty($signature)) {
+			return false;
+		}
+
+		// Get user's signing key (rotated on each login)
+		$user_id = get_current_user_id();
+		if (!$user_id) {
+			return false;
+		}
+
+		$signing_key = get_user_meta($user_id, BASE . 'signing_key', true);
+		if (empty($signing_key)) {
+			// Generate new key if missing
+			$signing_key = wp_generate_password(64, false);
+			update_user_meta($user_id, BASE . 'signing_key', $signing_key);
+		}
+
+		// Build signature from sensitive params
+		ksort($sensitive_params);
+		$message = json_encode($sensitive_params);
+		$expected = hash_hmac('sha256', $message, $signing_key);
+
+		return hash_equals($expected, $signature);
+	}
+
+	/**
+	 * Rotate signing key (call on login)
+	 *
+	 * @param int $user_id User ID
+	 * @return string New signing key
+	 */
+	protected function rotateSigningKey(int $user_id): string
+	{
+		$new_key = wp_generate_password(64, false);
+		update_user_meta($user_id, BASE . 'signing_key', $new_key);
+		return $new_key;
+	}
+	/**********************************************************
+	 * AUDIT LOGGING
+	**********************************************************/
+	/**
+	 * Log security-relevant events
+	 *
+	 * @param string $event Event name
+	 * @param array $data Additional event data
+	 * @return void
+	 */
+	protected function auditLog(string $event, array $data = []): void
+	{
+		// Add standard context
+		$context = array_merge($data, [
+			'timestamp' => current_time('mysql'),
+			'user_id' => get_current_user_id() ?: 0,
+			'ip' => $_SERVER['REMOTE_ADDR'] ?? '',
+			'user_agent' => $_SERVER['HTTP_USER_AGENT'] ?? '',
+		]);
+
+		// Use existing error handler
+		try {
+			JVB()->error()->log(
+				'security_audit',
+				$event,
+				$context,
+				'info'
+			);
+		} catch (\Exception $e) {
+			// Fallback to error_log if handler fails
+			error_log("Security Audit: {$event} - " . json_encode($context));
+		}
+	}
+
+	/***************************************************************
+	 * SANITIZATION HELPERS
+	***************************************************************/
+	/**
+	 * Sanitize array of IDs
+	 *
+	 * @param array $ids Array of IDs
+	 * @return array Sanitized array of integers
+	 */
+	protected function sanitizeIDs(array $ids): array
+	{
+		return array_map('absint', array_filter($ids, function($id) {
+			return is_numeric($id) && $id > 0;
+		}));
+	}
+
+	/***************************************************************
+	 * RESPONSE HELPERS
+	***************************************************************/
+	/**
+	 * Return validation error response
+	 *
+	 * @param array $errors Validation errors (field => message)
+	 * @return WP_REST_Response 422 response with errors
+	 */
+	protected function validationError(array $errors): WP_REST_Response
+	{
+		return new WP_REST_Response([
+			'success' => false,
+			'message' => 'Validation failed',
+			'errors' => $errors
+		], 422);
+	}
+
+	/**
+	 * Return unauthorized error response
+	 *
+	 * @param string $message Error message
+	 * @return WP_REST_Response 401 response
+	 */
+	protected function unauthorized(string $message = 'Unauthorized'): WP_REST_Response
+	{
+		return new WP_REST_Response([
+			'success' => false,
+			'message' => $message
+		], 401);
+	}
+
+	/**
+	 * Return forbidden error response
+	 *
+	 * @param string $message Error message
+	 * @return WP_REST_Response 403 response
+	 */
+	protected function forbidden(string $message = 'Forbidden'): WP_REST_Response
+	{
+		return new WP_REST_Response([
+			'success' => false,
+			'message' => $message
+		], 403);
+	}
+
+	/**
+	 * Return not found error response
+	 *
+	 * @param string $message Error message
+	 * @return WP_REST_Response 404 response
+	 */
+	protected function notFound(string $message = 'Not found'): WP_REST_Response
+	{
+		return new WP_REST_Response([
+			'success' => false,
+			'message' => $message
+		], 404);
+	}
+
+	/*****************************************************************
+	 * CONCURRENT CHECKS
+	*****************************************************************/
+	/**
+	 * Prevent concurrent requests for the same operation
+	 * Useful for preventing double-submissions
+	 *
+	 * @param string $operation_key Unique key for this operation
+	 * @param int $lock_duration Lock duration in seconds
+	 * @return bool True if lock acquired, false if already locked
+	 */
+	protected function acquireOperationLock(string $operation_key, int $lock_duration = 5): bool
+	{
+		$lock_key = 'op_lock_' . md5($operation_key);
+
+		// Try to acquire lock
+		$locked = get_transient($lock_key);
+		if ($locked) {
+			return false; // Already locked
+		}
+
+		// Set lock
+		set_transient($lock_key, true, $lock_duration);
+		return true;
+	}
+
+	/**
+	 * Release operation lock
+	 *
+	 * @param string $operation_key Unique key for this operation
+	 * @return void
+	 */
+	protected function releaseOperationLock(string $operation_key): void
+	{
+		$lock_key = 'op_lock_' . md5($operation_key);
+		delete_transient($lock_key);
+	}
 }
 //
 //Simple example:
@@ -605,3 +991,37 @@
 //	$response = new WP_REST_Response($data);
 //	return $this->addCacheHeaders($response);
 //}
+
+
+
+/**
+ * Use operation lock in your methods like this:
+ *
+ * public function updateContent(WP_REST_Request $request): WP_REST_Response
+ * {
+ *     $user_id = get_current_user_id();
+ *     $content_id = $request->get_param('content_id');
+ *
+ *     // Prevent concurrent updates
+ *     $lock_key = "update_{$user_id}_{$content_id}";
+ *     if (!$this->acquireOperationLock($lock_key)) {
+ *         return $this->error(
+ *             'An update is already in progress. Please wait.',
+ *             'concurrent_operation',
+ *             409
+ *         );
+ *     }
+ *
+ *     try {
+ *         // Do your operation
+ *         $result = $this->doUpdate($content_id);
+ *
+ *         $this->releaseOperationLock($lock_key);
+ *         return $this->success($result);
+ *
+ *     } catch (\Exception $e) {
+ *         $this->releaseOperationLock($lock_key);
+ *         return $this->error($e->getMessage(), 'operation_failed');
+ *     }
+ * }
+ */
diff --git a/inc/rest/_setup.php b/inc/rest/_setup.php
index a10f740..14e88b5 100644
--- a/inc/rest/_setup.php
+++ b/inc/rest/_setup.php
@@ -23,14 +23,14 @@
 	require(JVB_DIR . '/inc/rest/routes/TermRoutes.php');
 }
 
+require(JVB_DIR . '/inc/rest/routes/QueueRoutes.php');
+require(JVB_DIR . '/inc/rest/routes/ErrorRoutes.php');
+require(JVB_DIR . '/inc/rest/routes/UploadRoutes.php');
+require(JVB_DIR . '/inc/rest/routes/SettingsRoutes.php');
 if (Features::forSite()->has('dashboard')) {
-	require(JVB_DIR . '/inc/rest/routes/QueueRoutes.php');
-	require(JVB_DIR . '/inc/rest/routes/ErrorRoutes.php');
 	require(JVB_DIR . '/inc/rest/routes/AdminRoutes.php');
 	require(JVB_DIR . '/inc/rest/routes/ContentRoutes.php');
-	require(JVB_DIR . '/inc/rest/routes/UploadRoutes.php');
 	require(JVB_DIR . '/inc/rest/routes/BioRoutes.php');
-	require(JVB_DIR . '/inc/rest/routes/SettingsRoutes.php');
 	require(JVB_DIR . '/inc/rest/routes/ShopRoutes.php');
 }
 if (Features::forMembership()->has('forum')) {
@@ -61,3 +61,4 @@
 require(JVB_DIR .'/inc/rest/routes/OptionsRoutes.php');
 require(JVB_DIR .'/inc/rest/routes/FormRoutes.php');
 require(JVB_DIR .'/inc/rest/routes/IntegrationsRoutes.php');
+require(JVB_DIR .'/inc/rest/routes/LoginRoutes.php');
diff --git a/inc/rest/routes/ApprovalRoutes.php b/inc/rest/routes/ApprovalRoutes.php
index 3052999..f795dac 100644
--- a/inc/rest/routes/ApprovalRoutes.php
+++ b/inc/rest/routes/ApprovalRoutes.php
@@ -5,6 +5,7 @@
 use JVBase\JVB;
 use JVBase\rest\RestRouteManager;
 use JVBase\managers\CacheManager;
+use JVBase\utility\Features;
 use WP_User;
 use WP_REST_Request;
 use WP_REST_Response;
@@ -23,15 +24,17 @@
 	protected array $voteTables;
 
     protected int $expiryDays = 7;
+	protected bool $hasMemberApproval = false;
 
     public function __construct()
     {
         $this->cache_name = 'approvals';
+		$this->hasMemberApproval = Features::forMembership()->has('member_verified');
         parent::__construct();
 
         $this->initTypes();
 
-        if (jvbSiteHasMemberApproval()) {
+        if ($this->hasMemberApproval) {
             add_action('user_register', [$this, 'handleNewUserRegistration'], 10, 2);
         }
 
@@ -43,7 +46,7 @@
         $approvals = jvbApprovalTypes();
         $this->userTypes = [];
         $this->termTypes = [];
-        if (jvbSiteHasMemberApproval()) {
+        if ($this->hasMemberApproval) {
             $this->userTypes = array_filter(
                 array_keys($approvals),
                 function ($item) {
diff --git a/inc/rest/routes/ContentRoutes.php b/inc/rest/routes/ContentRoutes.php
index ff056a9..dfa9cb6 100644
--- a/inc/rest/routes/ContentRoutes.php
+++ b/inc/rest/routes/ContentRoutes.php
@@ -25,14 +25,18 @@
     protected string $post_type = '';
     protected string $user_id = '';
 
+	//For Timeline-specific posts
+	protected array $timelineSharedFields = [];
+	protected array $timelineUniqueFields = [];
+
     //TODO: Ensure we are handling the bulk operations for all processes
-    //TODO: be sure to clear cache ($this->>cache->invalidateGroup($this->>cache_name)) on content update/create
     //TODO: Also invalidate feed caches on updates!!
 
     public function __construct()
     {
         $this->cache_name = 'user_content_'.get_current_user_id();
         parent::__construct();
+
         $this->action = 'dash-';
         $this->operation_type = 'content_update';
         add_filter(BASE.'handle_bulk_operation', [$this, 'processOperation'], 10, 3);
@@ -75,6 +79,32 @@
         ]);
     }
 
+	protected function initTimelineFields(string $content):void
+	{
+		$content = jvbNoBase($content);
+		if (!Features::forContent($content)->has('is_timeline')){
+			return;
+		}
+		$config = Features::getConfig($content);
+		$this->fields = $config['fields'];
+
+		$this->timelineSharedFields = array_keys(array_filter($this->fields, function ($field) {
+			if (!array_key_exists('for_all', $field) || $field['for_all'] === false){
+				return true;
+			}
+			return false;
+		}));
+		array_unshift($this->timelineSharedFields, 'post_thumbnail');
+		array_unshift($this->timelineSharedFields, 'post_title');
+
+		$this->timelineUniqueFields = array_keys(array_filter($this->fields, function ($field) {
+			if (array_key_exists('for_all', $field) && $field['for_all'] === true) {
+				return true;
+			}
+			return false;
+		}));
+	}
+
     /**
      * Handle content update/creation
      * @param WP_REST_Request $request
@@ -187,9 +217,6 @@
         }
         $post_type = str_replace('-', '_',jvbCheckBase($params['content']));
 
-		$config = Features::getConfig($params['content']);
-
-
 
         // Build query args
         $args = [
@@ -396,14 +423,12 @@
 
 			}
 
-            CacheManager::invalidateGroup($post_data['content']);
+            CacheManager::for($post_data['content'])->clear();
 			if (jvbSiteUsesFeedBlock()) {
-				CacheManager::invalidateGroup($post_data['feed']);
+				CacheManager::for('feed')->clear();
 			}
         }
 
-
-        CacheManager::invalidateGroup('user_content');
 		if (jvbSiteHasNotifications()) {
 			$this->notifications = JVB()->notification();
 			$this->notifications->addNotification(
@@ -433,83 +458,109 @@
 			return ['success' => false, 'message' => 'No permission'];
 		}
 
-		$rows = $post_data['fields'] ?? [];
-		if (empty($rows)) {
-			return ['success' => false, 'message' => 'No data'];
-		}
+		$this->fields = jvbGetFields($post_data['content']);
+		$this->initTimelineFields($post_data['content']);
+		error_log('Received Data: '.print_r($post_data, true));
 
-		$fields = jvbGetFields($post_data['content']);
-
-		// First row = parent post
-		$parent_row = array_shift($rows);
-		if (($parent_row['id'] ?? null) != $parent_id) {
-			return ['success' => false, 'message' => 'Parent ID mismatch'];
-		}
-
-		$allowedFields = array_filter($parent_row, function($key) use ($fields) {
-			return array_key_exists($key, $fields);
+		// First, process the main fields that will apply to all posts
+		$sharedData = array_filter($post_data, function ($key) {
+			return in_array($key, $this->timelineSharedFields);
 		}, ARRAY_FILTER_USE_KEY);
 
-		$parentMeta = new MetaManager($parent_id, 'post');
-		$parentMeta->setAll($allowedFields);
+		//Next, process any individual posts, including any menu order changes
+		if (array_key_exists('timeline', $post_data) && is_array($post_data['timeline'])) {
+			$sharedTaxonomies = $sharedData;
+			unset($sharedTaxonomies['post_title']);
 
-		// Get existing children to track deletions
-		$existing_children = get_children([
-			'post_parent' => $parent_id,
-			'post_type' => jvbCheckBase($post_data['content']),
-			'fields' => 'ids'
-		]);
-
-		$processed_ids = [];
-
-		// Process remaining rows as children
-		foreach ($rows as $index => $row_data) {
-			$row_id = $row_data['id'] ?? null;
-
-			// New child post
-			if (!$row_id || str_starts_with($row_id, 'new')) {
-				$child_id = wp_insert_post([
-					'post_type' => jvbCheckBase($post_data['content']),
-					'post_parent' => $parent_id,
-					'post_author' => $this->user_id,
-					'post_status' => $post_data['status'] ?? 'draft',
-					'menu_order' => $index
-				]);
+			//Ensure the parent post exists and is still first in the array
+			$index = array_search((string)$parent_id, array_column($post_data['timeline'], 'id'));
+			error_log('Found index: '.print_r($index, true));
+			if ($index === false) {
+				return [
+					'success' => false,
+					'message'	=> 'Missing parent id. This should not have happened'
+				];
+			} elseif ($index !== 0) {
+				// Move that element to the start of the array
+				$item = $post_data['timeline'][$index];
+				unset($post_data['timeline'][$index]);
+				array_unshift($post_data['timeline'], $item);
 			}
-			// Existing child post
-			else {
-				$child_id = (int) $row_id;
+			$errors = [];
+			$success = [];
+			// Get existing children to track deletions
+			$existing_children = get_children([
+				'post_parent' => $parent_id,
+				'post_type' => jvbCheckBase($post_data['content']),
+				'fields' => 'ids'
+			]);
+			//Iterate through the timeline posts
+			foreach($post_data['timeline'] as $order => $timeline) {
+				$allowedFields = array_filter($timeline, function($key) {
+					return in_array($key, $this->timelineUniqueFields);
+				}, ARRAY_FILTER_USE_KEY);
 
-				// Verify ownership via parent
-				if (!in_array($child_id, $existing_children)) {
-					continue; // Skip if not actually a child of this parent
+				$allowedFields['post_title'] = $allowedFields['post_title'] ?? $sharedData['post_title'].' - Treatment #'.$order;
+				$allowedFields = array_merge($allowedFields, $sharedTaxonomies);
+				if (!array_key_exists('id', $timeline) || !is_numeric($timeline['id'])) {
+					$newChild = wp_insert_post([
+						'post_author'	=> $this->user_id,
+						'post_type'		=> jvbCheckBase($post_data['content']),
+						'post_title'	=> $allowedFields['post_title'],
+						'post_parent'	=> $parent_id,
+						'menu_order'	=> $order
+					]);
+					if (!$newChild || is_wp_error($newChild)) {
+						$errors[] = [
+							'message'	=> 'Could not create child post',
+							'data'		=> $timeline
+						];
+						continue;
+					}
+					$timeline['id'] = $newChild;
 				}
 
-				// Update menu_order (position may have changed)
-				wp_update_post([
-					'ID' => $child_id,
-					'menu_order' => $index
-				]);
+				if (in_array((int)$timeline['id'], $existing_children)) {
+					unset($existing_children[array_search((int)$timeline['id'], $existing_children)]);
+				}
+
+				//Determine which fields to update
+				$meta = new MetaManager($timeline['id'], 'post');
+				$oldValues = $meta->getAll(array_keys($allowedFields));
+				$updateValues = array_filter($allowedFields, function($value, $key) use ($oldValues) {
+					return (!array_key_exists($key, $oldValues) || $value !== $oldValues[$key]);
+				}, ARRAY_FILTER_USE_BOTH);
+				$meta->setAll($updateValues);
+				//Update Menu Order, if applicable
+				if ((int) $timeline['id'] !== $parent_id) {
+					$post = get_post((int) $timeline['id']);
+					if ($post && $post->menu_order !== $order) {
+						$updated = wp_update_post([
+							'ID'			=> $post->ID,
+							'menu_order'	=> $order,
+						]);
+						if (!$updated || is_wp_error($updated)) {
+							$errors[] = [
+								'message'	=> 'Could not update timeline order for post',
+								'data'		=> $timeline
+							];
+						}
+					}
+				}
+				$success[] = $timeline['id'];
 			}
-
-			// Update child meta
-			$allowedChildFields = array_filter($row_data, function($key) use ($fields) {
-				return array_key_exists($key, $fields) && $key !== 'id' && $key !== 'draggable';
-			}, ARRAY_FILTER_USE_KEY);
-
-			$childMeta = new MetaManager($child_id, 'post');
-			$childMeta->setAll($allowedChildFields);
-
-			$processed_ids[] = $child_id;
+		}
+		//Delete any remaining children that no longer exist
+		if (!empty($existing_children)) {
+			foreach ($existing_children as $ID) {
+				wp_delete_post($ID);
+			}
 		}
 
-		// Delete removed children
-		$deleted_ids = array_diff($existing_children, $processed_ids);
-		foreach ($deleted_ids as $delete_id) {
-			wp_delete_post($delete_id, true);
-		}
-
-		return ['success' => true, 'processed' => $processed_ids];
+		return ['success' => true, 'data' => [
+			'success'	=> $success,
+			'errors'	=> $errors
+		]];
 	}
 
     /**
@@ -590,9 +641,10 @@
      *
      * @return array
      */
-    protected function prepareItem(WP_Post $post, $skip = false):array
+    protected function prepareItem(WP_Post $post, bool $skip = false, bool $fields = true):array
     {
 		if (!$skip && Features::forContent($post->post_type)->has('is_timeline')) {
+			$this->initTimelineFields($post->post_type);
 			return $this->formatTimeline($post);
 		}
         $this->meta = new MetaManager($post->ID, 'post');
@@ -605,7 +657,7 @@
             'alt'       => get_post_meta(get_post_thumbnail_id(), '_wp_attachment_image_alt', true),
             'icon'      => $this->post_type,
             'taxonomies'=> [],
-            'fields'    => $this->meta->getAll(),
+            'fields'    => ($fields) ? $this->meta->getAll() : [],
 			'images'	=> [],
         ];
 
@@ -633,12 +685,13 @@
 
         return $data;
     }
-	protected function extractImages():array
+	protected function extractImages(array $fields = []):array
 	{
 		//Extract images
 		$images = [];
 		$get = [];
-		foreach ($this->fields as $field => $config) {
+		$fields = (empty($fields)) ? $this->fields : $fields;
+		foreach ($fields as $field => $config) {
 			if ($config['type'] === 'gallery' || $config['type'] === 'image' || $field === 'post_thumbnail') {
 				$get[] = $field;
 			}
@@ -660,36 +713,29 @@
 
 	protected function formatTimeline(WP_Post $post):array
 	{
-		$data = $this->prepareItem($post, true);
-		$firstRow = $data['fields'];
-		$firstRow['id'] = $post->ID;
-		$firstRow['draggable'] = false;
-		$fields = [$firstRow];
+		$item = $this->prepareItem($post, true, false);
+		//Step 1: Get the fields that apply to all posts
+		$mainMeta = new MetaManager($post->ID, 'post');
+		$item['fields'] = $mainMeta->getAll($this->timelineSharedFields);
 
-		$children = get_children(['post_parent' => $post->ID, 'orderby' => 'menu_order']);
-		$allImages = [];
+		//Step 2: Get the fields for each individual posts
+		$children = get_children(['post_parent' => $post->ID, 'orderby' => 'menu_order', 'post_status' => ['publish', 'draft'], 'fields'=> 'ids']);
+		array_unshift($children, $post->ID);
 
+		$subFields = [];
+		$images = [];
 		foreach ($children as $child) {
-			$this->meta = new MetaManager($child->ID, 'post');
-			$row = $this->meta->getAll();  // Store in variable first
-			$row['id'] = $child->ID;       // Add ID to the row
-			$row['draggable'] = true;      // Mark as draggable
-			$fields[] = $row;              // Then append to fields
+			$meta = new MetaManager($child, 'post');
+			$f = $meta->getAll($this->timelineUniqueFields);
+			$f =  ['id' => $child] + $f;
+			$subFields[$f['post_thumbnail']] = $f;
 
-			$images = $this->extractImages();
-			if (!empty($images)) {
-				$allImages = $allImages + $images;
-			}
+			$images[$f['post_thumbnail']] = jvbImageData((int) $f['post_thumbnail']);
 		}
+		$item['fields']['timeline'] = $subFields;
+		$item['images'] = $item['images'] + $images;
 
-		if (!empty($allImages)) {
-			if (!array_key_exists('images', $data)) {
-				$data['images'] = [];
-			}
-			$data['images'] = $data['images'] + $allImages;
-		}
-		$data['fields']['timeline'] = $fields;
-		return $data;
+		return $item;
 	}
 
     /**
@@ -839,9 +885,8 @@
                     }
 
                     //Clear cache
-                    CacheManager::invalidateGroup($data['content']);
-                    CacheManager::invalidateGroup('feed');
-                    CacheManager::invalidateGroup('user_content');
+					CacheManager::for($data['content'])->clear();
+                    CacheManager::for('feed')->clear();
                 }
 
 				return [
diff --git a/inc/rest/routes/FavouritesRoutes.php b/inc/rest/routes/FavouritesRoutes.php
index b1483b3..37bcfbc 100644
--- a/inc/rest/routes/FavouritesRoutes.php
+++ b/inc/rest/routes/FavouritesRoutes.php
@@ -33,6 +33,7 @@
         add_action('before_delete_post', [$this, 'cleanupPostFavourites']);
         add_action('delete_term', [$this, 'cleanupTermFavourites'], 10, 3);
 
+		add_action('jvbUserRegistered', [$this, 'maybeAcceptListInvite'], 10, 3);
 
         // Register cleanup scheduler
         add_action('jvb_cleanupOrphanedFavourites', [$this, 'cleanupOrphanedFavourites']);
@@ -3110,6 +3111,13 @@
         }
     }
 
+	public function maybeAcceptListInvite(int $user_id, string $email, array $data):void
+	{
+		if (array_key_exists('list_token', $data) && !empty($data['list_token'])) {
+			$this->acceptListInvitation($data['list_token'], $email);
+		}
+	}
+
     /**
      * Get the owner ID for a content item
      *
diff --git a/inc/rest/routes/Invitations.php b/inc/rest/routes/Invitations.php
index 1372cb4..8758609 100644
--- a/inc/rest/routes/Invitations.php
+++ b/inc/rest/routes/Invitations.php
@@ -48,20 +48,8 @@
         // Add hooks for processing accepted invitations
         add_action('user_register', [$this, 'checkInvitation'], 10, 1);
 
-		add_action('jvbLoginManagerInit', function($loginManager) {
-			$loginManager->registerTokenHandler('invite', function($token, $email, $user_id) {
-				JVB()->routes('invites')->acceptInvitation($token, $email, $user_id);
-			});
 
-			$loginManager->registerMessageHandler('invitation',
-				function() {
-					return '<h2>You\'ve been invited!</h2><p>Create your account to accept.</p>';
-				},
-				function() {
-					return isset($_GET['invite']);
-				}
-			);
-		});
+		add_filter('jvbLoginLabels', [$this, 'modifyLoginLabels'], 10, 2);
 
 
 
@@ -1361,4 +1349,47 @@
             ];
         }
     }
+
+	public function modifyLoginLabels(array $labels, array $get_params): array
+	{
+		// Only modify if invitation params present
+		if (!array_key_exists('invite', $get_params) || !array_key_exists('email', $get_params)) {
+			return $labels;
+		}
+		$email = sanitize_email($get_params['email']);
+		$token = sanitize_text_field($get_params['invite']);
+		$user = email_exists($email);
+		if (!$user) {
+			return $labels;
+		}
+		$role = jvbUserRole($user);
+		// Get invitation data
+		$data = $this->verifyInvitation(
+			$token,
+			$email,
+			$role,
+		);
+
+		if (!$data) {
+			return $labels;
+		}
+
+		// Build custom message
+		$inviters = json_decode($data->inviters, true);
+		$name = $data->name;
+		$names = array_map(function($inviter) {
+			$artist = jvbContentFromUser((int)$inviter['user_id']);
+			return $artist['name'] ?: $artist['display_name'];
+		}, $inviters);
+
+		$message = count($names) > 1
+			? 'are already here, and have invited you to join in!'
+			: ' is already here, and invited you to join in!';
+
+		// Modify labels
+		$labels['title'] = 'Join the Scene, ' . $data->name;
+		$labels['description'] = [jvbCommaList($names) . ' ' . $message];
+
+		return $labels;
+	}
 }
diff --git a/inc/rest/routes/LoginRoutes.php b/inc/rest/routes/LoginRoutes.php
new file mode 100644
index 0000000..ec82f0d
--- /dev/null
+++ b/inc/rest/routes/LoginRoutes.php
@@ -0,0 +1,667 @@
+<?php
+namespace JVBase\rest\routes;
+
+use JVBase\managers\EmailManager;
+use JVBase\managers\LoginManager;
+use JVBase\managers\MagicLinkManager;
+use JVBase\rest\RestRouteManager;
+use JVBase\utility\Features;
+use WP_REST_Request;
+use WP_REST_Response;
+use WP_Error;
+use WP_User;
+
+if (!defined('ABSPATH')) {
+	exit;
+}
+
+class LoginRoutes extends RestRouteManager
+{
+	protected EmailManager $emailManager;
+	protected MagicLinkManager $magic_link;
+	protected LoginManager $loginManager;
+	protected ?string $requestId = null;
+
+	public function __construct()
+	{
+		$this->cache_name = 'auth';
+		$this->cache_ttl = WEEK_IN_SECONDS;
+		$this->emailManager = new EmailManager();
+		if (Features::forSite()->has('magicLink')) {
+			$this->magic_link = new MagicLinkManager();
+		}
+
+		parent::__construct();
+	}
+
+	public function registerRoutes(): void
+	{
+		// Login endpoint
+		register_rest_route($this->namespace, '/auth/login', [
+			'methods' => 'POST',
+			'callback' => [$this, 'handleLogin'],
+			'permission_callback' => [$this, 'checkRateLimit'],
+			'args' => [
+				'user_email' => [
+					'required' => true,
+					'type' => 'string',
+					'sanitize_callback' => 'sanitize_email'
+				],
+				'user_password' => [
+					'required' => true,
+					'type' => 'string'
+				],
+				'remember_me' => [
+					'required' => false,
+					'type' => 'boolean',
+					'default' => false
+				]
+			]
+		]);
+
+		// Logout endpoint
+		register_rest_route($this->namespace, '/auth/logout', [
+			'methods' => 'POST',
+			'callback' => [$this, 'handleLogout'],
+			'permission_callback' => 'is_user_logged_in'
+		]);
+
+		// Check auth status
+		register_rest_route($this->namespace, '/auth/status', [
+			'methods' => 'GET',
+			'callback' => [$this, 'getAuthStatus'],
+			'permission_callback' => '__return_true'
+		]);
+
+		// Request password reset
+		register_rest_route($this->namespace, '/auth/lostpassword', [
+			'methods' => 'POST',
+			'callback' => [$this, 'requestPasswordReset'],
+			'permission_callback' => [$this, 'checkRateLimit'],
+			'args' => [
+				'user_email' => [
+					'required' => true,
+					'type' => 'string',
+					'format' => 'email',
+					'sanitize_callback' => 'sanitize_email'
+				]
+			]
+		]);
+
+		// Complete password reset (with token)
+		register_rest_route($this->namespace, '/auth/reset-password', [
+			'methods' => 'POST',
+			'callback' => [$this, 'resetPassword'],
+			'permission_callback' => [$this, 'checkRateLimit'],
+			'args' => [
+				'token' => [
+					'required' => true,
+					'type' => 'string',
+					'sanitize_callback' => 'sanitize_text_field'
+				],
+				'user_email' => [
+					'required' => true,
+					'type' => 'string',
+					'format' => 'email',
+					'sanitize_callback' => 'sanitize_email'
+				],
+				'password' => [
+					'required' => true,
+					'type' => 'string'
+				]
+			]
+		]);
+
+		register_rest_route($this->namespace, '/auth/register', [
+			'method'	=> 'POST',
+			'callback'	=> [$this, 'handleRegister'],
+			'permission_callback' => [$this, 'checkRateLimit'],
+		]);
+
+		// Refresh session
+		register_rest_route($this->namespace, '/auth/refresh', [
+			'methods' => 'POST',
+			'callback' => [$this, 'refreshSession'],
+			'permission_callback' => 'is_user_logged_in'
+		]);
+
+		register_rest_route($this->namespace, '/auth/email', [
+			'methods'	=> 'POST',
+			'callback'	=> [$this, 'checkEmailExists'],
+			'permission_callback' => [$this, 'checkRateLimit'],
+		]);
+	}
+
+	public function handleLogin(WP_REST_Request $request): WP_REST_Response
+	{
+		// Verify Turnstile
+		if (!$this->verifyTurnstile($request->get_param('cf-turnstile-response') ?? '')) {
+			return $this->error('Security verification failed', 'turnstile_failed', 403);
+		}
+
+		$username = $request->get_param('user_email');
+		$password = $request->get_param('user_password');
+		$remember = (bool)$request->get_param('remember_me');
+
+		// Check for account lockout
+		$lockout = $this->checkAccountLockout($username);
+		if (is_wp_error($lockout)) {
+			return $this->error(
+				$lockout->get_error_message(),
+				'account_locked',
+				429
+			);
+		}
+
+		// Attempt login
+		$user = wp_signon([
+			'user_login'	=> $username,
+			'user_email' 	=> $username,
+			'user_password' => $password,
+			'remember' => $remember
+		], false);
+
+		if (is_wp_error($user)) {
+			// Track failed attempt
+			$this->trackFailedLogin($username);
+
+			return $this->error(
+				'Invalid username or password',
+				'login_failed',
+				401
+			);
+		}
+
+		// Clear failed attempts on success
+		$this->clearFailedAttempts($username);
+
+		// Set auth cookie with remember me flag
+		wp_set_current_user($user->ID);
+		wp_set_auth_cookie($user->ID, $remember);
+
+		// Store session fingerprint for hijacking protection
+		$this->storeSessionFingerprint($user->ID, $request);
+
+		// Trigger WordPress login action
+		do_action('wp_login', $user->user_login, $user);
+
+		// Audit log
+		$this->auditLog('user_login', [
+			'user_id' => $user->ID,
+			'remember' => $remember
+		]);
+
+		return $this->success([
+			'message' => 'Login successful',
+			'user' => $this->formatUserData($user),
+			'redirect' => $this->getLoginRedirect($user)
+		]);
+	}
+
+	/**
+	 * Handle logout request
+	 */
+	public function handleLogout(WP_REST_Request $request): WP_REST_Response
+	{
+		$user_id = get_current_user_id();
+
+		// Clear session fingerprint
+		$this->clearSessionFingerprint($user_id);
+
+		// Audit log
+		$this->auditLog('user_logout', ['user_id' => $user_id]);
+
+		// WordPress logout
+		wp_logout();
+
+		return $this->success([
+			'message' => 'Logged out successfully'
+		]);
+	}
+
+	/**
+	 * Get current authentication status
+	 */
+	public function getAuthStatus(WP_REST_Request $request): WP_REST_Response
+	{
+		if (!is_user_logged_in()) {
+			return $this->success([
+				'authenticated' => false
+			]);
+		}
+
+		$user = wp_get_current_user();
+
+		return $this->success([
+			'authenticated' => true,
+			'user' => $this->formatUserData($user)
+		]);
+	}
+
+	/**
+	 * Request password reset
+	 */
+	public function requestPasswordReset(WP_REST_Request $request): WP_REST_Response
+	{
+		$email = $request->get_param('user_email')??'';
+		$email = sanitize_email($email);
+
+		$token = $request->get_param('cf-turnstile-response')??'';
+		if (!$this->verifyTurnstile($token)){
+			return $this->error('Security verification failed', 'turnstile_failed', 403);
+		}
+
+		// Use WordPress's built-in function
+		$result = retrieve_password($email);
+
+		// Log but don't expose
+		if (is_wp_error($result)) {
+			$this->auditLog('password_reset_failed', [
+				'email' => $email,
+				'reason' => $result->get_error_code()
+			]);
+		} else {
+			$this->auditLog('password_reset_sent', [
+				'email' => $email
+			]);
+		}
+
+		return $this->success([
+			'message' => 'If an account exists with this email, you will receive a password reset link.'
+		]);
+	}
+
+	/**
+	 * Complete password reset with token
+	 */
+	public function resetPassword(WP_REST_Request $request): WP_REST_Response
+	{
+		$key = sanitize_text_field($request->get_param('key'));
+		$login = sanitize_text_field($request->get_param('login'));
+		$password = $request->get_param('password');
+
+		if (empty($key) || empty($login)) {
+			return $this->error('Invalid reset link', 'invalid_key', 400);
+		}
+
+		// Use WordPress's native key verification
+		$user = check_password_reset_key($key, $login);
+
+		if (is_wp_error($user)) {
+			return $this->error(
+				'Invalid or expired reset link',
+				'invalid_token',
+				400
+			);
+		}
+
+		// Validate password strength
+		$validation = $this->validatePassword($password);
+		if (is_wp_error($validation)) {
+			return $this->error(
+				$validation->get_error_message(),
+				'weak_password',
+				400
+			);
+		}
+
+		// Reset the password
+		reset_password($user, $password);
+
+		// Log them in
+		wp_set_current_user($user->ID);
+		wp_set_auth_cookie($user->ID, true);
+
+		// Store session fingerprint
+		$this->storeSessionFingerprint($user->ID, $request);
+
+		// Audit log
+		$this->auditLog('password_reset_complete', [
+			'user_id' => $user->ID
+		]);
+
+		return $this->success([
+			'message' => 'Password reset successfully',
+			'redirect' => home_url('/dash')
+		]);
+	}
+
+	/**
+	 * Refresh session (extends session duration)
+	 */
+	public function refreshSession(WP_REST_Request $request): WP_REST_Response
+	{
+		$user_id = get_current_user_id();
+
+		// Validate session fingerprint
+		if (!$this->validateSessionFingerprint($user_id, $request)) {
+			wp_logout();
+			return $this->unauthorized('Session validation failed');
+		}
+
+		// Refresh auth cookie
+		wp_set_auth_cookie($user_id, true);
+
+		return $this->success([
+			'message' => 'Session refreshed'
+		]);
+	}
+
+
+	public function handleRegister(WP_REST_Request $request): WP_REST_Response
+	{
+		$data = $request->get_json_params();
+
+		// Duplicate submission check
+		if (!$this->checkRequestId($data['request_id'] ?? '')) {
+			return $this->error('Duplicate request detected', 'duplicate_request', 409);
+		}
+
+		// Verify Turnstile
+		if (!$this->verifyTurnstile($data['cf-turnstile-response'] ?? '')) {
+			return $this->error('Security verification failed', 'turnstile_failed', 403);
+		}
+
+		$name = sanitize_text_field($data['name'] ?? '');
+		$email = sanitize_email($data['email'] ?? '');
+		$user_type = sanitize_text_field($data['user_select'] ?? 'subscriber');
+
+		// Validate fields
+		if (empty($name)) {
+			return $this->error('Name is required', 'missing_name', 400, 'name');
+		}
+
+		if (empty($email)) {
+			return $this->error('Email is required', 'missing_email', 400, 'email');
+		}
+
+		// Spam prevention
+		if ($user_type === 'subscriber' && count(JVB_USER) > 0) {
+			$registerable = array_filter(JVB_USER, fn($config) => $config['can_register'] ?? false);
+			if (!empty($registerable)) {
+				return $this->error('Please select a valid account type', 'invalid_user_type', 400, 'user_select');
+			}
+		}
+
+		// Check if role can register
+		if ($user_type !== 'subscriber') {
+			if (!isset(JVB_USER[$user_type]) || empty(JVB_USER[$user_type]['can_register'])) {
+				return $this->error('Invalid account type', 'invalid_user_type', 400, 'user_select');
+			}
+		}
+
+		// Check if email exists
+		if (email_exists($email)) {
+			return $this->error('Email already registered', 'duplicate_email', 400, 'email');
+		}
+
+		// Allow WP plugins to add registration errors
+		$errors = new WP_Error();
+		$errors = apply_filters('registration_errors', $errors, $email, $email);
+
+		if ($errors->has_errors()) {
+			return $this->error(
+				$errors->get_error_message(),
+				$errors->get_error_code(),
+				400
+			);
+		}
+
+		// Create user
+		$user_id = wp_create_user($email, wp_generate_password(), $email);
+
+		if (is_wp_error($user_id)) {
+			return $this->error($user_id->get_error_message(), 'user_creation_failed', 500);
+		}
+
+		// Update user data
+		wp_update_user([
+			'ID' => $user_id,
+			'display_name' => $name,
+			'first_name' => strtok($name,' ')
+		]);
+
+		// Set role
+		$user = new WP_User($user_id);
+		if ($user_type === 'subscriber') {
+			$user->set_role('subscriber');
+		} else {
+			$role = JVB_USER[$user_type]['role'] ?? 'subscriber';
+			$user->set_role($role);
+
+			// Check if needs approval
+			if (Features::forMembership()->has('memberVerified') &&
+				in_array($role, JVB_MEMBERSHIP['memberVerified'] ?? [])) {
+				$user->add_cap('skip_moderation', false);
+				update_user_meta($user_id, BASE . 'pending_approval', true);
+			}
+		}
+
+		if (Features::forUser($user_type)->has('namedDirectory')) {
+			$upload_dir = wp_upload_dir();
+			$user_directory = $user_type.'/'.$user_id;
+			$target_dir = $upload_dir['basedir'].'/'.$user_directory;
+
+			wp_mkdir_p($target_dir);
+		}
+
+		// Save additional fields
+		update_user_meta($user_id, BASE . 'user_type', $user_type);
+
+		// Process additional fields from form
+		foreach ($data as $key => $value) {
+			if (in_array($key, ['name', 'email', 'action', 'request_id', 'user_select', 'cf-turnstile-response'])) {
+				continue;
+			}
+			update_user_meta($user_id, BASE . $key, sanitize_text_field($value));
+		}
+
+		// Handle token handlers
+		do_action('jvbUserRegistered', $user_id, $email, $data);
+
+
+		return $this->success([
+			'message' => 'Registration successful! Check your email.',
+			'user_id' => $user_id
+		]);
+	}
+	/**************************************************************
+		HELPERS
+	 **************************************************************/
+	/**
+	 * Format user data for response
+	 */
+	protected function formatUserData(WP_User $user): array
+	{
+		return [
+			'id' => $user->ID,
+			'username' => $user->user_login,
+			'email' => $user->user_email,
+			'display_name' => $user->display_name,
+			'roles' => $user->roles,
+			'capabilities' => [
+				'manage_options' => user_can($user, 'manage_options'),
+				'skip_moderation' => user_can($user, 'skip_moderation'),
+				// Add other relevant capabilities
+			]
+		];
+	}
+
+	/**
+	 * Get login redirect URL based on user role
+	 */
+	protected function getLoginRedirect(WP_User $user): string
+	{
+		if (user_can($user, 'manage_options')) {
+			return admin_url();
+		}
+
+		// Redirect to dashboard for members
+		if (function_exists('isOurPeople') && isOurPeople()) {
+			return home_url('/dash');
+		}
+
+		return home_url();
+	}
+
+	/**
+	 * Validate password strength
+	 */
+	protected function validatePassword(string $password): bool|WP_Error
+	{
+		if (strlen($password) < 8) {
+			return new WP_Error(
+				'weak_password',
+				'Password must be at least 8 characters long'
+			);
+		}
+
+		// Add additional strength requirements as needed
+		// - Must contain uppercase
+		// - Must contain number
+		// - Must contain special character
+
+		return true;
+	}
+
+	/**
+	 * Check if account is locked out due to failed attempts
+	 */
+	protected function checkAccountLockout(string $username): bool|WP_Error
+	{
+		if (!defined('JVB_MAX_LOGIN_ATTEMPTS')) {
+			return true;
+		}
+
+		$cache_key = 'login_attempts_' . md5($username);
+		$attempts = $this->cache->get($cache_key);
+
+		if (!$attempts || !isset($attempts['count'])) {
+			return true;
+		}
+
+		if ($attempts['count'] >= JVB_MAX_LOGIN_ATTEMPTS) {
+			$lockout_duration = defined('JVB_LOCKOUT_DURATION')
+				? JVB_LOCKOUT_DURATION
+				: 15 * MINUTE_IN_SECONDS;
+
+			$time_remaining = $lockout_duration - (time() - $attempts['timestamp']);
+
+			if ($time_remaining > 0) {
+				return new WP_Error(
+					'account_locked',
+					sprintf(
+						'Too many failed login attempts. Please try again in %d minutes.',
+						ceil($time_remaining / 60)
+					)
+				);
+			}
+
+			// Lockout expired - clear attempts
+			$this->cache->delete($cache_key);
+			return true;
+		}
+
+		return true;
+	}
+
+	/**
+	 * Track failed login attempt
+	 */
+	protected function trackFailedLogin(string $username): void
+	{
+		$cache_key = 'login_attempts_' . md5($username);
+		$attempts = $this->cache->get($cache_key) ?: [
+			'count' => 0,
+			'timestamp' => time()
+		];
+
+		$attempts['count']++;
+		$attempts['timestamp'] = time();
+
+		$lockout_duration = defined('JVB_LOCKOUT_DURATION')
+			? JVB_LOCKOUT_DURATION
+			: 15 * MINUTE_IN_SECONDS;
+
+		$this->cache->set($cache_key, $attempts, $lockout_duration);
+
+		// Audit log
+		$this->auditLog('failed_login', [
+			'username' => $username,
+			'attempts' => $attempts['count']
+		]);
+	}
+
+	/**
+	 * Clear failed login attempts on successful login
+	 */
+	protected function clearFailedAttempts(string $username): void
+	{
+		$cache_key = 'login_attempts_' . md5($username);
+		$this->cache->delete($cache_key);
+	}
+
+
+
+
+
+
+
+
+
+
+	public function checkEmailExists(WP_REST_Request $request): WP_REST_Response
+	{
+		$data = $request->get_json_params();
+		$email = sanitize_email($data['email'] ?? '');
+
+		return $this->success([
+			'exists' => is_email($email) && email_exists($email)
+		]);
+	}
+	/***********************************************************************
+	 * HELPER METHODS
+	 ***********************************************************************/
+
+	protected function checkRequestId(string $request_id): bool
+	{
+		if (empty($request_id)) {
+			return true;
+		}
+
+		$cache_key = 'request_' . $request_id;
+		if (get_transient($cache_key)) {
+			return false;
+		}
+
+		set_transient($cache_key, true, 60);
+		return true;
+	}
+
+	protected function verifyTurnstile(string $token): bool
+	{
+		if (!Features::hasIntegration('cloudflare') || !JVB()->connect('cloudflare')->isSetUp()) {
+			return true;
+		}
+
+		if (empty($token)) {
+			return false;
+		}
+
+		return JVB()->connect('cloudflare')->verifyTurnstile($token);
+	}
+
+	/**
+	 * Helper to return error response
+	 */
+	protected function error(string $message, string $code, int $status = 400, ?string $field = null): WP_REST_Response
+	{
+		if ($this->requestId) {
+			delete_transient('request_'.$this->requestId);
+			$this->requestId = null;
+		}
+		return parent::error($message, $code, $status, $field);
+	}
+
+
+}
diff --git a/inc/rest/routes/MagicLinkRoutes.php b/inc/rest/routes/MagicLinkRoutes.php
index 3535d6d..fc12edc 100644
--- a/inc/rest/routes/MagicLinkRoutes.php
+++ b/inc/rest/routes/MagicLinkRoutes.php
@@ -32,38 +32,38 @@
 	public function registerRoutes(): void
 	{
 		// Send magic link
-		register_rest_route($this->namespace, '/magic-link', [
+		register_rest_route($this->namespace, '/magic', [
 			'methods' => 'POST',
 			'callback' => [$this, 'sendMagicLink'],
-			'permission_callback' => '__return_true', // Public endpoint
-			'args' => [
-				'email' => [
-					'required' => true,
-					'type' => 'string',
-					'format' => 'email',
-					'validate_callback' => function($param) {
-						return is_email($param);
-					}
-				],
-				'type' => [
-					'required' => false,
-					'type' => 'string',
-					'default' => 'login',
-					'enum' => ['login', 'signup', 'referral', 'reset']
-				],
-				'context' => [
-					'required' => false,
-					'type' => 'object',
-					'default' => []
-				]
-			]
+			'permission_callback' => [$this, 'checkRateLimit'],
+//			'args' => [
+//				'email' => [
+//					'required' => true,
+//					'type' => 'string',
+//					'format' => 'email',
+//					'validate_callback' => function($param) {
+//						return is_email($param);
+//					}
+//				],
+//				'type' => [
+//					'required' => false,
+//					'type' => 'string',
+//					'default' => 'login',
+//					'enum' => ['login', 'signup', 'referral', 'reset']
+//				],
+//				'context' => [
+//					'required' => false,
+//					'type' => 'object',
+//					'default' => []
+//				]
+//			]
 		]);
 
 		// Resend magic link
-		register_rest_route($this->namespace, '/magic-link/resend', [
+		register_rest_route($this->namespace, '/magic/resend', [
 			'methods' => 'POST',
 			'callback' => [$this, 'resendMagicLink'],
-			'permission_callback' => '__return_true',
+			'permission_callback' => [$this, 'checkRateLimit'],
 			'args' => [
 				'email' => [
 					'required' => true,
@@ -78,7 +78,7 @@
 		]);
 
 		// Check token validity (useful for frontend)
-		register_rest_route($this->namespace, '/magic-link/verify', [
+		register_rest_route($this->namespace, '/magic/verify', [
 			'methods' => 'POST',
 			'callback' => [$this, 'verifyToken'],
 			'permission_callback' => '__return_true',
@@ -104,7 +104,7 @@
 	 */
 	public function sendMagicLink(WP_REST_Request $request): WP_REST_Response
 	{
-		$email = sanitize_email($request->get_param('email'));
+		$email = sanitize_email($request->get_param('email')??$request->get_param('user_email')??'');
 		$type = sanitize_text_field($request->get_param('type'));
 		$context = $request->get_param('context') ?? [];
 
@@ -120,6 +120,16 @@
 			], 400);
 		}
 
+		// Check if email exists
+		$exists = email_exists($email);
+		if (!$exists) {
+
+			return new WP_REST_Response([
+				'success' => false,
+				'message' => 'User account not found'
+			], 400);
+		}
+
 		// Send the magic link
 		$result = $this->magic_link->sendMagicLink($email, $type, $context);
 		error_log('Result: '.print_r($result, true));
@@ -163,34 +173,30 @@
 		$token = sanitize_text_field($request->get_param('token'));
 		$email = sanitize_email($request->get_param('email'));
 
-		$cache_key = 'magic_token_' . $token;
-		$token_data = get_transient($cache_key);
+		// This returns array|WP_Error - check for error first
+		$token_data = $this->magic_link->verifyToken($token, $email);
 
-		if (!$token_data) {
+		if (is_wp_error($token_data)) {
 			return new WP_REST_Response([
 				'valid' => false,
-				'message' => 'Token expired or invalid'
+				'message' => $token_data->get_error_message()
 			], 400);
 		}
 
-		if ($token_data['email'] !== $email) {
+		// Now check the data
+		if (!isset($token_data['email']) || $token_data['email'] !== $email) {
 			return new WP_REST_Response([
 				'valid' => false,
 				'message' => 'Invalid token'
 			], 400);
 		}
 
-		if (time() > $token_data['expires_at']) {
-			return new WP_REST_Response([
-				'valid' => false,
-				'message' => 'Token expired'
-			], 400);
-		}
-
+		// Check expiration - but your cache-based system doesn't store expires_at
+		// If token wasn't expired, it wouldn't have been returned from cache
+		// So just return valid:
 		return new WP_REST_Response([
 			'valid' => true,
-			'type' => $token_data['type'],
-			'expires_in' => $token_data['expires_at'] - time()
+			'type' => $token_data['type'] ?? 'unknown'
 		], 200);
 	}
 
diff --git a/inc/rest/routes/ReferralRoutes.php b/inc/rest/routes/ReferralRoutes.php
index 1589135..15f293e 100644
--- a/inc/rest/routes/ReferralRoutes.php
+++ b/inc/rest/routes/ReferralRoutes.php
@@ -1,6 +1,7 @@
 <?php
 namespace JVBase\rest\routes;
 
+use JVBase\managers\ReferralManager;
 use JVBase\rest\RestRouteManager;
 use WP_REST_Request;
 use WP_REST_Response;
@@ -15,7 +16,7 @@
  */
 class ReferralRoutes extends RestRouteManager
 {
-
+	protected ReferralManager $manager;
 	public function __construct()
 	{
 		$this->route = 'referrals';
@@ -31,10 +32,10 @@
 			'permission_callback' => [$this, 'checkPermission']
 		]);
 
-		register_rest_route($this->namespace, '/referrals/register', [
+		register_rest_route($this->namespace, "/{$this->route}/register", [
 			'methods' => 'POST',
 			'callback' => [$this, 'registerWithReferral'],
-			'permission_callback' => '__return_true',
+			'permission_callback' => [$this, 'checkRateLimit'],
 			'args' => [
 				'name' => [
 					'required' => true,
@@ -62,7 +63,7 @@
 		register_rest_route($this->namespace, '/referrals/check-code', [
 			'methods' => 'POST',
 			'callback' => [$this, 'checkReferralCode'],
-			'permission_callback' => '__return_true',
+			'permission_callback' => [$this, 'checkRateLimit'],
 			'args' => [
 				'code' => [
 					'required' => true,
@@ -85,7 +86,7 @@
 		register_rest_route($this->namespace, "/{$this->route}/track", [
 			'methods' => 'POST',
 			'callback' => [$this, 'trackReferralClick'],
-			'permission_callback' => '__return_true',
+			'permission_callback' => [$this, 'checkRateLimit'],
 			'args' => [
 				'code' => [
 					'required' => true,
@@ -181,6 +182,8 @@
 			]
 		]);
 
+
+
 		// Get top referrers (admin only)
 		register_rest_route($this->namespace, "/{$this->route}/leaderboard", [
 			'methods' => 'GET',
@@ -217,6 +220,21 @@
 				}
 			]
 		]);
+
+		register_rest_route($this->namespace, "/{$this->route}/add-code", [
+			'methods' 	=> 'POST',
+			'callback'	=> [$this, 'addReferralCodeAfterRegistration'],
+			'permission_callback'	=> [$this, 'checkRateLimit'],
+			'args'	=> [
+				'code'	=> [
+					'required'	=> true,
+					'type'		=> 'string',
+					'sanitize_callback'	=> function ($code) {
+						return strtoupper(sanitize_text_field($code));
+					}
+				]
+			]
+		]);
 	}
 
 	public function checkPermission(WP_REST_Request $request): bool
@@ -588,6 +606,9 @@
 				'message' => 'Invalid referral code'
 			], 404);
 		}
+		if (is_user_logged_in() && get_current_user_id() === $referrer->ID) {
+			return $this->error('You cannot use your own referral code', 'self_referral', 400);
+		}
 
 		// Return basic referrer info (no sensitive data)
 		return new WP_REST_Response([
@@ -596,4 +617,38 @@
 			'referrer_name' => $referrer->display_name,
 		], 200);
 	}
+
+	public function addReferralCodePostRegistration(WP_REST_Request $request): WP_REST_Response
+	{
+		if (!$this->manager) {
+			$this->manager = JVB()->referrals();
+		}
+		$user_id = get_current_user_id();
+		$code = $request->get_param('code');
+
+		// Check if user already has a referral (can't change)
+		$existing = $this->manager->getReferralByReferee($user_id);
+		if ($existing) {
+			return $this->error('You already have a referral code applied', 'already_referred', 400);
+		}
+
+		// Validate the code exists
+		$referrer = $this->manager->getUserByReferralCode($code);
+		if (!$referrer) {
+			return $this->error('Invalid referral code', 'invalid_code', 400);
+		}
+
+		// Create the referral
+		$user = wp_get_current_user();
+		$result = $this->manager->createReferral($referrer->ID, $user_id, $code);
+
+		if ($result) {
+			return $this->success([
+				'message' => 'Referral code applied successfully!',
+				'referrer_name' => $referrer->display_name
+			]);
+		}
+
+		return $this->error('Failed to apply referral code', 'creation_failed', 500);
+	}
 }
diff --git a/inc/templates.php b/inc/templates.php
index 7e85162..dd24b96 100644
--- a/inc/templates.php
+++ b/inc/templates.php
@@ -44,7 +44,7 @@
                     '.$textarea.'
                 </div>
                 <div class="actions row">
-                    <button type="button" class="cancel">'.jvbIcon('close', ['title'=>'Cancel']).'</button>
+                    <button type="button" class="cancel">'.jvbIcon('x', ['title'=>'Cancel']).'</button>
                     <button type="submit" class="create">Reply</button>
                 </div>
             </div>
@@ -86,8 +86,8 @@
 			<div class="upload-status">
 				<div class="upload-progress"></div>
 			</div>
-			<button type="button" class="remove-preview" title="Remove Image">'.jvbIcon('delete').'</button>
-			<button type="button" class="move-image" title="Reorder Image">'.jvbIcon('grab').'</button>
+			<button type="button" class="remove-preview" title="Remove Image">'.jvbIcon('trash').'</button>
+			<button type="button" class="move-image" title="Reorder Image">'.jvbIcon('dots-six-vertical').'</button>
 		</div>
 	</template>';
 }
diff --git a/inc/users/UserSettings.php b/inc/users/UserSettings.php
index 2fa3097..1a12bac 100644
--- a/inc/users/UserSettings.php
+++ b/inc/users/UserSettings.php
@@ -535,7 +535,7 @@
 
             <section class="invite-artists">
                 <div class="invited-artist">
-                    <button type="button" onclick="removeArtist(this)" title="Remove Invite"><?=jvbIcon('delete')?></button>
+                    <button type="button" onclick="removeArtist(this)" title="Remove Invite"><?=jvbIcon('trash')?></button>
                     <div>
                         <label for="invited-artist-name-1">First Name</label>
                         <input type="text" id="invited-artist-name-1" name="invited-artist-name-1">
@@ -551,13 +551,13 @@
                 </div>
             </section>
             <div class="actions">
-                <button type="button" onclick="addArtist(this)" class="add-artist"><?= jvbIcon('add')?>Add Artist</button>
-                <button type="button" onclick="sendInvites(this)" class="send-invites"><?=jvbIcon('email')?>Send Invites</button>
+                <button type="button" onclick="addArtist(this)" class="add-artist"><?= jvbIcon('plus-square')?>Add Artist</button>
+                <button type="button" onclick="sendInvites(this)" class="send-invites"><?=jvbIcon('envelope')?>Send Invites</button>
             </div>
 
             <template class="artistInvite">
                 <div class="invited-artist">
-                    <button type="button" onclick="removeArtist(this)" title="Remove Invite"><?=jvbIcon('delete')?></button>
+                    <button type="button" onclick="removeArtist(this)" title="Remove Invite"><?=jvbIcon('trash')?></button>
                     <div>
                         <label for="invited-artist-name">First Name</label>
                         <input type="text" id="invited-artist-name" name="invited-artist-name">
@@ -608,11 +608,11 @@
                         </td>
                         <td class="actions">
                             <button type="button" data-action="revoke" title="Revoke Invitation" onclick="revokeInvite(this)">
-                                <?=jvbIcon('delete')?>
+                                <?=jvbIcon('trash')?>
                                 <span class="screen-reader-text">Revoke invite</span>
                             </button>
                             <button type="button" data-action="resend"<?=($invite['status'] === 'exipired') ? '' : ' disabled'?> title="Resend Invitation" onclick="resendInvite(this)">
-                                <?=jvbIcon('refresh')?>
+                                <?=jvbIcon('arrows-clockwise')?>
                                 <span class="screen-reader-text">Try Again</span>
                             </button>
                         </td>
diff --git a/jvb.php b/jvb.php
index 9fd28df..816a013 100644
--- a/jvb.php
+++ b/jvb.php
@@ -10,6 +10,7 @@
 */
 
 use JVBase\JVB;
+use JVBase\managers\IconsManager;
 use JVBase\utility\Checker;
 use JVBase\utility\Features;
 
@@ -18,14 +19,82 @@
     exit;
 }
 
+/**
+ * Track REST API errors by wrapping request execution
+ */
+add_filter('rest_pre_dispatch', function($result, $server, $request) {
+	// Store request details globally for error logging
+	$GLOBALS['jvb_rest_request'] = [
+		'route' => $request->get_route(),
+		'method' => $request->get_method(),
+		'params' => $request->get_params(),
+		'time' => microtime(true),
+	];
+
+	// Set up error handler for this request
+	set_error_handler(function($errno, $errstr, $errfile, $errline) {
+		if (!(error_reporting() & $errno)) {
+			return false;
+		}
+
+		$context = array_merge($GLOBALS['jvb_rest_request'] ?? [], [
+			'error_type' => $errno,
+			'error_message' => $errstr,
+			'file' => $errfile,
+			'line' => $errline,
+			'user_id' => get_current_user_id(),
+		]);
+
+		error_log('JVB REST Error: ' . json_encode($context));
+
+		return false; // Let PHP handle it normally too
+	});
+
+	return $result;
+}, 10, 3);
+
+add_filter('rest_post_dispatch', function($result, $server, $request) {
+	// Restore default error handler
+	restore_error_handler();
+
+	// Check for fatal errors that occurred during request
+	$error = error_get_last();
+	if ($error && in_array($error['type'], [E_ERROR, E_USER_ERROR, E_PARSE, E_COMPILE_ERROR])) {
+		$context = array_merge($GLOBALS['jvb_rest_request'] ?? [], [
+			'fatal_error' => $error,
+			'user_id' => get_current_user_id(),
+		]);
+
+		error_log('JVB REST Fatal: ' . json_encode($context));
+	}
+
+	// Clean up
+	unset($GLOBALS['jvb_rest_request']);
+
+	return $result;
+}, 10, 3);
+
+
 const JVB_LOCAL = 'northeh.test';
 
+function jvbIgnoredPostTypes():array
+{
+	return [BASE.'directory', BASE.'dash', 'attachment', 'revision', 'nav_menu_item'];
+}
 add_filter('show_admin_bar', '__return_false');
 const JVB_TESTING = true;
 
 const JVB_DIR = WP_PLUGIN_DIR . '/jvb';
 define('JVB_URL', plugin_dir_url(__FILE__));
 
+// Session Security
+define('JVB_SESSION_FINGERPRINT', true);
+
+// Login Security
+define('JVB_MAX_LOGIN_ATTEMPTS', 5);
+define('JVB_LOCKOUT_DURATION', 15 * MINUTE_IN_SECONDS);
+
+
 require(JVB_DIR.'/base/_setup.php');
 //error_log('###############################################');
 //error_log('Registered Base');
@@ -54,6 +123,7 @@
 global $jvb_everything;
 $jvb_everything = array_merge(JVB_CONTENT, JVB_TAXONOMY);
 
+
 require(JVB_DIR . '/inc/registry/_setup.php');
 
 require(JVB_DIR . '/activate.php');
@@ -61,6 +131,15 @@
 require(JVB_DIR . '/inc/helpers/all.php');
 require(JVB_DIR . '/inc/meta/_setup.php');
 require(JVB_DIR . '/inc/managers/_setup.php');
+
+function jvbIcon($name, $options = []) {
+	return IconsManager::getInstance()->getIcon($name, $options);
+}
+
+function jvbCSSIcon($name, $options = []) {
+	$style = array_key_exists('style', $options) ? $options['style'] : null;
+	return IconsManager::getInstance()->getCSSIcon($name, $style);
+}
 require(JVB_DIR . '/inc/integrations/_setup.php');
 require(JVB_DIR . '/inc/rest/_setup.php');
 
@@ -804,12 +883,12 @@
     wp_enqueue_script('jvb-navigation');
 //    wp_enqueue_script('jvb-ui');
     wp_enqueue_script('jvb-media');
+	wp_enqueue_script('jvb-gallery');
     wp_enqueue_script('jvb-cache');
 
 
 
     $userID = get_current_user_id();
-	$icons = new \JVBase\JVBIcons();
     $queue = (is_user_logged_in()) ?
         [
             'api'             => rest_url('jvb/v1/'),
@@ -818,7 +897,6 @@
             'dash'            => wp_create_nonce('dash-'.$userID),
             'favourites'    => wp_create_nonce('favourites-'.$userID),
             'notifications'    => wp_create_nonce('notifications-'.$userID),
-            'icons'            => $icons->localizeIcons(),
 			'labels'			=> jvbGetLabels(),
         ] :
         [
@@ -826,7 +904,6 @@
             'nonce'       => wp_create_nonce('wp_rest'),
             'currentUser' => false,
             'redirect'    => wp_login_url(home_url(add_query_arg(null, null))), // Current URL as redirect
-            'icons'       => $icons->localizeIcons(),
 			'labels'			=> jvbGetLabels(),
         ];
 
@@ -1012,7 +1089,27 @@
 //add_action('wp_head', 'jvbDumpIt');
 function jvbDumpIt()
 {
-	jvbDump(get_post_type_archive_link(BASE.'faq'));
+}
+
+add_action('after_setup_theme', 'jvbImageSize');
+function jvbImageSize():void
+{
+	add_theme_support('post-thumbnails');
+	add_image_size( 'tiny', 50, 50, false );
 }
 
 
+//
+//add_action('rest_api_init', function() {
+//	error_log('User ' . get_current_user_id() . ' can edit pages: ' . (current_user_can('edit_pages') ? 'yes' : 'no'));
+//	error_log('User roles: ' . print_r(wp_get_current_user()->roles, true));
+//});
+//
+//add_filter('rest_pre_dispatch', function($result, $server, $request) {
+//	if (strpos($request->get_route(), '/wp/v2/pages') !== false) {
+//		error_log('Pages request - User: ' . get_current_user_id());
+//		error_log('Can edit pages: ' . (current_user_can('edit_pages') ? 'yes' : 'no'));
+//		error_log('Nonce: ' . $request->get_header('X-WP-Nonce'));
+//	}
+//	return $result;
+//}, 10, 3);
diff --git a/src/feed/render.php b/src/feed/render.php
index 2284965..7af8d29 100644
--- a/src/feed/render.php
+++ b/src/feed/render.php
@@ -195,7 +195,7 @@
                         <div class="filter-actions">
                             <?= jvbRenderToggleTextField('match', 'Match', 'Filters', 'ALL', 'ANY', true) ?>
                             <button type="button" class="clear-filters row" hidden>
-                                <?= jvbIcon('close', ['title'    => 'Clear']) ?>
+                                <?= jvbIcon('x', ['title'    => 'Clear']) ?>
                                 Clear All Filters
                             </button>
                         </div>
@@ -220,7 +220,7 @@
 
                             <input type="radio" id="order-random" class="btn" name="orderby" value="random">
                             <label for="order-random" title="Random Order">
-                                <?= jvbIcon('random') ?>
+                                <?= jvbIcon('shuffle') ?>
                                 <span class="label">Random</span>
                             </label>
                         </div>
@@ -229,12 +229,12 @@
                     <div class="order-direction radio-group-label" data-for-order="date,title">
                         <input type="radio" id="order-desc" class="btn" name="order" value="desc" checked>
                         <label for="order-desc" title="Newest First">
-                            <?= jvbIcon('desc') ?>
+                            <?= jvbIcon('sort-descending') ?>
                         </label>
 
                         <input type="radio" id="order-asc" class="btn" name="order" value="asc">
                         <label for="order-asc" title="Oldest First">
-                            <?= jvbIcon('asc') ?>
+                            <?= jvbIcon('sort-ascending') ?>
                         </label>
                     </div>
                 </div>
diff --git a/src/glossary/view.js b/src/glossary/view.js
index 0d426b1..abe67a6 100644
--- a/src/glossary/view.js
+++ b/src/glossary/view.js
@@ -14,7 +14,6 @@
 		this.navList = this.nav.querySelector('ul');
 		this.activeClass = 'active';
 		this.currentActive = null;
-		this.breakpoint = 768; // Adjust this to match your small screen breakpoint
 
 		this.init();
 		this.setupResizeHandler();
@@ -42,13 +41,6 @@
 	}
 
 	getRootMargin() {
-		if (window.innerWidth < this.breakpoint) {
-			// On small screens: 5rem from top and bottom
-			// Convert rem to pixels
-			const remInPixels = parseFloat(getComputedStyle(document.documentElement).fontSize);
-			const margin = Math.round(remInPixels * 5); // 5rem in pixels
-			return `-${margin}px 0px -${margin}px 0px`;
-		}
 		// On larger screens: centered (50% from top and bottom)
 		return '-50% 0px -50% 0px';
 	}
@@ -94,16 +86,11 @@
 		this.terms.forEach(term => {
 			const rect = term.getBoundingClientRect();
 
-			// Check if term is within the trigger zone (4rem from top or bottom on small screens)
-			const isInZone = window.innerWidth < this.breakpoint
-				? rect.top >= margin && rect.top <= window.innerHeight - margin
-				: rect.top + rect.height / 2 >= 0 && rect.top + rect.height / 2 <= window.innerHeight;
+			const isInZone = rect.top + rect.height / 2 >= 0 && rect.top + rect.height / 2 <= window.innerHeight;
 
 			if (isInZone) {
 				// Find closest to the trigger point
-				const triggerPoint = window.innerWidth < this.breakpoint
-					? margin  // Top edge of zone on small screens
-					: window.innerHeight / 2;  // Center on large screens
+				const triggerPoint = window.innerHeight / 2;
 
 				const distance = Math.abs(rect.top - triggerPoint);
 
diff --git a/src/summary/render.php b/src/summary/render.php
index 9253d73..d6a2040 100644
--- a/src/summary/render.php
+++ b/src/summary/render.php
@@ -52,11 +52,11 @@
     <nav id="artist" class="on-this-page index">
         <label>Jump to:
             <button type="button" aria-label="Show Index" title="Show Index" class="toggle" aria-expanded="false">
-                <?= jvbIcon('add')?>
+                <?= jvbIcon('plus-square')?>
             </button>
         </label>
         <ul>
-            <li><a href="#top" title="Back to Top"><?=jvbIcon('up')?></a></li>
+            <li><a href="#top" title="Back to Top"><?=jvbIcon('caret-circle-up')?></a></li>
             <li><a href="#about">About</a></li>
             <li><a href="#styles">Styles</a></li>
             <li><a href="#contact">Contact</a></li>
@@ -184,11 +184,11 @@
     <nav id="shop" class="on-this-page index">
         <label>Jump to:
             <button type="button" aria-label="Show Index" title="Show Index" class="toggle" aria-expanded="false">
-                <?= jvbIcon('add')?>
+                <?= jvbIcon('plus-square')?>
             </button>
         </label>
         <ul>
-            <li><a href="#top" title="Back to Top"><?=jvbIcon('up')?></a></li> <?php
+            <li><a href="#top" title="Back to Top"><?=jvbIcon('caret-circle-up')?></a></li> <?php
             if ($rating !== 'none') {
                 ?>
                 <li><a href="#rating">Rating</a></li>
diff --git a/templates/dashboard/sections/favourites.php b/templates/dashboard/sections/favourites.php
index 8fca5b7..78f756a 100644
--- a/templates/dashboard/sections/favourites.php
+++ b/templates/dashboard/sections/favourites.php
@@ -35,7 +35,7 @@
             <div class="controls">
                 <div class="type-filters">
                     <button type="button" class="type-filter active" data-type="all">
-                        <?= jvbIcon('all'); ?>
+                        <?= jvbIcon('infinity'); ?>
                         <span>Everything</span>
                         <span class="count">(<?= array_sum($counts); ?>)</span>
                     </button>
@@ -62,10 +62,10 @@
 
                 <div class="view-controls">
                     <button type="button" class="view-toggle" data-view="grid" aria-pressed="true">
-                        <?= jvbIcon('grid'); ?>
+                        <?= jvbIcon('squares-four'); ?>
                     </button>
                     <button type="button" class="view-toggle" data-view="list" aria-pressed="false">
-                        <?= jvbIcon('list'); ?>
+                        <?= jvbIcon('rows'); ?>
                     </button>
                 </div>
             </div>
@@ -89,7 +89,7 @@
                 </select>
                 <button type="button" class="apply-bulk">Apply</button>
                 <button type="button" class="cancel-bulk">
-                    <?= jvbIcon('close', ['title'=>'Cancel']); ?>
+                    <?= jvbIcon('x', ['title'=>'Cancel']); ?>
                 </button>
             </div>
         </div>
@@ -99,7 +99,7 @@
         <div class="tab-content" data-tab="lists" role="tabpanel" aria-hidden="true">
             <div class="lists-header">
                 <button type="button" class="create-list-btn" onclick="window.userFavouritesManager.showCreateListModal()">
-                    <?= jvbIcon('add', ['title'=>'Create List']); ?>
+                    <?= jvbIcon('plus-square', ['title'=>'Create List']); ?>
                     <span>Create List</span>
                 </button>
             </div>
@@ -122,11 +122,11 @@
                         <span>Share</span>
                     </button>
                     <button type="button" class="duplicate-list" title="Duplicate List">
-                        <?= jvbIcon('copy')?>
+                        <?= jvbIcon('copy-simple')?>
                         <span>Duplicate</span>
                     </button>
                     <button type="button" class="delete-list" title="Delete List">
-                        <?= jvbIcon('delete')?>
+                        <?= jvbIcon('trash')?>
                         <span>Delete</span>
                     </button>
                 </div>
@@ -148,7 +148,7 @@
                         </select>
                         <button type="button" class="apply-bulk">Apply</button>
                         <button type="button" class="cancel-bulk">
-                            <?= jvbIcon('close', ['title'=>'Cancel']); ?>
+                            <?= jvbIcon('x', ['title'=>'Cancel']); ?>
                         </button>
                     </div>
                 </div>
@@ -223,7 +223,7 @@
             <div class="input-with-button">
                 <input type="email" id="share-email" name="share_email" placeholder="Enter email address">
                 <button type="button" class="add-email">
-                    <?= jvbIcon('add', ['title'=>'Add share access'])?>
+                    <?= jvbIcon('plus-square', ['title'=>'Add share access'])?>
                 </button>
             </div>
         </div>
diff --git a/templates/dashboard/sections/news.php b/templates/dashboard/sections/news.php
index 299b027..ae4d46f 100644
--- a/templates/dashboard/sections/news.php
+++ b/templates/dashboard/sections/news.php
@@ -37,7 +37,7 @@
     <!-- Tab navigation -->
     <div class="tabs" role="tablist">
         <button type="button" class="tab active" data-tab="news" role="tab" aria-selected="true">
-            <h2><?= jvbIcon('news')?>News</h2>
+            <h2><?= jvbIcon('newspaper')?>News</h2>
         </button>
         <button type="button" class="tab" data-tab="mine" role="tab" aria-selected="false">
             <h2>Yours</h2>
@@ -47,7 +47,7 @@
         </button>
 
         <button type="button" class="add-item-btn">
-            <?= jvbIcon('add', ['title' =>'Add News']) ?>
+            <?= jvbIcon('plus-square', ['title' =>'Add News']) ?>
             <span>Post</span>
         </button>
     </div>
@@ -119,7 +119,7 @@
     <details class="type-filters">
         <summary class="row btw">Filters:
             <button class="clear-filters row">
-                <?= jvbIcon('close', ['title' => 'Clear Filters'])?>
+                <?= jvbIcon('x', ['title' => 'Clear Filters'])?>
                 <span>Clear Filters</span>
                 </span>
             </button>
@@ -202,13 +202,13 @@
 
                         <input type="radio" id="order-karma" class="btn" name="orderby" value="karma">
                         <label for="order-karma" title="Order by Standing" class="row">
-                            <?= jvbIcon('karma') ?>
+                            <?= jvbIcon('scales') ?>
                             <span class="label">Standing</span>
                         </label>
 
                         <input type="radio" id="order-random" class="btn" name="orderby" value="random">
                         <label for="order-random" title="Random Order" class="row">
-                            <?= jvbIcon('random') ?>
+                            <?= jvbIcon('shuffle') ?>
                             <span class="label">Random</span>
                         </label>
                     </div>
@@ -217,12 +217,12 @@
                 <div class="order-direction radio-group-label" data-for-order="date,title,karma">
                     <input type="radio" id="order-desc" class="btn" name="order" value="DESC" checked>
                     <label for="order-desc" title="Newest First" class="row">
-                        <?= jvbIcon('desc') ?>
+                        <?= jvbIcon('sort-descending') ?>
                     </label>
 
                     <input type="radio" id="order-asc" class="btn" name="order" value="ASC">
                     <label for="order-asc" title="Oldest First" class="row">
-                        <?= jvbIcon('asc') ?>
+                        <?= jvbIcon('sort-ascending') ?>
                     </label>
                 </div>
             </div>
@@ -297,11 +297,11 @@
             </div>
             <div class="item-actions">
                 <button type="button" class="action" data-action="edit" title="Edit tattoo">
-                    <?= jvbIcon('edit') ?>
+                    <?= jvbIcon('pencil-simple') ?>
                     <span class="screen-reader-text">Edit news item</span>
                 </button>
                 <button type="button" class="action" data-action="trash" title="Scrap tattoo">
-                    <?= jvbIcon('delete') ?>
+                    <?= jvbIcon('trash') ?>
                     <span class="screen-reader-text">Scrap news item</span>
                 </button>
                 <button type="button" class="action" data-action="toggle-status">

--
Gitblit v1.10.0