From 313cbf6b9b7aed2687633df872cdb0d4245eddcb Mon Sep 17 00:00:00 2001 From: MisterDetour Date: Mon, 24 Aug 2026 12:23:36 -0700 Subject: [PATCH 001/110] Remove some root directory files --- .github/workflows/cla.yml | 22 ------- .shopifyignore | 22 +++---- CODE_OF_CONDUCT.md | 132 -------------------------------------- CONTRIBUTING.md | 24 ------- LICENSE.md | 9 --- 5 files changed, 9 insertions(+), 200 deletions(-) delete mode 100644 .github/workflows/cla.yml delete mode 100644 CODE_OF_CONDUCT.md delete mode 100644 CONTRIBUTING.md delete mode 100644 LICENSE.md diff --git a/.github/workflows/cla.yml b/.github/workflows/cla.yml deleted file mode 100644 index 2c3a4042f..000000000 --- a/.github/workflows/cla.yml +++ /dev/null @@ -1,22 +0,0 @@ -name: Contributor License Agreement (CLA) - -on: - pull_request_target: - types: [opened, synchronize] - issue_comment: - types: [created] - -jobs: - cla: - runs-on: ubuntu-latest - if: | - (github.event.issue.pull_request - && !github.event.issue.pull_request.merged_at - && contains(github.event.comment.body, 'signed') - ) - || (github.event.pull_request && !github.event.pull_request.merged) - steps: - - uses: Shopify/shopify-cla-action@v1 - with: - github-token: ${{ secrets.GITHUB_TOKEN }} - cla-token: ${{ secrets.CLA_TOKEN }} diff --git a/.shopifyignore b/.shopifyignore index 59a11a13e..77876ea8c 100644 --- a/.shopifyignore +++ b/.shopifyignore @@ -1,13 +1,9 @@ -# This file contains a list of files that will be excluded from Shopify CLI -# operations such as push, pull, dev, etc. -# -# Examples: -# -# Ignore a specific file: -# templates/product.json -# -# Ignore templates with a wildcard pattern: -# templates/*.json -# -# Ignore templates and sections with a regular expression: -# /(templates|sections)/.*\.json/ +.gitignore +.gitattributes +.prettierrc.json +.theme-check.yml +.vscode/* +*.md +.git/* +.github/* +.shopify/* diff --git a/CODE_OF_CONDUCT.md b/CODE_OF_CONDUCT.md deleted file mode 100644 index 2ac6ce681..000000000 --- a/CODE_OF_CONDUCT.md +++ /dev/null @@ -1,132 +0,0 @@ -# Contributor Covenant Code of Conduct - -## Our Pledge - -We as members, contributors, and leaders pledge to make participation in our -community a harassment-free experience for everyone, regardless of age, body -size, visible or invisible disability, ethnicity, sex characteristics, gender -identity and expression, level of experience, education, socio-economic status, -nationality, personal appearance, race, caste, color, religion, or sexual -identity and orientation. - -We pledge to act and interact in ways that contribute to an open, welcoming, -diverse, inclusive, and healthy community. - -## Our Standards - -Examples of behavior that contributes to a positive environment for our -community include: - -- Demonstrating empathy and kindness toward other people -- Being respectful of differing opinions, viewpoints, and experiences -- Giving and gracefully accepting constructive feedback -- Accepting responsibility and apologizing to those affected by our mistakes, - and learning from the experience -- Focusing on what is best not just for us as individuals, but for the overall - community - -Examples of unacceptable behavior include: - -- The use of sexualized language or imagery, and sexual attention or advances of - any kind -- Trolling, insulting or derogatory comments, and personal or political attacks -- Public or private harassment -- Publishing others' private information, such as a physical or email address, - without their explicit permission -- Other conduct which could reasonably be considered inappropriate in a - professional setting - -## Enforcement Responsibilities - -Community leaders are responsible for clarifying and enforcing our standards of -acceptable behavior and will take appropriate and fair corrective action in -response to any behavior that they deem inappropriate, threatening, offensive, -or harmful. - -Community leaders have the right and responsibility to remove, edit, or reject -comments, commits, code, wiki edits, issues, and other contributions that are -not aligned to this Code of Conduct, and will communicate reasons for moderation -decisions when appropriate. - -## Scope - -This Code of Conduct applies within all community spaces, and also applies when -an individual is officially representing the community in public spaces. -Examples of representing our community include using an official email address, -posting via an official social media account, or acting as an appointed -representative at an online or offline event. - -## Enforcement - -Instances of abusive, harassing, or otherwise unacceptable behavior may be -reported to the community leaders responsible for enforcement at -opensource@shopify.com. -All complaints will be reviewed and investigated promptly and fairly. - -All community leaders are obligated to respect the privacy and security of the -reporter of any incident. - -## Enforcement Guidelines - -Community leaders will follow these Community Impact Guidelines in determining -the consequences for any action they deem in violation of this Code of Conduct: - -### 1. Correction - -**Community Impact**: Use of inappropriate language or other behavior deemed -unprofessional or unwelcome in the community. - -**Consequence**: A private, written warning from community leaders, providing -clarity around the nature of the violation and an explanation of why the -behavior was inappropriate. A public apology may be requested. - -### 2. Warning - -**Community Impact**: A violation through a single incident or series of -actions. - -**Consequence**: A warning with consequences for continued behavior. No -interaction with the people involved, including unsolicited interaction with -those enforcing the Code of Conduct, for a specified period of time. This -includes avoiding interactions in community spaces as well as external channels -like social media. Violating these terms may lead to a temporary or permanent -ban. - -### 3. Temporary Ban - -**Community Impact**: A serious violation of community standards, including -sustained inappropriate behavior. - -**Consequence**: A temporary ban from any sort of interaction or public -communication with the community for a specified period of time. No public or -private interaction with the people involved, including unsolicited interaction -with those enforcing the Code of Conduct, is allowed during this period. -Violating these terms may lead to a permanent ban. - -### 4. Permanent Ban - -**Community Impact**: Demonstrating a pattern of violation of community -standards, including sustained inappropriate behavior, harassment of an -individual, or aggression toward or disparagement of classes of individuals. - -**Consequence**: A permanent ban from any sort of public interaction within the -community. - -## Attribution - -This Code of Conduct is adapted from the [Contributor Covenant][homepage], -version 2.1, available at -[https://www.contributor-covenant.org/version/2/1/code_of_conduct.html][v2.1]. - -Community Impact Guidelines were inspired by -[Mozilla's code of conduct enforcement ladder][Mozilla CoC]. - -For answers to common questions about this code of conduct, see the FAQ at -[https://www.contributor-covenant.org/faq][FAQ]. Translations are available at -[https://www.contributor-covenant.org/translations][translations]. - -[homepage]: https://www.contributor-covenant.org -[v2.1]: https://www.contributor-covenant.org/version/2/1/code_of_conduct.html -[Mozilla CoC]: https://github.com/mozilla/diversity -[FAQ]: https://www.contributor-covenant.org/faq -[translations]: https://www.contributor-covenant.org/translations diff --git a/CONTRIBUTING.md b/CONTRIBUTING.md deleted file mode 100644 index 0877f7465..000000000 --- a/CONTRIBUTING.md +++ /dev/null @@ -1,24 +0,0 @@ -# Contributing to Skeleton Theme - -## How to contribute - -We ❤️ pull requests. If you'd like to fix a bug, contribute a feature, or just correct a typo, feel free to do so, as long as you follow our [Code of Conduct](./CODE_OF_CONDUCT.md). - -If you're thinking of adding a new feature or proposing a new pattern across the theme, please consider opening an issue first. This will allow us to discuss your idea, ensure it aligns with the project's direction, and potentially save you some time. - -For your contribution to be accepted, you'll need to sign the [Shopify Contributor License Agreement (CLA)](https://cla.shopify.com/). - -## Standards - -* This codebase must be minimalist, not a fully featured theme. -* This theme must provide a common foundational starting point for most developers. -* Do not include or reference legacy or non-recommended features. -* All changes must preserve the principles defined in the README. - -## Steps to contribute - -1. Fork the repository: [https://github.com/Shopify/skeleton-theme/fork](https://github.com/Shopify/skeleton-theme/fork) -2. Create your feature branch: `git checkout -b my-new-feature` -3. Commit your changes: `git commit -am 'Add some feature'` -4. Push to your branch: `git push origin my-new-feature` -5. Create a new Pull Request diff --git a/LICENSE.md b/LICENSE.md deleted file mode 100644 index 611b31d0a..000000000 --- a/LICENSE.md +++ /dev/null @@ -1,9 +0,0 @@ -Copyright (c) 2018-present Shopify Inc. - -Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, sell and/or create derivative works of the Software or any part thereof, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: - -The rights granted above may only be exercised to develop themes that integrate or interoperate with Shopify software or services, and, if applicable, to distribute, offer for sale or otherwise make available any such themes via the Shopify Theme Store. All other uses of the Software are strictly prohibited. - -The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. - -THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. From 9f32969de6d4bc589234ee466c19b8364312c395 Mon Sep 17 00:00:00 2001 From: MisterDetour Date: Mon, 24 Aug 2026 14:23:40 -0700 Subject: [PATCH 002/110] Add prettier config --- .prettierrc.json | 12 ++++++++++++ .vscode/extensions.json | 3 +++ .vscode/settings.json | 17 +++++++++++++++++ TODO.md | 13 +++++++++++++ assets/critical.css | 5 +---- 5 files changed, 46 insertions(+), 4 deletions(-) create mode 100644 .prettierrc.json create mode 100644 .vscode/extensions.json create mode 100644 .vscode/settings.json create mode 100644 TODO.md diff --git a/.prettierrc.json b/.prettierrc.json new file mode 100644 index 000000000..a7785dfc0 --- /dev/null +++ b/.prettierrc.json @@ -0,0 +1,12 @@ +{ + "printWidth": 120, + "singleQuote": true, + "overrides": [ + { + "files": "*.liquid", + "options": { + "singleQuote": false + } + } + ] +} diff --git a/.vscode/extensions.json b/.vscode/extensions.json new file mode 100644 index 000000000..f6480c2e7 --- /dev/null +++ b/.vscode/extensions.json @@ -0,0 +1,3 @@ +{ + "recommendations": ["shopify.theme-check-vscode", "esbenp.prettier-vscode"] +} diff --git a/.vscode/settings.json b/.vscode/settings.json new file mode 100644 index 000000000..cbc8dfc65 --- /dev/null +++ b/.vscode/settings.json @@ -0,0 +1,17 @@ +{ + "editor.tabSize": 2, + "editor.insertSpaces": true, + "editor.formatOnSave": false, + "[javascript]": { + "editor.formatOnSave": true, + "editor.defaultFormatter": "esbenp.prettier-vscode" + }, + "[css]": { + "editor.formatOnSave": true + }, + "[liquid]": { + "editor.defaultFormatter": "Shopify.theme-check-vscode", + "editor.formatOnSave": true + }, + "themeCheck.checkOnSave": true +} diff --git a/TODO.md b/TODO.md new file mode 100644 index 000000000..70929710b --- /dev/null +++ b/TODO.md @@ -0,0 +1,13 @@ +# To Dos + +- Collection (quick add to cart) +- PDP +- Cart +- Cart drawer +- Mobile menu +- Announcement bar +- Category filtering +- Predictive search +- Review all Dawn settings to see what we might want +- See how apps work with in general +- Theme Blocks https://shopify.dev/docs/storefronts/themes/architecture/blocks/theme-blocks/quick-start?framework=liquid diff --git a/assets/critical.css b/assets/critical.css index cdb1ae1aa..b839627db 100644 --- a/assets/critical.css +++ b/assets/critical.css @@ -92,10 +92,7 @@ body { * the entire viewport. */ .shopify-section { - --content-width: min( - calc(var(--page-width) - var(--page-margin) * 2), - calc(100% - var(--page-margin) * 2) - ); + --content-width: min(calc(var(--page-width) - var(--page-margin) * 2), calc(100% - var(--page-margin) * 2)); --content-margin: minmax(var(--page-margin), 1fr); --content-grid: var(--content-margin) var(--content-width) var(--content-margin); From c08156ffbc10150e3e9f94ecfdb8b58a4e580fd1 Mon Sep 17 00:00:00 2001 From: MisterDetour Date: Mon, 24 Aug 2026 14:33:51 -0700 Subject: [PATCH 003/110] Remove section description comments --- TODO.md | 8 ++++++-- sections/404.liquid | 7 ------- sections/article.liquid | 8 -------- sections/blog.liquid | 7 ------- sections/cart.liquid | 7 ------- sections/collection.liquid | 7 ------- sections/collections.liquid | 7 ------- sections/hello-world.liquid | 4 ---- sections/page.liquid | 7 ------- sections/password.liquid | 7 ------- sections/product.liquid | 7 ------- sections/search.liquid | 7 ------- 12 files changed, 6 insertions(+), 77 deletions(-) diff --git a/TODO.md b/TODO.md index 70929710b..de71e309d 100644 --- a/TODO.md +++ b/TODO.md @@ -1,13 +1,17 @@ # To Dos -- Collection (quick add to cart) - PDP -- Cart +- Clean up existing files & code - Cart drawer +- Collection (quick add to cart) +- Cart - Mobile menu - Announcement bar - Category filtering - Predictive search +- Password page +- Go through each template file - Review all Dawn settings to see what we might want - See how apps work with in general - Theme Blocks https://shopify.dev/docs/storefronts/themes/architecture/blocks/theme-blocks/quick-start?framework=liquid +- Modify readme diff --git a/sections/404.liquid b/sections/404.liquid index 076cfc919..b46077c1b 100644 --- a/sections/404.liquid +++ b/sections/404.liquid @@ -1,10 +1,3 @@ -{% comment %} - This section is used in the 404 template to render page shown when customers - enter an invalid store URL. - - https://shopify.dev/docs/storefronts/themes/architecture/templates/404 -{% endcomment %} -

{{ '404.title' | t }}

diff --git a/sections/article.liquid b/sections/article.liquid index 146acd1ed..615fce083 100644 --- a/sections/article.liquid +++ b/sections/article.liquid @@ -1,11 +1,3 @@ -{% comment %} - This section is used in the article template to render the article page, which - displays the full content of the blog post and can optionally include a - comments section for customers (not shown in this example). - - https://shopify.dev/docs/storefronts/themes/architecture/templates/article -{% endcomment %} - {% if article.image %} {{ article.image | image_url: width: 1000 | image_tag }} {% endif %} diff --git a/sections/blog.liquid b/sections/blog.liquid index 0ea2113bd..2e0d36586 100644 --- a/sections/blog.liquid +++ b/sections/blog.liquid @@ -1,10 +1,3 @@ -{% comment %} - This section is used in the blog template to render the blog page listing all - articles within a blog. - - https://shopify.dev/docs/storefronts/themes/architecture/templates/blog -{% endcomment %} -

{{ blog.title }}

{% paginate blog.articles by 5 %} diff --git a/sections/cart.liquid b/sections/cart.liquid index 6d7e49841..a806f0bb5 100644 --- a/sections/cart.liquid +++ b/sections/cart.liquid @@ -1,10 +1,3 @@ -{% comment %} - This section is used in the cart template to render /cart page with an - overview of the items in customer's cart. - - https://shopify.dev/docs/storefronts/themes/architecture/templates/cart -{% endcomment %} -

{{ 'cart.title' | t }}

diff --git a/sections/collection.liquid b/sections/collection.liquid index e5a786805..9b7ecaded 100644 --- a/sections/collection.liquid +++ b/sections/collection.liquid @@ -1,10 +1,3 @@ -{% comment %} - This section is used in the collection template to render collection page - listing all products within a collection. - - https://shopify.dev/docs/storefronts/themes/architecture/templates/collection -{% endcomment %} -

{{ collection.title }}

diff --git a/sections/collections.liquid b/sections/collections.liquid index db897a3dd..e0e099fd5 100644 --- a/sections/collections.liquid +++ b/sections/collections.liquid @@ -1,10 +1,3 @@ -{% comment %} - This section is used in the list-collections template to render a list of - collections. - - https://shopify.dev/docs/storefronts/themes/architecture/templates/collection -{% endcomment %} -

{{ 'collections.title' | t }}

diff --git a/sections/page.liquid b/sections/page.liquid index 1a3fea126..6a0482961 100644 --- a/sections/page.liquid +++ b/sections/page.liquid @@ -1,10 +1,3 @@ -{% comment %} - This section is used in the page template to render store pages like About us - or Contact us. - - https://shopify.dev/docs/storefronts/themes/architecture/templates/page -{% endcomment %} -

{{ page.title }}

{{ page.content }} diff --git a/sections/password.liquid b/sections/password.liquid index 6f2272182..5248347f2 100644 --- a/sections/password.liquid +++ b/sections/password.liquid @@ -1,10 +1,3 @@ -{% comment %} - This section is used in the password template to render landing page shown - when password protection is applied to a store. - - https://shopify.dev/docs/storefronts/themes/architecture/templates/password -{% endcomment %} -

{{ 'password.title' | t }}

{% if shop.password_message %} diff --git a/sections/product.liquid b/sections/product.liquid index 9664dfda5..d46307f3f 100644 --- a/sections/product.liquid +++ b/sections/product.liquid @@ -1,10 +1,3 @@ -{% comment %} - This section is used in the product template to render product page with - media, content, and add-to-cart form. - - https://shopify.dev/docs/storefronts/themes/architecture/templates/product -{% endcomment %} -
{% for image in product.images %} {% render 'image', class: 'product-image', image: image %} diff --git a/sections/search.liquid b/sections/search.liquid index 7177c1818..7259ed711 100644 --- a/sections/search.liquid +++ b/sections/search.liquid @@ -1,10 +1,3 @@ -{% comment %} - This section is used in the search template to render search results for - products, articles, and pages. - - https://shopify.dev/docs/storefronts/themes/architecture/templates/search -{% endcomment %} -

{{ 'search.title' | t }}

From e92031705368b43abbfd5c85db0174798754a96d Mon Sep 17 00:00:00 2001 From: MisterDetour Date: Mon, 24 Aug 2026 15:04:04 -0700 Subject: [PATCH 004/110] Start working on pdp main --- TODO.md | 3 ++ assets/critical.css | 5 +++ assets/swiper-bundle.min.css | 13 ++++++++ assets/swiper-bundle.min.js | 14 +++++++++ sections/product.liquid | 60 ++++++++++++++++++++++++++++++------ 5 files changed, 86 insertions(+), 9 deletions(-) create mode 100644 assets/swiper-bundle.min.css create mode 100644 assets/swiper-bundle.min.js diff --git a/TODO.md b/TODO.md index de71e309d..19072a065 100644 --- a/TODO.md +++ b/TODO.md @@ -7,9 +7,12 @@ - Cart - Mobile menu - Announcement bar +- Multi variant images - Category filtering - Predictive search - Password page +- Header & Footer styles should be moved to critical +- Review class names on all exisiting code - Go through each template file - Review all Dawn settings to see what we might want - See how apps work with in general diff --git a/assets/critical.css b/assets/critical.css index b839627db..b2a43febc 100644 --- a/assets/critical.css +++ b/assets/critical.css @@ -6,7 +6,12 @@ margin: 0; } +html { + font-size: 62.5%; +} + body { + font-size: 1.6rem; display: flex; flex-direction: column; margin: 0; diff --git a/assets/swiper-bundle.min.css b/assets/swiper-bundle.min.css new file mode 100644 index 000000000..c5bf0fb2b --- /dev/null +++ b/assets/swiper-bundle.min.css @@ -0,0 +1,13 @@ +/** + * Swiper 12.0.3 + * Most modern mobile touch slider and framework with hardware accelerated transitions + * https://swiperjs.com + * + * Copyright 2014-2025 Vladimir Kharlampidi + * + * Released under the MIT License + * + * Released on: October 21, 2025 + */ + +:root{--swiper-theme-color:#007aff}:host{display:block;margin-left:auto;margin-right:auto;position:relative;z-index:1}.swiper{display:block;list-style:none;margin-left:auto;margin-right:auto;overflow:hidden;padding:0;position:relative;z-index:1}.swiper-vertical>.swiper-wrapper{flex-direction:column}.swiper-wrapper{box-sizing:initial;display:flex;height:100%;position:relative;transition-property:transform;transition-timing-function:var(--swiper-wrapper-transition-timing-function,initial);width:100%;z-index:1}.swiper-android .swiper-slide,.swiper-ios .swiper-slide,.swiper-wrapper{transform:translateZ(0)}.swiper-horizontal{touch-action:pan-y}.swiper-vertical{touch-action:pan-x}.swiper-slide{display:block;flex-shrink:0;height:100%;position:relative;transition-property:transform;width:100%}.swiper-slide-invisible-blank{visibility:hidden}.swiper-autoheight,.swiper-autoheight .swiper-slide{height:auto}.swiper-autoheight .swiper-wrapper{align-items:flex-start;transition-property:transform,height}.swiper-backface-hidden .swiper-slide{backface-visibility:hidden;transform:translateZ(0)}.swiper-3d.swiper-css-mode .swiper-wrapper{perspective:1200px}.swiper-3d .swiper-wrapper{transform-style:preserve-3d}.swiper-3d{perspective:1200px;.swiper-cube-shadow,.swiper-slide{transform-style:preserve-3d}}.swiper-css-mode{>.swiper-wrapper{overflow:auto;scrollbar-width:none;-ms-overflow-style:none;&::-webkit-scrollbar{display:none}}>.swiper-wrapper>.swiper-slide{scroll-snap-align:start start}&.swiper-horizontal{>.swiper-wrapper{scroll-snap-type:x mandatory}}&.swiper-vertical{>.swiper-wrapper{scroll-snap-type:y mandatory}}&.swiper-free-mode{>.swiper-wrapper{scroll-snap-type:none}>.swiper-wrapper>.swiper-slide{scroll-snap-align:none}}&.swiper-centered{>.swiper-wrapper:before{content:"";flex-shrink:0;order:9999}>.swiper-wrapper>.swiper-slide{scroll-snap-align:center center;scroll-snap-stop:always}}&.swiper-centered.swiper-horizontal{>.swiper-wrapper>.swiper-slide:first-child{margin-inline-start:var(--swiper-centered-offset-before)}>.swiper-wrapper:before{height:100%;min-height:1px;width:var(--swiper-centered-offset-after)}}&.swiper-centered.swiper-vertical{>.swiper-wrapper>.swiper-slide:first-child{margin-block-start:var(--swiper-centered-offset-before)}>.swiper-wrapper:before{height:var(--swiper-centered-offset-after);min-width:1px;width:100%}}}.swiper-3d{.swiper-slide-shadow,.swiper-slide-shadow-bottom,.swiper-slide-shadow-left,.swiper-slide-shadow-right,.swiper-slide-shadow-top{height:100%;left:0;pointer-events:none;position:absolute;top:0;width:100%;z-index:10}.swiper-slide-shadow{background:#00000026}.swiper-slide-shadow-left{background-image:linear-gradient(270deg,#00000080,#0000)}.swiper-slide-shadow-right{background-image:linear-gradient(90deg,#00000080,#0000)}.swiper-slide-shadow-top{background-image:linear-gradient(0deg,#00000080,#0000)}.swiper-slide-shadow-bottom{background-image:linear-gradient(180deg,#00000080,#0000)}}.swiper-lazy-preloader{border:4px solid var(--swiper-preloader-color,var(--swiper-theme-color));border-radius:50%;border-top:4px solid #0000;box-sizing:border-box;height:42px;left:50%;margin-left:-21px;margin-top:-21px;position:absolute;top:50%;transform-origin:50%;width:42px;z-index:10}.swiper-watch-progress .swiper-slide-visible,.swiper:not(.swiper-watch-progress){.swiper-lazy-preloader{animation:swiper-preloader-spin 1s linear infinite}}.swiper-lazy-preloader-white{--swiper-preloader-color:#fff}.swiper-lazy-preloader-black{--swiper-preloader-color:#000}@keyframes swiper-preloader-spin{0%{transform:rotate(0deg)}to{transform:rotate(1turn)}}.swiper-virtual .swiper-slide{-webkit-backface-visibility:hidden;transform:translateZ(0)}.swiper-virtual.swiper-css-mode{.swiper-wrapper:after{content:"";left:0;pointer-events:none;position:absolute;top:0}}.swiper-virtual.swiper-css-mode.swiper-horizontal{.swiper-wrapper:after{height:1px;width:var(--swiper-virtual-size)}}.swiper-virtual.swiper-css-mode.swiper-vertical{.swiper-wrapper:after{height:var(--swiper-virtual-size);width:1px}}:root{--swiper-navigation-size:44px}.swiper-button-next,.swiper-button-prev{align-items:center;color:var(--swiper-navigation-color,var(--swiper-theme-color));cursor:pointer;display:flex;height:var(--swiper-navigation-size);justify-content:center;position:absolute;width:var(--swiper-navigation-size);z-index:10;&.swiper-button-disabled{cursor:auto;opacity:.35;pointer-events:none}&.swiper-button-hidden{cursor:auto;opacity:0;pointer-events:none}.swiper-navigation-disabled &{display:none!important}svg{height:100%;object-fit:contain;transform-origin:center;width:100%;fill:currentColor;pointer-events:none}}.swiper-button-lock{display:none}.swiper-button-next,.swiper-button-prev{margin-top:calc(0px - var(--swiper-navigation-size)/2);top:var(--swiper-navigation-top-offset,50%)}.swiper-button-prev{left:var(--swiper-navigation-sides-offset,4px);right:auto;.swiper-navigation-icon{transform:rotate(180deg)}}.swiper-button-next{left:auto;right:var(--swiper-navigation-sides-offset,4px)}.swiper-horizontal{.swiper-button-next,.swiper-button-prev,~.swiper-button-next,~.swiper-button-prev{margin-left:0;margin-top:calc(0px - var(--swiper-navigation-size)/2);top:var(--swiper-navigation-top-offset,50%)}&.swiper-rtl .swiper-button-next,&.swiper-rtl~.swiper-button-next,&~.swiper-button-prev,.swiper-button-prev{left:var(--swiper-navigation-sides-offset,4px);right:auto}&.swiper-rtl .swiper-button-prev,&.swiper-rtl~.swiper-button-prev,&~.swiper-button-next,.swiper-button-next{left:auto;right:var(--swiper-navigation-sides-offset,4px)}&.swiper-rtl .swiper-button-next,&.swiper-rtl~.swiper-button-next,&~.swiper-button-prev,.swiper-button-prev{.swiper-navigation-icon{transform:rotate(180deg)}}&.swiper-rtl .swiper-button-prev,&.swiper-rtl~.swiper-button-prev{.swiper-navigation-icon{transform:rotate(0deg)}}}.swiper-vertical{.swiper-button-next,.swiper-button-prev,~.swiper-button-next,~.swiper-button-prev{left:var(--swiper-navigation-top-offset,50%);margin-left:calc(0px - var(--swiper-navigation-size)/2);margin-top:0;right:auto}.swiper-button-prev,~.swiper-button-prev{bottom:auto;top:var(--swiper-navigation-sides-offset,4px);.swiper-navigation-icon{transform:rotate(-90deg)}}.swiper-button-next,~.swiper-button-next{bottom:var(--swiper-navigation-sides-offset,4px);top:auto;.swiper-navigation-icon{transform:rotate(90deg)}}}.swiper-pagination{position:absolute;text-align:center;transform:translateZ(0);transition:opacity .3s;z-index:10;&.swiper-pagination-hidden{opacity:0}&.swiper-pagination-disabled,.swiper-pagination-disabled>&{display:none!important}}.swiper-horizontal>.swiper-pagination-bullets,.swiper-pagination-bullets.swiper-pagination-horizontal,.swiper-pagination-custom,.swiper-pagination-fraction{bottom:var(--swiper-pagination-bottom,8px);left:0;top:var(--swiper-pagination-top,auto);width:100%}.swiper-pagination-bullets-dynamic{font-size:0;overflow:hidden;.swiper-pagination-bullet{position:relative;transform:scale(.33)}.swiper-pagination-bullet-active,.swiper-pagination-bullet-active-main{transform:scale(1)}.swiper-pagination-bullet-active-prev{transform:scale(.66)}.swiper-pagination-bullet-active-prev-prev{transform:scale(.33)}.swiper-pagination-bullet-active-next{transform:scale(.66)}.swiper-pagination-bullet-active-next-next{transform:scale(.33)}}.swiper-pagination-bullet{background:var(--swiper-pagination-bullet-inactive-color,#000);border-radius:var(--swiper-pagination-bullet-border-radius,50%);display:inline-block;height:var(--swiper-pagination-bullet-height,var(--swiper-pagination-bullet-size,8px));opacity:var(--swiper-pagination-bullet-inactive-opacity,.2);width:var(--swiper-pagination-bullet-width,var(--swiper-pagination-bullet-size,8px));button&{appearance:none;border:none;box-shadow:none;margin:0;padding:0}.swiper-pagination-clickable &{cursor:pointer}&:only-child{display:none!important}}.swiper-pagination-bullet-active{background:var(--swiper-pagination-color,var(--swiper-theme-color));opacity:var(--swiper-pagination-bullet-opacity,1)}.swiper-pagination-vertical.swiper-pagination-bullets,.swiper-vertical>.swiper-pagination-bullets{left:var(--swiper-pagination-left,auto);right:var(--swiper-pagination-right,8px);top:50%;transform:translate3d(0,-50%,0);.swiper-pagination-bullet{display:block;margin:var(--swiper-pagination-bullet-vertical-gap,6px) 0}&.swiper-pagination-bullets-dynamic{top:50%;transform:translateY(-50%);width:8px;.swiper-pagination-bullet{display:inline-block;transition:transform .2s,top .2s}}}.swiper-horizontal>.swiper-pagination-bullets,.swiper-pagination-horizontal.swiper-pagination-bullets{.swiper-pagination-bullet{margin:0 var(--swiper-pagination-bullet-horizontal-gap,4px)}&.swiper-pagination-bullets-dynamic{left:50%;transform:translateX(-50%);white-space:nowrap;.swiper-pagination-bullet{transition:transform .2s,left .2s}}}.swiper-horizontal.swiper-rtl>.swiper-pagination-bullets-dynamic .swiper-pagination-bullet{transition:transform .2s,right .2s}.swiper-pagination-fraction{color:var(--swiper-pagination-fraction-color,inherit)}.swiper-pagination-progressbar{background:var(--swiper-pagination-progressbar-bg-color,#00000040);position:absolute;.swiper-pagination-progressbar-fill{background:var(--swiper-pagination-color,var(--swiper-theme-color));height:100%;left:0;position:absolute;top:0;transform:scale(0);transform-origin:left top;width:100%}.swiper-rtl & .swiper-pagination-progressbar-fill{transform-origin:right top}&.swiper-pagination-horizontal,&.swiper-pagination-vertical.swiper-pagination-progressbar-opposite,.swiper-horizontal>&,.swiper-vertical>&.swiper-pagination-progressbar-opposite{height:var(--swiper-pagination-progressbar-size,4px);left:0;top:0;width:100%}&.swiper-pagination-horizontal.swiper-pagination-progressbar-opposite,&.swiper-pagination-vertical,.swiper-horizontal>&.swiper-pagination-progressbar-opposite,.swiper-vertical>&{height:100%;left:0;top:0;width:var(--swiper-pagination-progressbar-size,4px)}}.swiper-pagination-lock{display:none}.swiper-scrollbar{background:var(--swiper-scrollbar-bg-color,#0000001a);border-radius:var(--swiper-scrollbar-border-radius,10px);position:relative;touch-action:none;&.swiper-scrollbar-disabled,.swiper-scrollbar-disabled>&{display:none!important}&.swiper-scrollbar-horizontal,.swiper-horizontal>&{bottom:var(--swiper-scrollbar-bottom,4px);height:var(--swiper-scrollbar-size,4px);left:var(--swiper-scrollbar-sides-offset,1%);position:absolute;top:var(--swiper-scrollbar-top,auto);width:calc(100% - var(--swiper-scrollbar-sides-offset, 1%)*2);z-index:50}&.swiper-scrollbar-vertical,.swiper-vertical>&{height:calc(100% - var(--swiper-scrollbar-sides-offset, 1%)*2);left:var(--swiper-scrollbar-left,auto);position:absolute;right:var(--swiper-scrollbar-right,4px);top:var(--swiper-scrollbar-sides-offset,1%);width:var(--swiper-scrollbar-size,4px);z-index:50}}.swiper-scrollbar-drag{background:var(--swiper-scrollbar-drag-bg-color,#00000080);border-radius:var(--swiper-scrollbar-border-radius,10px);height:100%;left:0;position:relative;top:0;width:100%}.swiper-scrollbar-cursor-drag{cursor:move}.swiper-scrollbar-lock{display:none}.swiper-zoom-container{align-items:center;display:flex;height:100%;justify-content:center;text-align:center;width:100%;>canvas,>img,>svg{max-height:100%;max-width:100%;object-fit:contain}}.swiper-slide-zoomed{cursor:move;touch-action:none}.swiper .swiper-notification{left:0;opacity:0;pointer-events:none;position:absolute;top:0;z-index:-1000}.swiper-free-mode>.swiper-wrapper{margin:0 auto;transition-timing-function:ease-out}.swiper-grid>.swiper-wrapper{flex-wrap:wrap}.swiper-grid-column>.swiper-wrapper{flex-direction:column;flex-wrap:wrap}.swiper-fade{&.swiper-free-mode{.swiper-slide{transition-timing-function:ease-out}}.swiper-slide{pointer-events:none;transition-property:opacity;.swiper-slide{pointer-events:none}}.swiper-slide-active{pointer-events:auto;& .swiper-slide-active{pointer-events:auto}}}.swiper.swiper-cube{overflow:visible}.swiper-cube{.swiper-slide{backface-visibility:hidden;height:100%;pointer-events:none;transform-origin:0 0;visibility:hidden;width:100%;z-index:1;.swiper-slide{pointer-events:none}}&.swiper-rtl .swiper-slide{transform-origin:100% 0}.swiper-slide-active{&,& .swiper-slide-active{pointer-events:auto}}.swiper-slide-active,.swiper-slide-next,.swiper-slide-prev{pointer-events:auto;visibility:visible}.swiper-cube-shadow{bottom:0;height:100%;left:0;opacity:.6;position:absolute;width:100%;z-index:0;&:before{background:#000;bottom:0;content:"";filter:blur(50px);left:0;position:absolute;right:0;top:0}}}.swiper-cube{.swiper-slide-next+.swiper-slide{pointer-events:auto;visibility:visible}}.swiper-cube{.swiper-slide-shadow-cube.swiper-slide-shadow-bottom,.swiper-slide-shadow-cube.swiper-slide-shadow-left,.swiper-slide-shadow-cube.swiper-slide-shadow-right,.swiper-slide-shadow-cube.swiper-slide-shadow-top{backface-visibility:hidden;z-index:0}}.swiper.swiper-flip{overflow:visible}.swiper-flip{.swiper-slide{backface-visibility:hidden;pointer-events:none;z-index:1;.swiper-slide{pointer-events:none}}.swiper-slide-active{&,& .swiper-slide-active{pointer-events:auto}}}.swiper-flip{.swiper-slide-shadow-flip.swiper-slide-shadow-bottom,.swiper-slide-shadow-flip.swiper-slide-shadow-left,.swiper-slide-shadow-flip.swiper-slide-shadow-right,.swiper-slide-shadow-flip.swiper-slide-shadow-top{backface-visibility:hidden;z-index:0}}.swiper-creative{.swiper-slide{backface-visibility:hidden;overflow:hidden;transition-property:transform,opacity,height}}.swiper.swiper-cards{overflow:visible}.swiper-cards{.swiper-slide{backface-visibility:hidden;overflow:hidden;transform-origin:center bottom}} \ No newline at end of file diff --git a/assets/swiper-bundle.min.js b/assets/swiper-bundle.min.js new file mode 100644 index 000000000..343d5a329 --- /dev/null +++ b/assets/swiper-bundle.min.js @@ -0,0 +1,14 @@ +/** + * Swiper 12.0.3 + * Most modern mobile touch slider and framework with hardware accelerated transitions + * https://swiperjs.com + * + * Copyright 2014-2025 Vladimir Kharlampidi + * + * Released under the MIT License + * + * Released on: October 21, 2025 + */ + +var Swiper=function(){"use strict";function e(e){return null!==e&&"object"==typeof e&&"constructor"in e&&e.constructor===Object}function t(s={},a={}){const i=["__proto__","constructor","prototype"];Object.keys(a).filter(e=>i.indexOf(e)<0).forEach(i=>{void 0===s[i]?s[i]=a[i]:e(a[i])&&e(s[i])&&Object.keys(a[i]).length>0&&t(s[i],a[i])})}const s={body:{},addEventListener(){},removeEventListener(){},activeElement:{blur(){},nodeName:""},querySelector:()=>null,querySelectorAll:()=>[],getElementById:()=>null,createEvent:()=>({initEvent(){}}),createElement:()=>({children:[],childNodes:[],style:{},setAttribute(){},getElementsByTagName:()=>[]}),createElementNS:()=>({}),importNode:()=>null,location:{hash:"",host:"",hostname:"",href:"",origin:"",pathname:"",protocol:"",search:""}};function a(){const e="undefined"!=typeof document?document:{};return t(e,s),e}const i={document:s,navigator:{userAgent:""},location:{hash:"",host:"",hostname:"",href:"",origin:"",pathname:"",protocol:"",search:""},history:{replaceState(){},pushState(){},go(){},back(){}},CustomEvent:function(){return this},addEventListener(){},removeEventListener(){},getComputedStyle:()=>({getPropertyValue:()=>""}),Image(){},Date(){},screen:{},setTimeout(){},clearTimeout(){},matchMedia:()=>({}),requestAnimationFrame:e=>"undefined"==typeof setTimeout?(e(),null):setTimeout(e,0),cancelAnimationFrame(e){"undefined"!=typeof setTimeout&&clearTimeout(e)}};function r(){const e="undefined"!=typeof window?window:{};return t(e,i),e}function n(e=""){return e.trim().split(" ").filter(e=>!!e.trim())}function l(e,t=0){return setTimeout(e,t)}function o(){return Date.now()}function d(e,t="x"){const s=r();let a,i,n;const l=function(e){const t=r();let s;return t.getComputedStyle&&(s=t.getComputedStyle(e,null)),!s&&e.currentStyle&&(s=e.currentStyle),s||(s=e.style),s}(e);return s.WebKitCSSMatrix?(i=l.transform||l.webkitTransform,i.split(",").length>6&&(i=i.split(", ").map(e=>e.replace(",",".")).join(", ")),n=new s.WebKitCSSMatrix("none"===i?"":i)):(n=l.MozTransform||l.OTransform||l.MsTransform||l.msTransform||l.transform||l.getPropertyValue("transform").replace("translate(","matrix(1, 0, 0, 1,"),a=n.toString().split(",")),"x"===t&&(i=s.WebKitCSSMatrix?n.m41:16===a.length?parseFloat(a[12]):parseFloat(a[4])),"y"===t&&(i=s.WebKitCSSMatrix?n.m42:16===a.length?parseFloat(a[13]):parseFloat(a[5])),i||0}function c(e){return"object"==typeof e&&null!==e&&e.constructor&&"Object"===Object.prototype.toString.call(e).slice(8,-1)}function p(e){return"undefined"!=typeof window&&void 0!==window.HTMLElement?e instanceof HTMLElement:e&&(1===e.nodeType||11===e.nodeType)}function u(...e){const t=Object(e[0]),s=["__proto__","constructor","prototype"];for(let a=1;as.indexOf(e)<0);for(let s=0,a=e.length;si?"next":"prev",c=(e,t)=>"next"===d&&e>=t||"prev"===d&&e<=t,p=()=>{n=(new Date).getTime(),null===l&&(l=n);const r=Math.max(Math.min((n-l)/o,1),0),d=.5-Math.cos(r*Math.PI)/2;let u=i+d*(t-i);if(c(u,t)&&(u=t),e.wrapperEl.scrollTo({[s]:u}),c(u,t))return e.wrapperEl.style.overflow="hidden",e.wrapperEl.style.scrollSnapType="",setTimeout(()=>{e.wrapperEl.style.overflow="",e.wrapperEl.scrollTo({[s]:u})}),void a.cancelAnimationFrame(e.cssModeFrameID);e.cssModeFrameID=a.requestAnimationFrame(p)};p()}function f(e){return e.querySelector(".swiper-slide-transform")||e.shadowRoot&&e.shadowRoot.querySelector(".swiper-slide-transform")||e}function g(e,t=""){const s=r(),a=[...e.children];return s.HTMLSlotElement&&e instanceof HTMLSlotElement&&a.push(...e.assignedElements()),t?a.filter(e=>e.matches(t)):a}function v(e){try{return void console.warn(e)}catch(e){}}function w(e,t=[]){const s=document.createElement(e);return s.classList.add(...Array.isArray(t)?t:n(t)),s}function b(e){const t=r(),s=a(),i=e.getBoundingClientRect(),n=s.body,l=e.clientTop||n.clientTop||0,o=e.clientLeft||n.clientLeft||0,d=e===t?t.scrollY:e.scrollTop,c=e===t?t.scrollX:e.scrollLeft;return{top:i.top+d-l,left:i.left+c-o}}function y(e,t){return r().getComputedStyle(e,null).getPropertyValue(t)}function E(e){let t,s=e;if(s){for(t=0;null!==(s=s.previousSibling);)1===s.nodeType&&(t+=1);return t}}function x(e,t){const s=[];let a=e.parentElement;for(;a;)t?a.matches(t)&&s.push(a):s.push(a),a=a.parentElement;return s}function S(e,t){t&&e.addEventListener("transitionend",function s(a){a.target===e&&(t.call(e,a),e.removeEventListener("transitionend",s))})}function T(e,t,s){const a=r();return s?e["width"===t?"offsetWidth":"offsetHeight"]+parseFloat(a.getComputedStyle(e,null).getPropertyValue("width"===t?"margin-right":"margin-top"))+parseFloat(a.getComputedStyle(e,null).getPropertyValue("width"===t?"margin-left":"margin-bottom")):e.offsetWidth}function M(e){return(Array.isArray(e)?e:[e]).filter(e=>!!e)}function C(e){return t=>Math.abs(t)>0&&e.browser&&e.browser.need3dFix&&Math.abs(t)%90==0?t+.001:t}function P(e,t=""){"undefined"!=typeof trustedTypes?e.innerHTML=trustedTypes.createPolicy("html",{createHTML:e=>e}).createHTML(t):e.innerHTML=t}let L,I,z;function A(){return L||(L=function(){const e=r(),t=a();return{smoothScroll:t.documentElement&&t.documentElement.style&&"scrollBehavior"in t.documentElement.style,touch:!!("ontouchstart"in e||e.DocumentTouch&&t instanceof e.DocumentTouch)}}()),L}function $(e={}){return I||(I=function({userAgent:e}={}){const t=A(),s=r(),a=s.navigator.platform,i=e||s.navigator.userAgent,n={ios:!1,android:!1},l=s.screen.width,o=s.screen.height,d=i.match(/(Android);?[\s\/]+([\d.]+)?/);let c=i.match(/(iPad)(?!\1).*OS\s([\d_]+)/);const p=i.match(/(iPod)(.*OS\s([\d_]+))?/),u=!c&&i.match(/(iPhone\sOS|iOS)\s([\d_]+)/),m="Win32"===a;let h="MacIntel"===a;return!c&&h&&t.touch&&["1024x1366","1366x1024","834x1194","1194x834","834x1112","1112x834","768x1024","1024x768","820x1180","1180x820","810x1080","1080x810"].indexOf(`${l}x${o}`)>=0&&(c=i.match(/(Version)\/([\d.]+)/),c||(c=[0,1,"13_0_0"]),h=!1),d&&!m&&(n.os="android",n.android=!0),(c||u||p)&&(n.os="ios",n.ios=!0),n}(e)),I}function k(){return z||(z=function(){const e=r(),t=$();let s=!1;function a(){const t=e.navigator.userAgent.toLowerCase();return t.indexOf("safari")>=0&&t.indexOf("chrome")<0&&t.indexOf("android")<0}if(a()){const t=String(e.navigator.userAgent);if(t.includes("Version/")){const[e,a]=t.split("Version/")[1].split(" ")[0].split(".").map(e=>Number(e));s=e<16||16===e&&a<2}}const i=/(iPhone|iPod|iPad).*AppleWebKit(?!.*Safari)/i.test(e.navigator.userAgent),n=a();return{isSafari:s||n,needPerspectiveFix:s,need3dFix:n||i&&t.ios,isWebView:i}}()),z}var O={on(e,t,s){const a=this;if(!a.eventsListeners||a.destroyed)return a;if("function"!=typeof t)return a;const i=s?"unshift":"push";return e.split(" ").forEach(e=>{a.eventsListeners[e]||(a.eventsListeners[e]=[]),a.eventsListeners[e][i](t)}),a},once(e,t,s){const a=this;if(!a.eventsListeners||a.destroyed)return a;if("function"!=typeof t)return a;function i(...s){a.off(e,i),i.__emitterProxy&&delete i.__emitterProxy,t.apply(a,s)}return i.__emitterProxy=t,a.on(e,i,s)},onAny(e,t){const s=this;if(!s.eventsListeners||s.destroyed)return s;if("function"!=typeof e)return s;const a=t?"unshift":"push";return s.eventsAnyListeners.indexOf(e)<0&&s.eventsAnyListeners[a](e),s},offAny(e){const t=this;if(!t.eventsListeners||t.destroyed)return t;if(!t.eventsAnyListeners)return t;const s=t.eventsAnyListeners.indexOf(e);return s>=0&&t.eventsAnyListeners.splice(s,1),t},off(e,t){const s=this;return!s.eventsListeners||s.destroyed?s:s.eventsListeners?(e.split(" ").forEach(e=>{void 0===t?s.eventsListeners[e]=[]:s.eventsListeners[e]&&s.eventsListeners[e].forEach((a,i)=>{(a===t||a.__emitterProxy&&a.__emitterProxy===t)&&s.eventsListeners[e].splice(i,1)})}),s):s},emit(...e){const t=this;if(!t.eventsListeners||t.destroyed)return t;if(!t.eventsListeners)return t;let s,a,i;"string"==typeof e[0]||Array.isArray(e[0])?(s=e[0],a=e.slice(1,e.length),i=t):(s=e[0].events,a=e[0].data,i=e[0].context||t),a.unshift(i);return(Array.isArray(s)?s:s.split(" ")).forEach(e=>{t.eventsAnyListeners&&t.eventsAnyListeners.length&&t.eventsAnyListeners.forEach(t=>{t.apply(i,[e,...a])}),t.eventsListeners&&t.eventsListeners[e]&&t.eventsListeners[e].forEach(e=>{e.apply(i,a)})}),t}};const D=(e,t,s)=>{t&&!e.classList.contains(s)?e.classList.add(s):!t&&e.classList.contains(s)&&e.classList.remove(s)};const G=(e,t,s)=>{t&&!e.classList.contains(s)?e.classList.add(s):!t&&e.classList.contains(s)&&e.classList.remove(s)};const X=(e,t)=>{if(!e||e.destroyed||!e.params)return;const s=t.closest(e.isElement?"swiper-slide":`.${e.params.slideClass}`);if(s){let t=s.querySelector(`.${e.params.lazyPreloaderClass}`);!t&&e.isElement&&(s.shadowRoot?t=s.shadowRoot.querySelector(`.${e.params.lazyPreloaderClass}`):requestAnimationFrame(()=>{s.shadowRoot&&(t=s.shadowRoot.querySelector(`.${e.params.lazyPreloaderClass}`),t&&t.remove())})),t&&t.remove()}},B=(e,t)=>{if(!e.slides[t])return;const s=e.slides[t].querySelector('[loading="lazy"]');s&&s.removeAttribute("loading")},Y=e=>{if(!e||e.destroyed||!e.params)return;let t=e.params.lazyPreloadPrevNext;const s=e.slides.length;if(!s||!t||t<0)return;t=Math.min(t,s);const a="auto"===e.params.slidesPerView?e.slidesPerViewDynamic():Math.ceil(e.params.slidesPerView),i=e.activeIndex;if(e.params.grid&&e.params.grid.rows>1){const s=i,r=[s-t];return r.push(...Array.from({length:t}).map((e,t)=>s+a+t)),void e.slides.forEach((t,s)=>{r.includes(t.column)&&B(e,s)})}const r=i+a-1;if(e.params.rewind||e.params.loop)for(let a=i-t;a<=r+t;a+=1){const t=(a%s+s)%s;(tr)&&B(e,t)}else for(let a=Math.max(i-t,0);a<=Math.min(r+t,s-1);a+=1)a!==i&&(a>r||a=0?x=parseFloat(x.replace("%",""))/100*E:"string"==typeof x&&(x=parseFloat(x)),e.virtualSize=-x-f-v,d.forEach(e=>{r?e.style.marginLeft="":e.style.marginRight="",e.style.marginBottom="",e.style.marginTop=""}),s.centeredSlides&&s.cssMode&&(m(a,"--swiper-centered-offset-before",""),m(a,"--swiper-centered-offset-after",""));const P=s.grid&&s.grid.rows>1&&e.grid;let L;P?e.grid.initSlides(d):e.grid&&e.grid.unsetSlides();const I="auto"===s.slidesPerView&&s.breakpoints&&Object.keys(s.breakpoints).filter(e=>void 0!==s.breakpoints[e].slidesPerView).length>0;for(let a=0;a1&&p.push(e.virtualSize-E)}if(l&&s.loop){const t=h[0]+x;if(s.slidesPerGroup>1){const a=Math.ceil((e.virtual.slidesBefore+e.virtual.slidesAfter)/s.slidesPerGroup),i=t*s.slidesPerGroup;for(let e=0;e!(s.cssMode&&!s.loop)||t!==d.length-1).forEach(e=>{e.style[t]=`${x}px`})}if(s.centeredSlides&&s.centeredSlidesBounds){let e=0;h.forEach(t=>{e+=t+(x||0)}),e-=x;const t=e>E?e-E:0;p=p.map(e=>e<=0?-f:e>t?t+v:e)}if(s.centerInsufficientSlides){let e=0;h.forEach(t=>{e+=t+(x||0)}),e-=x;const t=(f||0)+(v||0);if(e+t{p[t]=e-s}),u.forEach((e,t)=>{u[t]=e+s})}}if(Object.assign(e,{slides:d,snapGrid:p,slidesGrid:u,slidesSizesGrid:h}),s.centeredSlides&&s.cssMode&&!s.centeredSlidesBounds){m(a,"--swiper-centered-offset-before",-p[0]+"px"),m(a,"--swiper-centered-offset-after",e.size/2-h[h.length-1]/2+"px");const t=-e.snapGrid[0],s=-e.slidesGrid[0];e.snapGrid=e.snapGrid.map(e=>e+t),e.slidesGrid=e.slidesGrid.map(e=>e+s)}if(c!==o&&e.emit("slidesLengthChange"),p.length!==w&&(e.params.watchOverflow&&e.checkOverflow(),e.emit("snapGridLengthChange")),u.length!==b&&e.emit("slidesGridLengthChange"),s.watchSlidesProgress&&e.updateSlidesOffset(),e.emit("slidesUpdated"),!(l||s.cssMode||"slide"!==s.effect&&"fade"!==s.effect)){const t=`${s.containerModifierClass}backface-hidden`,a=e.el.classList.contains(t);c<=s.maxBackfaceHiddenSlides?a||e.el.classList.add(t):a&&e.el.classList.remove(t)}},updateAutoHeight:function(e){const t=this,s=[],a=t.virtual&&t.params.virtual.enabled;let i,r=0;"number"==typeof e?t.setTransition(e):!0===e&&t.setTransition(t.params.speed);const n=e=>a?t.slides[t.getSlideIndexByData(e)]:t.slides[e];if("auto"!==t.params.slidesPerView&&t.params.slidesPerView>1)if(t.params.centeredSlides)(t.visibleSlides||[]).forEach(e=>{s.push(e)});else for(i=0;it.slides.length&&!a)break;s.push(n(e))}else s.push(n(t.activeIndex));for(i=0;ir?e:r}(r||0===r)&&(t.wrapperEl.style.height=`${r}px`)},updateSlidesOffset:function(){const e=this,t=e.slides,s=e.isElement?e.isHorizontal()?e.wrapperEl.offsetLeft:e.wrapperEl.offsetTop:0;for(let a=0;a=0?l=parseFloat(l.replace("%",""))/100*t.size:"string"==typeof l&&(l=parseFloat(l));for(let e=0;e=0&&u<=t.size-t.slidesSizesGrid[e],f=u>=0&&u1&&m<=t.size||u<=0&&m>=t.size;f&&(t.visibleSlides.push(o),t.visibleSlidesIndexes.push(e)),D(o,f,s.slideVisibleClass),D(o,h,s.slideFullyVisibleClass),o.progress=i?-c:c,o.originalProgress=i?-p:p}},updateProgress:function(e){const t=this;if(void 0===e){const s=t.rtlTranslate?-1:1;e=t&&t.translate&&t.translate*s||0}const s=t.params,a=t.maxTranslate()-t.minTranslate();let{progress:i,isBeginning:r,isEnd:n,progressLoop:l}=t;const o=r,d=n;if(0===a)i=0,r=!0,n=!0;else{i=(e-t.minTranslate())/a;const s=Math.abs(e-t.minTranslate())<1,l=Math.abs(e-t.maxTranslate())<1;r=s||i<=0,n=l||i>=1,s&&(i=0),l&&(i=1)}if(s.loop){const s=t.getSlideIndexByData(0),a=t.getSlideIndexByData(t.slides.length-1),i=t.slidesGrid[s],r=t.slidesGrid[a],n=t.slidesGrid[t.slidesGrid.length-1],o=Math.abs(e);l=o>=i?(o-i)/n:(o+n-r)/n,l>1&&(l-=1)}Object.assign(t,{progress:i,progressLoop:l,isBeginning:r,isEnd:n}),(s.watchSlidesProgress||s.centeredSlides&&s.autoHeight)&&t.updateSlidesProgress(e),r&&!o&&t.emit("reachBeginning toEdge"),n&&!d&&t.emit("reachEnd toEdge"),(o&&!r||d&&!n)&&t.emit("fromEdge"),t.emit("progress",i)},updateSlidesClasses:function(){const e=this,{slides:t,params:s,slidesEl:a,activeIndex:i}=e,r=e.virtual&&s.virtual.enabled,n=e.grid&&s.grid&&s.grid.rows>1,l=e=>g(a,`.${s.slideClass}${e}, swiper-slide${e}`)[0];let o,d,c;if(r)if(s.loop){let t=i-e.virtual.slidesBefore;t<0&&(t=e.virtual.slides.length+t),t>=e.virtual.slides.length&&(t-=e.virtual.slides.length),o=l(`[data-swiper-slide-index="${t}"]`)}else o=l(`[data-swiper-slide-index="${i}"]`);else n?(o=t.find(e=>e.column===i),c=t.find(e=>e.column===i+1),d=t.find(e=>e.column===i-1)):o=t[i];o&&(n||(c=function(e,t){const s=[];for(;e.nextElementSibling;){const a=e.nextElementSibling;t?a.matches(t)&&s.push(a):s.push(a),e=a}return s}(o,`.${s.slideClass}, swiper-slide`)[0],s.loop&&!c&&(c=t[0]),d=function(e,t){const s=[];for(;e.previousElementSibling;){const a=e.previousElementSibling;t?a.matches(t)&&s.push(a):s.push(a),e=a}return s}(o,`.${s.slideClass}, swiper-slide`)[0],s.loop&&0===!d&&(d=t[t.length-1]))),t.forEach(e=>{G(e,e===o,s.slideActiveClass),G(e,e===c,s.slideNextClass),G(e,e===d,s.slidePrevClass)}),e.emitSlidesClasses()},updateActiveIndex:function(e){const t=this,s=t.rtlTranslate?t.translate:-t.translate,{snapGrid:a,params:i,activeIndex:r,realIndex:n,snapIndex:l}=t;let o,d=e;const c=e=>{let s=e-t.virtual.slidesBefore;return s<0&&(s=t.virtual.slides.length+s),s>=t.virtual.slides.length&&(s-=t.virtual.slides.length),s};if(void 0===d&&(d=function(e){const{slidesGrid:t,params:s}=e,a=e.rtlTranslate?e.translate:-e.translate;let i;for(let e=0;e=t[e]&&a=t[e]&&a=t[e]&&(i=e);return s.normalizeSlideIndex&&(i<0||void 0===i)&&(i=0),i}(t)),a.indexOf(s)>=0)o=a.indexOf(s);else{const e=Math.min(i.slidesPerGroupSkip,d);o=e+Math.floor((d-e)/i.slidesPerGroup)}if(o>=a.length&&(o=a.length-1),d===r&&!t.params.loop)return void(o!==l&&(t.snapIndex=o,t.emit("snapIndexChange")));if(d===r&&t.params.loop&&t.virtual&&t.params.virtual.enabled)return void(t.realIndex=c(d));const p=t.grid&&i.grid&&i.grid.rows>1;let u;if(t.virtual&&i.virtual.enabled&&i.loop)u=c(d);else if(p){const e=t.slides.find(e=>e.column===d);let s=parseInt(e.getAttribute("data-swiper-slide-index"),10);Number.isNaN(s)&&(s=Math.max(t.slides.indexOf(e),0)),u=Math.floor(s/i.grid.rows)}else if(t.slides[d]){const e=t.slides[d].getAttribute("data-swiper-slide-index");u=e?parseInt(e,10):d}else u=d;Object.assign(t,{previousSnapIndex:l,snapIndex:o,previousRealIndex:n,realIndex:u,previousIndex:r,activeIndex:d}),t.initialized&&Y(t),t.emit("activeIndexChange"),t.emit("snapIndexChange"),(t.initialized||t.params.runCallbacksOnInit)&&(n!==u&&t.emit("realIndexChange"),t.emit("slideChange"))},updateClickedSlide:function(e,t){const s=this,a=s.params;let i=e.closest(`.${a.slideClass}, swiper-slide`);!i&&s.isElement&&t&&t.length>1&&t.includes(e)&&[...t.slice(t.indexOf(e)+1,t.length)].forEach(e=>{!i&&e.matches&&e.matches(`.${a.slideClass}, swiper-slide`)&&(i=e)});let r,n=!1;if(i)for(let e=0;eo?o:a&&er?"next":i=o.length&&(v=o.length-1);const w=-o[v];if(l.normalizeSlideIndex)for(let e=0;e=s&&t=s&&t=s&&(n=e)}if(r.initialized&&n!==p){if(!r.allowSlideNext&&(u?w>r.translate&&w>r.minTranslate():wr.translate&&w>r.maxTranslate()&&(p||0)!==n)return!1}let b;n!==(c||0)&&s&&r.emit("beforeSlideChangeStart"),r.updateProgress(w),b=n>p?"next":n0?(r._cssModeVirtualInitialSet=!0,requestAnimationFrame(()=>{m[e?"scrollLeft":"scrollTop"]=s})):m[e?"scrollLeft":"scrollTop"]=s,y&&requestAnimationFrame(()=>{r.wrapperEl.style.scrollSnapType="",r._immediateVirtual=!1});else{if(!r.support.smoothScroll)return h({swiper:r,targetPosition:s,side:e?"left":"top"}),!0;m.scrollTo({[e?"left":"top"]:s,behavior:"smooth"})}return!0}const E=k().isSafari;return y&&!i&&E&&r.isElement&&r.virtual.update(!1,!1,n),r.setTransition(t),r.setTranslate(w),r.updateActiveIndex(n),r.updateSlidesClasses(),r.emit("beforeTransitionStart",t,a),r.transitionStart(s,b),0===t?r.transitionEnd(s,b):r.animating||(r.animating=!0,r.onSlideToWrapperTransitionEnd||(r.onSlideToWrapperTransitionEnd=function(e){r&&!r.destroyed&&e.target===this&&(r.wrapperEl.removeEventListener("transitionend",r.onSlideToWrapperTransitionEnd),r.onSlideToWrapperTransitionEnd=null,delete r.onSlideToWrapperTransitionEnd,r.transitionEnd(s,b))}),r.wrapperEl.addEventListener("transitionend",r.onSlideToWrapperTransitionEnd)),!0},slideToLoop:function(e=0,t,s=!0,a){if("string"==typeof e){e=parseInt(e,10)}const i=this;if(i.destroyed)return;void 0===t&&(t=i.params.speed);const r=i.grid&&i.params.grid&&i.params.grid.rows>1;let n=e;if(i.params.loop)if(i.virtual&&i.params.virtual.enabled)n+=i.virtual.slidesBefore;else{let e;if(r){const t=n*i.params.grid.rows;e=i.slides.find(e=>1*e.getAttribute("data-swiper-slide-index")===t).column}else e=i.getSlideIndexByData(n);const t=r?Math.ceil(i.slides.length/i.params.grid.rows):i.slides.length,{centeredSlides:s,slidesOffsetBefore:l,slidesOffsetAfter:o}=i.params,d=s||!!l||!!o;let c=i.params.slidesPerView;"auto"===c?c=i.slidesPerViewDynamic():(c=Math.ceil(parseFloat(i.params.slidesPerView,10)),d&&c%2==0&&(c+=1));let p=t-e1*t.getAttribute("data-swiper-slide-index")===e).column}else n=i.getSlideIndexByData(n)}return requestAnimationFrame(()=>{i.slideTo(n,t,s,a)}),i},slideNext:function(e,t=!0,s){const a=this,{enabled:i,params:r,animating:n}=a;if(!i||a.destroyed)return a;void 0===e&&(e=a.params.speed);let l=r.slidesPerGroup;"auto"===r.slidesPerView&&1===r.slidesPerGroup&&r.slidesPerGroupAuto&&(l=Math.max(a.slidesPerViewDynamic("current",!0),1));const o=a.activeIndex{a.slideTo(a.activeIndex+o,e,t,s)}),!0}return r.rewind&&a.isEnd?a.slideTo(0,e,t,s):a.slideTo(a.activeIndex+o,e,t,s)},slidePrev:function(e,t=!0,s){const a=this,{params:i,snapGrid:r,slidesGrid:n,rtlTranslate:l,enabled:o,animating:d}=a;if(!o||a.destroyed)return a;void 0===e&&(e=a.params.speed);const c=a.virtual&&i.virtual.enabled;if(i.loop){if(d&&!c&&i.loopPreventsSliding)return!1;a.loopFix({direction:"prev"}),a._clientLeft=a.wrapperEl.clientLeft}function p(e){return e<0?-Math.floor(Math.abs(e)):Math.floor(e)}const u=p(l?a.translate:-a.translate),m=r.map(e=>p(e)),h=i.freeMode&&i.freeMode.enabled;let f=r[m.indexOf(u)-1];if(void 0===f&&(i.cssMode||h)){let e;r.forEach((t,s)=>{u>=t&&(e=s)}),void 0!==e&&(f=h?r[e]:r[e>0?e-1:e])}let g=0;if(void 0!==f&&(g=n.indexOf(f),g<0&&(g=a.activeIndex-1),"auto"===i.slidesPerView&&1===i.slidesPerGroup&&i.slidesPerGroupAuto&&(g=g-a.slidesPerViewDynamic("previous",!0)+1,g=Math.max(g,0))),i.rewind&&a.isBeginning){const i=a.params.virtual&&a.params.virtual.enabled&&a.virtual?a.virtual.slides.length-1:a.slides.length-1;return a.slideTo(i,e,t,s)}return i.loop&&0===a.activeIndex&&i.cssMode?(requestAnimationFrame(()=>{a.slideTo(g,e,t,s)}),!0):a.slideTo(g,e,t,s)},slideReset:function(e,t=!0,s){const a=this;if(!a.destroyed)return void 0===e&&(e=a.params.speed),a.slideTo(a.activeIndex,e,t,s)},slideToClosest:function(e,t=!0,s,a=.5){const i=this;if(i.destroyed)return;void 0===e&&(e=i.params.speed);let r=i.activeIndex;const n=Math.min(i.params.slidesPerGroupSkip,r),l=n+Math.floor((r-n)/i.params.slidesPerGroup),o=i.rtlTranslate?i.translate:-i.translate;if(o>=i.snapGrid[l]){const e=i.snapGrid[l];o-e>(i.snapGrid[l+1]-e)*a&&(r+=i.params.slidesPerGroup)}else{const e=i.snapGrid[l-1];o-e<=(i.snapGrid[l]-e)*a&&(r-=i.params.slidesPerGroup)}return r=Math.max(r,0),r=Math.min(r,i.slidesGrid.length-1),i.slideTo(r,e,t,s)},slideToClickedSlide:function(){const e=this;if(e.destroyed)return;const{params:t,slidesEl:s}=e,a="auto"===t.slidesPerView?e.slidesPerViewDynamic():t.slidesPerView;let i,r=e.getSlideIndexWhenGrid(e.clickedIndex);const n=e.isElement?"swiper-slide":`.${t.slideClass}`,o=e.grid&&e.params.grid&&e.params.grid.rows>1;if(t.loop){if(e.animating)return;i=parseInt(e.clickedSlide.getAttribute("data-swiper-slide-index"),10),t.centeredSlides?e.slideToLoop(i):r>(o?(e.slides.length-a)/2-(e.params.grid.rows-1):e.slides.length-a)?(e.loopFix(),r=e.getSlideIndex(g(s,`${n}[data-swiper-slide-index="${i}"]`)[0]),l(()=>{e.slideTo(r)})):e.slideTo(r)}else e.slideTo(r)}};var q={loopCreate:function(e,t){const s=this,{params:a,slidesEl:i}=s;if(!a.loop||s.virtual&&s.params.virtual.enabled)return;const r=()=>{g(i,`.${a.slideClass}, swiper-slide`).forEach((e,t)=>{e.setAttribute("data-swiper-slide-index",t)})},n=s.grid&&a.grid&&a.grid.rows>1;a.loopAddBlankSlides&&(a.slidesPerGroup>1||n)&&(()=>{const e=g(i,`.${a.slideBlankClass}`);e.forEach(e=>{e.remove()}),e.length>0&&(s.recalcSlides(),s.updateSlides())})();const l=a.slidesPerGroup*(n?a.grid.rows:1),o=s.slides.length%l!==0,d=n&&s.slides.length%a.grid.rows!==0,c=e=>{for(let t=0;t1;d.lengthe.classList.contains(m.slideActiveClass))):L=i;const I="next"===s||!s,z="prev"===s||!s;let A=0,$=0;const k=(S?d[i].column:i)+(b&&void 0===a?-y/2+.5:0);if(k=0;t-=1)d[t].column===e&&T.push(t)}else T.push(C-t-1)}}else if(k+y>C-x){$=Math.max(k-(C-2*x),E),P&&($=Math.max($,y-C+w+1));for(let e=0;e<$;e+=1){const t=e-Math.floor(e/C)*C;S?d.forEach((e,s)=>{e.column===t&&M.push(s)}):M.push(t)}}if(o.__preventObserver__=!0,requestAnimationFrame(()=>{o.__preventObserver__=!1}),"cards"===o.params.effect&&d.length{d[e].swiperLoopMoveDOM=!0,u.prepend(d[e]),d[e].swiperLoopMoveDOM=!1}),I&&M.forEach(e=>{d[e].swiperLoopMoveDOM=!0,u.append(d[e]),d[e].swiperLoopMoveDOM=!1}),o.recalcSlides(),"auto"===m.slidesPerView?o.updateSlides():S&&(T.length>0&&z||M.length>0&&I)&&o.slides.forEach((e,t)=>{o.grid.updateSlide(t,e,o.slides)}),m.watchSlidesProgress&&o.updateSlidesOffset(),t)if(T.length>0&&z){if(void 0===e){const e=o.slidesGrid[L],t=o.slidesGrid[L+A]-e;l?o.setTranslate(o.translate-t):(o.slideTo(L+Math.ceil(A),0,!1,!0),a&&(o.touchEventsData.startTranslate=o.touchEventsData.startTranslate-t,o.touchEventsData.currentTranslate=o.touchEventsData.currentTranslate-t))}else if(a){const e=S?T.length/m.grid.rows:T.length;o.slideTo(o.activeIndex+e,0,!1,!0),o.touchEventsData.currentTranslate=o.translate}}else if(M.length>0&&I)if(void 0===e){const e=o.slidesGrid[L],t=o.slidesGrid[L-$]-e;l?o.setTranslate(o.translate-t):(o.slideTo(L-$,0,!1,!0),a&&(o.touchEventsData.startTranslate=o.touchEventsData.startTranslate-t,o.touchEventsData.currentTranslate=o.touchEventsData.currentTranslate-t))}else{const e=S?M.length/m.grid.rows:M.length;o.slideTo(o.activeIndex-e,0,!1,!0)}if(o.allowSlidePrev=c,o.allowSlideNext=p,o.controller&&o.controller.control&&!n){const r={slideRealIndex:e,direction:s,setTranslate:a,activeSlideIndex:i,byController:!0};Array.isArray(o.controller.control)?o.controller.control.forEach(e=>{!e.destroyed&&e.params.loop&&e.loopFix({...r,slideTo:e.params.slidesPerView===m.slidesPerView&&t})}):o.controller.control instanceof o.constructor&&o.controller.control.params.loop&&o.controller.control.loopFix({...r,slideTo:o.controller.control.params.slidesPerView===m.slidesPerView&&t})}o.emit("loopFix")},loopDestroy:function(){const e=this,{params:t,slidesEl:s}=e;if(!t.loop||!s||e.virtual&&e.params.virtual.enabled)return;e.recalcSlides();const a=[];e.slides.forEach(e=>{const t=void 0===e.swiperSlideIndex?1*e.getAttribute("data-swiper-slide-index"):e.swiperSlideIndex;a[t]=e}),e.slides.forEach(e=>{e.removeAttribute("data-swiper-slide-index")}),a.forEach(e=>{s.append(e)}),e.recalcSlides(),e.slideTo(e.realIndex,0)}};function _(e,t,s){const a=r(),{params:i}=e,n=i.edgeSwipeDetection,l=i.edgeSwipeThreshold;return!n||!(s<=l||s>=a.innerWidth-l)||"prevent"===n&&(t.preventDefault(),!0)}function F(e){const t=this,s=a();let i=e;i.originalEvent&&(i=i.originalEvent);const n=t.touchEventsData;if("pointerdown"===i.type){if(null!==n.pointerId&&n.pointerId!==i.pointerId)return;n.pointerId=i.pointerId}else"touchstart"===i.type&&1===i.targetTouches.length&&(n.touchId=i.targetTouches[0].identifier);if("touchstart"===i.type)return void _(t,i,i.targetTouches[0].pageX);const{params:l,touches:d,enabled:c}=t;if(!c)return;if(!l.simulateTouch&&"mouse"===i.pointerType)return;if(t.animating&&l.preventInteractionOnTransition)return;!t.animating&&l.cssMode&&l.loop&&t.loopFix();let p=i.target;if("wrapper"===l.touchEventsTarget&&!function(e,t){const s=r();let a=t.contains(e);!a&&s.HTMLSlotElement&&t instanceof HTMLSlotElement&&(a=[...t.assignedElements()].includes(e),a||(a=function(e,t){const s=[t];for(;s.length>0;){const t=s.shift();if(e===t)return!0;s.push(...t.children,...t.shadowRoot?t.shadowRoot.children:[],...t.assignedElements?t.assignedElements():[])}}(e,t)));return a}(p,t.wrapperEl))return;if("which"in i&&3===i.which)return;if("button"in i&&i.button>0)return;if(n.isTouched&&n.isMoved)return;const u=!!l.noSwipingClass&&""!==l.noSwipingClass,m=i.composedPath?i.composedPath():i.path;u&&i.target&&i.target.shadowRoot&&m&&(p=m[0]);const h=l.noSwipingSelector?l.noSwipingSelector:`.${l.noSwipingClass}`,f=!(!i.target||!i.target.shadowRoot);if(l.noSwiping&&(f?function(e,t=this){return function t(s){if(!s||s===a()||s===r())return null;s.assignedSlot&&(s=s.assignedSlot);const i=s.closest(e);return i||s.getRootNode?i||t(s.getRootNode().host):null}(t)}(h,p):p.closest(h)))return void(t.allowClick=!0);if(l.swipeHandler&&!p.closest(l.swipeHandler))return;d.currentX=i.pageX,d.currentY=i.pageY;const g=d.currentX,v=d.currentY;if(!_(t,i,g))return;Object.assign(n,{isTouched:!0,isMoved:!1,allowTouchCallbacks:!0,isScrolling:void 0,startMoving:void 0}),d.startX=g,d.startY=v,n.touchStartTime=o(),t.allowClick=!0,t.updateSize(),t.swipeDirection=void 0,l.threshold>0&&(n.allowThresholdMove=!1);let w=!0;p.matches(n.focusableElements)&&(w=!1,"SELECT"===p.nodeName&&(n.isTouched=!1)),s.activeElement&&s.activeElement.matches(n.focusableElements)&&s.activeElement!==p&&("mouse"===i.pointerType||"mouse"!==i.pointerType&&!p.matches(n.focusableElements))&&s.activeElement.blur();const b=w&&t.allowTouchMove&&l.touchStartPreventDefault;!l.touchStartForcePreventDefault&&!b||p.isContentEditable||i.preventDefault(),l.freeMode&&l.freeMode.enabled&&t.freeMode&&t.animating&&!l.cssMode&&t.freeMode.onTouchStart(),t.emit("touchStart",i)}function W(e){const t=a(),s=this,i=s.touchEventsData,{params:r,touches:n,rtlTranslate:l,enabled:d}=s;if(!d)return;if(!r.simulateTouch&&"mouse"===e.pointerType)return;let c,p=e;if(p.originalEvent&&(p=p.originalEvent),"pointermove"===p.type){if(null!==i.touchId)return;if(p.pointerId!==i.pointerId)return}if("touchmove"===p.type){if(c=[...p.changedTouches].find(e=>e.identifier===i.touchId),!c||c.identifier!==i.touchId)return}else c=p;if(!i.isTouched)return void(i.startMoving&&i.isScrolling&&s.emit("touchMoveOpposite",p));const u=c.pageX,m=c.pageY;if(p.preventedByNestedSwiper)return n.startX=u,void(n.startY=m);if(!s.allowTouchMove)return p.target.matches(i.focusableElements)||(s.allowClick=!1),void(i.isTouched&&(Object.assign(n,{startX:u,startY:m,currentX:u,currentY:m}),i.touchStartTime=o()));if(r.touchReleaseOnEdges&&!r.loop)if(s.isVertical()){if(mn.startY&&s.translate>=s.minTranslate())return i.isTouched=!1,void(i.isMoved=!1)}else{if(l&&(u>n.startX&&-s.translate<=s.maxTranslate()||u=s.minTranslate()))return;if(!l&&(un.startX&&s.translate>=s.minTranslate()))return}if(t.activeElement&&t.activeElement.matches(i.focusableElements)&&t.activeElement!==p.target&&"mouse"!==p.pointerType&&t.activeElement.blur(),t.activeElement&&p.target===t.activeElement&&p.target.matches(i.focusableElements))return i.isMoved=!0,void(s.allowClick=!1);i.allowTouchCallbacks&&s.emit("touchMove",p),n.previousX=n.currentX,n.previousY=n.currentY,n.currentX=u,n.currentY=m;const h=n.currentX-n.startX,f=n.currentY-n.startY;if(s.params.threshold&&Math.sqrt(h**2+f**2)=25&&(e=180*Math.atan2(Math.abs(f),Math.abs(h))/Math.PI,i.isScrolling=s.isHorizontal()?e>r.touchAngle:90-e>r.touchAngle)}if(i.isScrolling&&s.emit("touchMoveOpposite",p),void 0===i.startMoving&&(n.currentX===n.startX&&n.currentY===n.startY||(i.startMoving=!0)),i.isScrolling||"touchmove"===p.type&&i.preventTouchMoveFromPointerMove)return void(i.isTouched=!1);if(!i.startMoving)return;s.allowClick=!1,!r.cssMode&&p.cancelable&&p.preventDefault(),r.touchMoveStopPropagation&&!r.nested&&p.stopPropagation();let g=s.isHorizontal()?h:f,v=s.isHorizontal()?n.currentX-n.previousX:n.currentY-n.previousY;r.oneWayMovement&&(g=Math.abs(g)*(l?1:-1),v=Math.abs(v)*(l?1:-1)),n.diff=g,g*=r.touchRatio,l&&(g=-g,v=-v);const w=s.touchesDirection;s.swipeDirection=g>0?"prev":"next",s.touchesDirection=v>0?"prev":"next";const b=s.params.loop&&!r.cssMode,y="next"===s.touchesDirection&&s.allowSlideNext||"prev"===s.touchesDirection&&s.allowSlidePrev;if(!i.isMoved){if(b&&y&&s.loopFix({direction:s.swipeDirection}),i.startTranslate=s.getTranslate(),s.setTransition(0),s.animating){const e=new window.CustomEvent("transitionend",{bubbles:!0,cancelable:!0,detail:{bySwiperTouchMove:!0}});s.wrapperEl.dispatchEvent(e)}i.allowMomentumBounce=!1,!r.grabCursor||!0!==s.allowSlideNext&&!0!==s.allowSlidePrev||s.setGrabCursor(!0),s.emit("sliderFirstMove",p)}if((new Date).getTime(),!1!==r._loopSwapReset&&i.isMoved&&i.allowThresholdMove&&w!==s.touchesDirection&&b&&y&&Math.abs(g)>=1)return Object.assign(n,{startX:u,startY:m,currentX:u,currentY:m,startTranslate:i.currentTranslate}),i.loopSwapReset=!0,void(i.startTranslate=i.currentTranslate);s.emit("sliderMove",p),i.isMoved=!0,i.currentTranslate=g+i.startTranslate;let E=!0,x=r.resistanceRatio;if(r.touchReleaseOnEdges&&(x=0),g>0?(b&&y&&i.allowThresholdMove&&i.currentTranslate>(r.centeredSlides?s.minTranslate()-s.slidesSizesGrid[s.activeIndex+1]-("auto"!==r.slidesPerView&&s.slides.length-r.slidesPerView>=2?s.slidesSizesGrid[s.activeIndex+1]+s.params.spaceBetween:0)-s.params.spaceBetween:s.minTranslate())&&s.loopFix({direction:"prev",setTranslate:!0,activeSlideIndex:0}),i.currentTranslate>s.minTranslate()&&(E=!1,r.resistance&&(i.currentTranslate=s.minTranslate()-1+(-s.minTranslate()+i.startTranslate+g)**x))):g<0&&(b&&y&&i.allowThresholdMove&&i.currentTranslate<(r.centeredSlides?s.maxTranslate()+s.slidesSizesGrid[s.slidesSizesGrid.length-1]+s.params.spaceBetween+("auto"!==r.slidesPerView&&s.slides.length-r.slidesPerView>=2?s.slidesSizesGrid[s.slidesSizesGrid.length-1]+s.params.spaceBetween:0):s.maxTranslate())&&s.loopFix({direction:"next",setTranslate:!0,activeSlideIndex:s.slides.length-("auto"===r.slidesPerView?s.slidesPerViewDynamic():Math.ceil(parseFloat(r.slidesPerView,10)))}),i.currentTranslatei.startTranslate&&(i.currentTranslate=i.startTranslate),s.allowSlidePrev||s.allowSlideNext||(i.currentTranslate=i.startTranslate),r.threshold>0){if(!(Math.abs(g)>r.threshold||i.allowThresholdMove))return void(i.currentTranslate=i.startTranslate);if(!i.allowThresholdMove)return i.allowThresholdMove=!0,n.startX=n.currentX,n.startY=n.currentY,i.currentTranslate=i.startTranslate,void(n.diff=s.isHorizontal()?n.currentX-n.startX:n.currentY-n.startY)}r.followFinger&&!r.cssMode&&((r.freeMode&&r.freeMode.enabled&&s.freeMode||r.watchSlidesProgress)&&(s.updateActiveIndex(),s.updateSlidesClasses()),r.freeMode&&r.freeMode.enabled&&s.freeMode&&s.freeMode.onTouchMove(),s.updateProgress(i.currentTranslate),s.setTranslate(i.currentTranslate))}function j(e){const t=this,s=t.touchEventsData;let a,i=e;i.originalEvent&&(i=i.originalEvent);if("touchend"===i.type||"touchcancel"===i.type){if(a=[...i.changedTouches].find(e=>e.identifier===s.touchId),!a||a.identifier!==s.touchId)return}else{if(null!==s.touchId)return;if(i.pointerId!==s.pointerId)return;a=i}if(["pointercancel","pointerout","pointerleave","contextmenu"].includes(i.type)){if(!(["pointercancel","contextmenu"].includes(i.type)&&(t.browser.isSafari||t.browser.isWebView)))return}s.pointerId=null,s.touchId=null;const{params:r,touches:n,rtlTranslate:d,slidesGrid:c,enabled:p}=t;if(!p)return;if(!r.simulateTouch&&"mouse"===i.pointerType)return;if(s.allowTouchCallbacks&&t.emit("touchEnd",i),s.allowTouchCallbacks=!1,!s.isTouched)return s.isMoved&&r.grabCursor&&t.setGrabCursor(!1),s.isMoved=!1,void(s.startMoving=!1);r.grabCursor&&s.isMoved&&s.isTouched&&(!0===t.allowSlideNext||!0===t.allowSlidePrev)&&t.setGrabCursor(!1);const u=o(),m=u-s.touchStartTime;if(t.allowClick){const e=i.path||i.composedPath&&i.composedPath();t.updateClickedSlide(e&&e[0]||i.target,e),t.emit("tap click",i),m<300&&u-s.lastClickTime<300&&t.emit("doubleTap doubleClick",i)}if(s.lastClickTime=o(),l(()=>{t.destroyed||(t.allowClick=!0)}),!s.isTouched||!s.isMoved||!t.swipeDirection||0===n.diff&&!s.loopSwapReset||s.currentTranslate===s.startTranslate&&!s.loopSwapReset)return s.isTouched=!1,s.isMoved=!1,void(s.startMoving=!1);let h;if(s.isTouched=!1,s.isMoved=!1,s.startMoving=!1,h=r.followFinger?d?t.translate:-t.translate:-s.currentTranslate,r.cssMode)return;if(r.freeMode&&r.freeMode.enabled)return void t.freeMode.onTouchEnd({currentPos:h});const f=h>=-t.maxTranslate()&&!t.params.loop;let g=0,v=t.slidesSizesGrid[0];for(let e=0;e=c[e]&&h=c[e])&&(g=e,v=c[c.length-1]-c[c.length-2])}let w=null,b=null;r.rewind&&(t.isBeginning?b=r.virtual&&r.virtual.enabled&&t.virtual?t.virtual.slides.length-1:t.slides.length-1:t.isEnd&&(w=0));const y=(h-c[g])/v,E=gr.longSwipesMs){if(!r.longSwipes)return void t.slideTo(t.activeIndex);"next"===t.swipeDirection&&(y>=r.longSwipesRatio?t.slideTo(r.rewind&&t.isEnd?w:g+E):t.slideTo(g)),"prev"===t.swipeDirection&&(y>1-r.longSwipesRatio?t.slideTo(g+E):null!==b&&y<0&&Math.abs(y)>r.longSwipesRatio?t.slideTo(b):t.slideTo(g))}else{if(!r.shortSwipes)return void t.slideTo(t.activeIndex);t.navigation&&(i.target===t.navigation.nextEl||i.target===t.navigation.prevEl)?i.target===t.navigation.nextEl?t.slideTo(g+E):t.slideTo(g):("next"===t.swipeDirection&&t.slideTo(null!==w?w:g+E),"prev"===t.swipeDirection&&t.slideTo(null!==b?b:g))}}function U(){const e=this,{params:t,el:s}=e;if(s&&0===s.offsetWidth)return;t.breakpoints&&e.setBreakpoint();const{allowSlideNext:a,allowSlidePrev:i,snapGrid:r}=e,n=e.virtual&&e.params.virtual.enabled;e.allowSlideNext=!0,e.allowSlidePrev=!0,e.updateSize(),e.updateSlides(),e.updateSlidesClasses();const l=n&&t.loop;!("auto"===t.slidesPerView||t.slidesPerView>1)||!e.isEnd||e.isBeginning||e.params.centeredSlides||l?e.params.loop&&!n?e.slideToLoop(e.realIndex,0,!1,!0):e.slideTo(e.activeIndex,0,!1,!0):e.slideTo(e.slides.length-1,0,!1,!0),e.autoplay&&e.autoplay.running&&e.autoplay.paused&&(clearTimeout(e.autoplay.resizeTimeout),e.autoplay.resizeTimeout=setTimeout(()=>{e.autoplay&&e.autoplay.running&&e.autoplay.paused&&e.autoplay.resume()},500)),e.allowSlidePrev=i,e.allowSlideNext=a,e.params.watchOverflow&&r!==e.snapGrid&&e.checkOverflow()}function K(e){const t=this;t.enabled&&(t.allowClick||(t.params.preventClicks&&e.preventDefault(),t.params.preventClicksPropagation&&t.animating&&(e.stopPropagation(),e.stopImmediatePropagation())))}function Z(){const e=this,{wrapperEl:t,rtlTranslate:s,enabled:a}=e;if(!a)return;let i;e.previousTranslate=e.translate,e.isHorizontal()?e.translate=-t.scrollLeft:e.translate=-t.scrollTop,0===e.translate&&(e.translate=0),e.updateActiveIndex(),e.updateSlidesClasses();const r=e.maxTranslate()-e.minTranslate();i=0===r?0:(e.translate-e.minTranslate())/r,i!==e.progress&&e.updateProgress(s?-e.translate:e.translate),e.emit("setTranslate",e.translate,!1)}function Q(e){const t=this;X(t,e.target),t.params.cssMode||"auto"!==t.params.slidesPerView&&!t.params.autoHeight||t.update()}function J(){const e=this;e.documentTouchHandlerProceeded||(e.documentTouchHandlerProceeded=!0,e.params.touchReleaseOnEdges&&(e.el.style.touchAction="auto"))}const ee=(e,t)=>{const s=a(),{params:i,el:r,wrapperEl:n,device:l}=e,o=!!i.nested,d="on"===t?"addEventListener":"removeEventListener",c=t;r&&"string"!=typeof r&&(s[d]("touchstart",e.onDocumentTouchStart,{passive:!1,capture:o}),r[d]("touchstart",e.onTouchStart,{passive:!1}),r[d]("pointerdown",e.onTouchStart,{passive:!1}),s[d]("touchmove",e.onTouchMove,{passive:!1,capture:o}),s[d]("pointermove",e.onTouchMove,{passive:!1,capture:o}),s[d]("touchend",e.onTouchEnd,{passive:!0}),s[d]("pointerup",e.onTouchEnd,{passive:!0}),s[d]("pointercancel",e.onTouchEnd,{passive:!0}),s[d]("touchcancel",e.onTouchEnd,{passive:!0}),s[d]("pointerout",e.onTouchEnd,{passive:!0}),s[d]("pointerleave",e.onTouchEnd,{passive:!0}),s[d]("contextmenu",e.onTouchEnd,{passive:!0}),(i.preventClicks||i.preventClicksPropagation)&&r[d]("click",e.onClick,!0),i.cssMode&&n[d]("scroll",e.onScroll),i.updateOnWindowResize?e[c](l.ios||l.android?"resize orientationchange observerUpdate":"resize observerUpdate",U,!0):e[c]("observerUpdate",U,!0),r[d]("load",e.onLoad,{capture:!0}))};const te=(e,t)=>e.grid&&t.grid&&t.grid.rows>1;var se={init:!0,direction:"horizontal",oneWayMovement:!1,swiperElementNodeName:"SWIPER-CONTAINER",touchEventsTarget:"wrapper",initialSlide:0,speed:300,cssMode:!1,updateOnWindowResize:!0,resizeObserver:!0,nested:!1,createElements:!1,eventsPrefix:"swiper",enabled:!0,focusableElements:"input, select, option, textarea, button, video, label",width:null,height:null,preventInteractionOnTransition:!1,userAgent:null,url:null,edgeSwipeDetection:!1,edgeSwipeThreshold:20,autoHeight:!1,setWrapperSize:!1,virtualTranslate:!1,effect:"slide",breakpoints:void 0,breakpointsBase:"window",spaceBetween:0,slidesPerView:1,slidesPerGroup:1,slidesPerGroupSkip:0,slidesPerGroupAuto:!1,centeredSlides:!1,centeredSlidesBounds:!1,slidesOffsetBefore:0,slidesOffsetAfter:0,normalizeSlideIndex:!0,centerInsufficientSlides:!1,watchOverflow:!0,roundLengths:!1,touchRatio:1,touchAngle:45,simulateTouch:!0,shortSwipes:!0,longSwipes:!0,longSwipesRatio:.5,longSwipesMs:300,followFinger:!0,allowTouchMove:!0,threshold:5,touchMoveStopPropagation:!1,touchStartPreventDefault:!0,touchStartForcePreventDefault:!1,touchReleaseOnEdges:!1,uniqueNavElements:!0,resistance:!0,resistanceRatio:.85,watchSlidesProgress:!1,grabCursor:!1,preventClicks:!0,preventClicksPropagation:!0,slideToClickedSlide:!1,loop:!1,loopAddBlankSlides:!0,loopAdditionalSlides:0,loopPreventsSliding:!0,rewind:!1,allowSlidePrev:!0,allowSlideNext:!0,swipeHandler:null,noSwiping:!0,noSwipingClass:"swiper-no-swiping",noSwipingSelector:null,passiveListeners:!0,maxBackfaceHiddenSlides:10,containerModifierClass:"swiper-",slideClass:"swiper-slide",slideBlankClass:"swiper-slide-blank",slideActiveClass:"swiper-slide-active",slideVisibleClass:"swiper-slide-visible",slideFullyVisibleClass:"swiper-slide-fully-visible",slideNextClass:"swiper-slide-next",slidePrevClass:"swiper-slide-prev",wrapperClass:"swiper-wrapper",lazyPreloaderClass:"swiper-lazy-preloader",lazyPreloadPrevNext:0,runCallbacksOnInit:!0,_emitClasses:!1};function ae(e,t){return function(s={}){const a=Object.keys(s)[0],i=s[a];"object"==typeof i&&null!==i?(!0===e[a]&&(e[a]={enabled:!0}),"navigation"===a&&e[a]&&e[a].enabled&&!e[a].prevEl&&!e[a].nextEl&&(e[a].auto=!0),["pagination","scrollbar"].indexOf(a)>=0&&e[a]&&e[a].enabled&&!e[a].el&&(e[a].auto=!0),a in e&&"enabled"in i?("object"!=typeof e[a]||"enabled"in e[a]||(e[a].enabled=!0),e[a]||(e[a]={enabled:!1}),u(t,s)):u(t,s)):u(t,s)}}const ie={eventsEmitter:O,update:H,translate:N,transition:{setTransition:function(e,t){const s=this;s.params.cssMode||(s.wrapperEl.style.transitionDuration=`${e}ms`,s.wrapperEl.style.transitionDelay=0===e?"0ms":""),s.emit("setTransition",e,t)},transitionStart:function(e=!0,t){const s=this,{params:a}=s;a.cssMode||(a.autoHeight&&s.updateAutoHeight(),R({swiper:s,runCallbacks:e,direction:t,step:"Start"}))},transitionEnd:function(e=!0,t){const s=this,{params:a}=s;s.animating=!1,a.cssMode||(s.setTransition(0),R({swiper:s,runCallbacks:e,direction:t,step:"End"}))}},slide:V,loop:q,grabCursor:{setGrabCursor:function(e){const t=this;if(!t.params.simulateTouch||t.params.watchOverflow&&t.isLocked||t.params.cssMode)return;const s="container"===t.params.touchEventsTarget?t.el:t.wrapperEl;t.isElement&&(t.__preventObserver__=!0),s.style.cursor="move",s.style.cursor=e?"grabbing":"grab",t.isElement&&requestAnimationFrame(()=>{t.__preventObserver__=!1})},unsetGrabCursor:function(){const e=this;e.params.watchOverflow&&e.isLocked||e.params.cssMode||(e.isElement&&(e.__preventObserver__=!0),e["container"===e.params.touchEventsTarget?"el":"wrapperEl"].style.cursor="",e.isElement&&requestAnimationFrame(()=>{e.__preventObserver__=!1}))}},events:{attachEvents:function(){const e=this,{params:t}=e;e.onTouchStart=F.bind(e),e.onTouchMove=W.bind(e),e.onTouchEnd=j.bind(e),e.onDocumentTouchStart=J.bind(e),t.cssMode&&(e.onScroll=Z.bind(e)),e.onClick=K.bind(e),e.onLoad=Q.bind(e),ee(e,"on")},detachEvents:function(){ee(this,"off")}},breakpoints:{setBreakpoint:function(){const e=this,{realIndex:t,initialized:s,params:i,el:r}=e,n=i.breakpoints;if(!n||n&&0===Object.keys(n).length)return;const l=a(),o="window"!==i.breakpointsBase&&i.breakpointsBase?"container":i.breakpointsBase,d=["window","container"].includes(i.breakpointsBase)||!i.breakpointsBase?e.el:l.querySelector(i.breakpointsBase),c=e.getBreakpoint(n,o,d);if(!c||e.currentBreakpoint===c)return;const p=(c in n?n[c]:void 0)||e.originalParams,m=te(e,i),h=te(e,p),f=e.params.grabCursor,g=p.grabCursor,v=i.enabled;m&&!h?(r.classList.remove(`${i.containerModifierClass}grid`,`${i.containerModifierClass}grid-column`),e.emitContainerClasses()):!m&&h&&(r.classList.add(`${i.containerModifierClass}grid`),(p.grid.fill&&"column"===p.grid.fill||!p.grid.fill&&"column"===i.grid.fill)&&r.classList.add(`${i.containerModifierClass}grid-column`),e.emitContainerClasses()),f&&!g?e.unsetGrabCursor():!f&&g&&e.setGrabCursor(),["navigation","pagination","scrollbar"].forEach(t=>{if(void 0===p[t])return;const s=i[t]&&i[t].enabled,a=p[t]&&p[t].enabled;s&&!a&&e[t].disable(),!s&&a&&e[t].enable()});const w=p.direction&&p.direction!==i.direction,b=i.loop&&(p.slidesPerView!==i.slidesPerView||w),y=i.loop;w&&s&&e.changeDirection(),u(e.params,p);const E=e.params.enabled,x=e.params.loop;Object.assign(e,{allowTouchMove:e.params.allowTouchMove,allowSlideNext:e.params.allowSlideNext,allowSlidePrev:e.params.allowSlidePrev}),v&&!E?e.disable():!v&&E&&e.enable(),e.currentBreakpoint=c,e.emit("_beforeBreakpoint",p),s&&(b?(e.loopDestroy(),e.loopCreate(t),e.updateSlides()):!y&&x?(e.loopCreate(t),e.updateSlides()):y&&!x&&e.loopDestroy()),e.emit("breakpoint",p)},getBreakpoint:function(e,t="window",s){if(!e||"container"===t&&!s)return;let a=!1;const i=r(),n="window"===t?i.innerHeight:s.clientHeight,l=Object.keys(e).map(e=>{if("string"==typeof e&&0===e.indexOf("@")){const t=parseFloat(e.substr(1));return{value:n*t,point:e}}return{value:e,point:e}});l.sort((e,t)=>parseInt(e.value,10)-parseInt(t.value,10));for(let e=0;es}else e.isLocked=1===e.snapGrid.length;!0===s.allowSlideNext&&(e.allowSlideNext=!e.isLocked),!0===s.allowSlidePrev&&(e.allowSlidePrev=!e.isLocked),t&&t!==e.isLocked&&(e.isEnd=!1),t!==e.isLocked&&e.emit(e.isLocked?"lock":"unlock")}},classes:{addClasses:function(){const e=this,{classNames:t,params:s,rtl:a,el:i,device:r}=e,n=function(e,t){const s=[];return e.forEach(e=>{"object"==typeof e?Object.keys(e).forEach(a=>{e[a]&&s.push(t+a)}):"string"==typeof e&&s.push(t+e)}),s}(["initialized",s.direction,{"free-mode":e.params.freeMode&&s.freeMode.enabled},{autoheight:s.autoHeight},{rtl:a},{grid:s.grid&&s.grid.rows>1},{"grid-column":s.grid&&s.grid.rows>1&&"column"===s.grid.fill},{android:r.android},{ios:r.ios},{"css-mode":s.cssMode},{centered:s.cssMode&&s.centeredSlides},{"watch-progress":s.watchSlidesProgress}],s.containerModifierClass);t.push(...n),i.classList.add(...t),e.emitContainerClasses()},removeClasses:function(){const{el:e,classNames:t}=this;e&&"string"!=typeof e&&(e.classList.remove(...t),this.emitContainerClasses())}}},re={};class ne{constructor(...e){let t,s;1===e.length&&e[0].constructor&&"Object"===Object.prototype.toString.call(e[0]).slice(8,-1)?s=e[0]:[t,s]=e,s||(s={}),s=u({},s),t&&!s.el&&(s.el=t);const i=a();if(s.el&&"string"==typeof s.el&&i.querySelectorAll(s.el).length>1){const e=[];return i.querySelectorAll(s.el).forEach(t=>{const a=u({},s,{el:t});e.push(new ne(a))}),e}const r=this;r.__swiper__=!0,r.support=A(),r.device=$({userAgent:s.userAgent}),r.browser=k(),r.eventsListeners={},r.eventsAnyListeners=[],r.modules=[...r.__modules__],s.modules&&Array.isArray(s.modules)&&r.modules.push(...s.modules);const n={};r.modules.forEach(e=>{e({params:s,swiper:r,extendParams:ae(s,n),on:r.on.bind(r),once:r.once.bind(r),off:r.off.bind(r),emit:r.emit.bind(r)})});const l=u({},se,n);return r.params=u({},l,re,s),r.originalParams=u({},r.params),r.passedParams=u({},s),r.params&&r.params.on&&Object.keys(r.params.on).forEach(e=>{r.on(e,r.params.on[e])}),r.params&&r.params.onAny&&r.onAny(r.params.onAny),Object.assign(r,{enabled:r.params.enabled,el:t,classNames:[],slides:[],slidesGrid:[],snapGrid:[],slidesSizesGrid:[],isHorizontal:()=>"horizontal"===r.params.direction,isVertical:()=>"vertical"===r.params.direction,activeIndex:0,realIndex:0,isBeginning:!0,isEnd:!1,translate:0,previousTranslate:0,progress:0,velocity:0,animating:!1,cssOverflowAdjustment(){return Math.trunc(this.translate/2**23)*2**23},allowSlideNext:r.params.allowSlideNext,allowSlidePrev:r.params.allowSlidePrev,touchEventsData:{isTouched:void 0,isMoved:void 0,allowTouchCallbacks:void 0,touchStartTime:void 0,isScrolling:void 0,currentTranslate:void 0,startTranslate:void 0,allowThresholdMove:void 0,focusableElements:r.params.focusableElements,lastClickTime:0,clickTimeout:void 0,velocities:[],allowMomentumBounce:void 0,startMoving:void 0,pointerId:null,touchId:null},allowClick:!0,allowTouchMove:r.params.allowTouchMove,touches:{startX:0,startY:0,currentX:0,currentY:0,diff:0},imagesToLoad:[],imagesLoaded:0}),r.emit("_swiper"),r.params.init&&r.init(),r}getDirectionLabel(e){return this.isHorizontal()?e:{width:"height","margin-top":"margin-left","margin-bottom ":"margin-right","margin-left":"margin-top","margin-right":"margin-bottom","padding-left":"padding-top","padding-right":"padding-bottom",marginRight:"marginBottom"}[e]}getSlideIndex(e){const{slidesEl:t,params:s}=this,a=E(g(t,`.${s.slideClass}, swiper-slide`)[0]);return E(e)-a}getSlideIndexByData(e){return this.getSlideIndex(this.slides.find(t=>1*t.getAttribute("data-swiper-slide-index")===e))}getSlideIndexWhenGrid(e){return this.grid&&this.params.grid&&this.params.grid.rows>1&&("column"===this.params.grid.fill?e=Math.floor(e/this.params.grid.rows):"row"===this.params.grid.fill&&(e%=Math.ceil(this.slides.length/this.params.grid.rows))),e}recalcSlides(){const{slidesEl:e,params:t}=this;this.slides=g(e,`.${t.slideClass}, swiper-slide`)}enable(){const e=this;e.enabled||(e.enabled=!0,e.params.grabCursor&&e.setGrabCursor(),e.emit("enable"))}disable(){const e=this;e.enabled&&(e.enabled=!1,e.params.grabCursor&&e.unsetGrabCursor(),e.emit("disable"))}setProgress(e,t){const s=this;e=Math.min(Math.max(e,0),1);const a=s.minTranslate(),i=(s.maxTranslate()-a)*e+a;s.translateTo(i,void 0===t?0:t),s.updateActiveIndex(),s.updateSlidesClasses()}emitContainerClasses(){const e=this;if(!e.params._emitClasses||!e.el)return;const t=e.el.className.split(" ").filter(t=>0===t.indexOf("swiper")||0===t.indexOf(e.params.containerModifierClass));e.emit("_containerClasses",t.join(" "))}getSlideClasses(e){const t=this;return t.destroyed?"":e.className.split(" ").filter(e=>0===e.indexOf("swiper-slide")||0===e.indexOf(t.params.slideClass)).join(" ")}emitSlidesClasses(){const e=this;if(!e.params._emitClasses||!e.el)return;const t=[];e.slides.forEach(s=>{const a=e.getSlideClasses(s);t.push({slideEl:s,classNames:a}),e.emit("_slideClass",s,a)}),e.emit("_slideClasses",t)}slidesPerViewDynamic(e="current",t=!1){const{params:s,slides:a,slidesGrid:i,slidesSizesGrid:r,size:n,activeIndex:l}=this;let o=1;if("number"==typeof s.slidesPerView)return s.slidesPerView;if(s.centeredSlides){let e,t=a[l]?Math.ceil(a[l].swiperSlideSize):0;for(let s=l+1;sn&&(e=!0));for(let s=l-1;s>=0;s-=1)a[s]&&!e&&(t+=a[s].swiperSlideSize,o+=1,t>n&&(e=!0))}else if("current"===e)for(let e=l+1;e=0;e-=1){i[l]-i[e]{t.complete&&X(e,t)}),e.updateSize(),e.updateSlides(),e.updateProgress(),e.updateSlidesClasses(),s.freeMode&&s.freeMode.enabled&&!s.cssMode)a(),s.autoHeight&&e.updateAutoHeight();else{if(("auto"===s.slidesPerView||s.slidesPerView>1)&&e.isEnd&&!s.centeredSlides){const t=e.virtual&&s.virtual.enabled?e.virtual.slides:e.slides;i=e.slideTo(t.length-1,0,!1,!0)}else i=e.slideTo(e.activeIndex,0,!1,!0);i||a()}s.watchOverflow&&t!==e.snapGrid&&e.checkOverflow(),e.emit("update")}changeDirection(e,t=!0){const s=this,a=s.params.direction;return e||(e="horizontal"===a?"vertical":"horizontal"),e===a||"horizontal"!==e&&"vertical"!==e||(s.el.classList.remove(`${s.params.containerModifierClass}${a}`),s.el.classList.add(`${s.params.containerModifierClass}${e}`),s.emitContainerClasses(),s.params.direction=e,s.slides.forEach(t=>{"vertical"===e?t.style.width="":t.style.height=""}),s.emit("changeDirection"),t&&s.update()),s}changeLanguageDirection(e){const t=this;t.rtl&&"rtl"===e||!t.rtl&&"ltr"===e||(t.rtl="rtl"===e,t.rtlTranslate="horizontal"===t.params.direction&&t.rtl,t.rtl?(t.el.classList.add(`${t.params.containerModifierClass}rtl`),t.el.dir="rtl"):(t.el.classList.remove(`${t.params.containerModifierClass}rtl`),t.el.dir="ltr"),t.update())}mount(e){const t=this;if(t.mounted)return!0;let s=e||t.params.el;if("string"==typeof s&&(s=document.querySelector(s)),!s)return!1;s.swiper=t,s.parentNode&&s.parentNode.host&&s.parentNode.host.nodeName===t.params.swiperElementNodeName.toUpperCase()&&(t.isElement=!0);const a=()=>`.${(t.params.wrapperClass||"").trim().split(" ").join(".")}`;let i=(()=>{if(s&&s.shadowRoot&&s.shadowRoot.querySelector){return s.shadowRoot.querySelector(a())}return g(s,a())[0]})();return!i&&t.params.createElements&&(i=w("div",t.params.wrapperClass),s.append(i),g(s,`.${t.params.slideClass}`).forEach(e=>{i.append(e)})),Object.assign(t,{el:s,wrapperEl:i,slidesEl:t.isElement&&!s.parentNode.host.slideSlots?s.parentNode.host:i,hostEl:t.isElement?s.parentNode.host:s,mounted:!0,rtl:"rtl"===s.dir.toLowerCase()||"rtl"===y(s,"direction"),rtlTranslate:"horizontal"===t.params.direction&&("rtl"===s.dir.toLowerCase()||"rtl"===y(s,"direction")),wrongRTL:"-webkit-box"===y(i,"display")}),!0}init(e){const t=this;if(t.initialized)return t;if(!1===t.mount(e))return t;t.emit("beforeInit"),t.params.breakpoints&&t.setBreakpoint(),t.addClasses(),t.updateSize(),t.updateSlides(),t.params.watchOverflow&&t.checkOverflow(),t.params.grabCursor&&t.enabled&&t.setGrabCursor(),t.params.loop&&t.virtual&&t.params.virtual.enabled?t.slideTo(t.params.initialSlide+t.virtual.slidesBefore,0,t.params.runCallbacksOnInit,!1,!0):t.slideTo(t.params.initialSlide,0,t.params.runCallbacksOnInit,!1,!0),t.params.loop&&t.loopCreate(void 0,!0),t.attachEvents();const s=[...t.el.querySelectorAll('[loading="lazy"]')];return t.isElement&&s.push(...t.hostEl.querySelectorAll('[loading="lazy"]')),s.forEach(e=>{e.complete?X(t,e):e.addEventListener("load",e=>{X(t,e.target)})}),Y(t),t.initialized=!0,Y(t),t.emit("init"),t.emit("afterInit"),t}destroy(e=!0,t=!0){const s=this,{params:a,el:i,wrapperEl:r,slides:n}=s;return void 0===s.params||s.destroyed||(s.emit("beforeDestroy"),s.initialized=!1,s.detachEvents(),a.loop&&s.loopDestroy(),t&&(s.removeClasses(),i&&"string"!=typeof i&&i.removeAttribute("style"),r&&r.removeAttribute("style"),n&&n.length&&n.forEach(e=>{e.classList.remove(a.slideVisibleClass,a.slideFullyVisibleClass,a.slideActiveClass,a.slideNextClass,a.slidePrevClass),e.removeAttribute("style"),e.removeAttribute("data-swiper-slide-index")})),s.emit("destroy"),Object.keys(s.eventsListeners).forEach(e=>{s.off(e)}),!1!==e&&(s.el&&"string"!=typeof s.el&&(s.el.swiper=null),function(e){const t=e;Object.keys(t).forEach(e=>{try{t[e]=null}catch(e){}try{delete t[e]}catch(e){}})}(s)),s.destroyed=!0),null}static extendDefaults(e){u(re,e)}static get extendedDefaults(){return re}static get defaults(){return se}static installModule(e){ne.prototype.__modules__||(ne.prototype.__modules__=[]);const t=ne.prototype.__modules__;"function"==typeof e&&t.indexOf(e)<0&&t.push(e)}static use(e){return Array.isArray(e)?(e.forEach(e=>ne.installModule(e)),ne):(ne.installModule(e),ne)}}function le(e,t,s,a){return e.params.createElements&&Object.keys(a).forEach(i=>{if(!s[i]&&!0===s.auto){let r=g(e.el,`.${a[i]}`)[0];r||(r=w("div",a[i]),r.className=a[i],e.el.append(r)),s[i]=r,t[i]=r}}),s}Object.keys(ie).forEach(e=>{Object.keys(ie[e]).forEach(t=>{ne.prototype[t]=ie[e][t]})}),ne.use([function({swiper:e,on:t,emit:s}){const a=r();let i=null,n=null;const l=()=>{e&&!e.destroyed&&e.initialized&&(s("beforeResize"),s("resize"))},o=()=>{e&&!e.destroyed&&e.initialized&&s("orientationchange")};t("init",()=>{e.params.resizeObserver&&void 0!==a.ResizeObserver?e&&!e.destroyed&&e.initialized&&(i=new ResizeObserver(t=>{n=a.requestAnimationFrame(()=>{const{width:s,height:a}=e;let i=s,r=a;t.forEach(({contentBoxSize:t,contentRect:s,target:a})=>{a&&a!==e.el||(i=s?s.width:(t[0]||t).inlineSize,r=s?s.height:(t[0]||t).blockSize)}),i===s&&r===a||l()})}),i.observe(e.el)):(a.addEventListener("resize",l),a.addEventListener("orientationchange",o))}),t("destroy",()=>{n&&a.cancelAnimationFrame(n),i&&i.unobserve&&e.el&&(i.unobserve(e.el),i=null),a.removeEventListener("resize",l),a.removeEventListener("orientationchange",o)})},function({swiper:e,extendParams:t,on:s,emit:a}){const i=[],n=r(),l=(t,s={})=>{const r=new(n.MutationObserver||n.WebkitMutationObserver)(t=>{if(e.__preventObserver__)return;if(1===t.length)return void a("observerUpdate",t[0]);const s=function(){a("observerUpdate",t[0])};n.requestAnimationFrame?n.requestAnimationFrame(s):n.setTimeout(s,0)});r.observe(t,{attributes:void 0===s.attributes||s.attributes,childList:e.isElement||(void 0===s.childList||s).childList,characterData:void 0===s.characterData||s.characterData}),i.push(r)};t({observer:!1,observeParents:!1,observeSlideChildren:!1}),s("init",()=>{if(e.params.observer){if(e.params.observeParents){const t=x(e.hostEl);for(let e=0;e{i.forEach(e=>{e.disconnect()}),i.splice(0,i.length)})}]);const oe='';function de(e=""){return`.${e.trim().replace(/([\.:!+\/()[\]])/g,"\\$1").replace(/ /g,".")}`}function ce(e){const t=this,{params:s,slidesEl:a}=t;s.loop&&t.loopDestroy();const i=e=>{if("string"==typeof e){const t=document.createElement("div");P(t,e),a.append(t.children[0]),P(t,"")}else a.append(e)};if("object"==typeof e&&"length"in e)for(let t=0;t{if("string"==typeof e){const t=document.createElement("div");P(t,e),i.prepend(t.children[0]),P(t,"")}else i.prepend(e)};if("object"==typeof e&&"length"in e){for(let t=0;t=l)return void s.appendSlide(t);let o=n>e?n+1:n;const d=[];for(let t=l-1;t>=e;t-=1){const e=s.slides[t];e.remove(),d.unshift(e)}if("object"==typeof t&&"length"in t){for(let e=0;ee?n+t.length:n}else r.append(t);for(let e=0;e{if(s.params.effect!==t)return;s.classNames.push(`${s.params.containerModifierClass}${t}`),l&&l()&&s.classNames.push(`${s.params.containerModifierClass}3d`);const e=n?n():{};Object.assign(s.params,e),Object.assign(s.originalParams,e)}),a("setTranslate _virtualUpdated",()=>{s.params.effect===t&&i()}),a("setTransition",(e,a)=>{s.params.effect===t&&r(a)}),a("transitionEnd",()=>{if(s.params.effect===t&&o){if(!d||!d().slideShadows)return;s.slides.forEach(e=>{e.querySelectorAll(".swiper-slide-shadow-top, .swiper-slide-shadow-right, .swiper-slide-shadow-bottom, .swiper-slide-shadow-left").forEach(e=>e.remove())}),o()}}),a("virtualUpdate",()=>{s.params.effect===t&&(s.slides.length||(c=!0),requestAnimationFrame(()=>{c&&s.slides&&s.slides.length&&(i(),c=!1)}))})}function ge(e,t){const s=f(t);return s!==t&&(s.style.backfaceVisibility="hidden",s.style["-webkit-backface-visibility"]="hidden"),s}function ve({swiper:e,duration:t,transformElements:s,allSlides:a}){const{activeIndex:i}=e;if(e.params.virtualTranslate&&0!==t){let t,r=!1;t=a?s:s.filter(t=>{const s=t.classList.contains("swiper-slide-transform")?(t=>{if(!t.parentElement)return e.slides.find(e=>e.shadowRoot&&e.shadowRoot===t.parentNode);return t.parentElement})(t):t;return e.getSlideIndex(s)===i}),t.forEach(t=>{S(t,()=>{if(r)return;if(!e||e.destroyed)return;r=!0,e.animating=!1;const t=new window.CustomEvent("transitionend",{bubbles:!0,cancelable:!0});e.wrapperEl.dispatchEvent(t)})})}}function we(e,t,s){const a=`swiper-slide-shadow${s?`-${s}`:""}${e?` swiper-slide-shadow-${e}`:""}`,i=f(t);let r=i.querySelector(`.${a.split(" ").join(".")}`);return r||(r=w("div",a.split(" ")),i.append(r)),r}const be=[function({swiper:e,extendParams:t,on:s,emit:i}){let r;t({virtual:{enabled:!1,slides:[],cache:!0,slidesPerViewAutoSlideSize:320,renderSlide:null,renderExternal:null,renderExternalUpdate:!0,addSlidesBefore:0,addSlidesAfter:0}});const n=a();e.virtual={cache:{},from:void 0,to:void 0,slides:[],offset:0,slidesGrid:[]};const l=n.createElement("div");function o(t,s){const a=e.params.virtual;if(a.cache&&e.virtual.cache[s])return e.virtual.cache[s];let i;return a.renderSlide?(i=a.renderSlide.call(e,t,s),"string"==typeof i&&(P(l,i),i=l.children[0])):i=e.isElement?w("swiper-slide"):w("div",e.params.slideClass),i.setAttribute("data-swiper-slide-index",s),a.renderSlide||P(i,t),a.cache&&(e.virtual.cache[s]=i),i}function d(t,s,a){const{slidesPerGroup:r,centeredSlides:n,slidesPerView:l,loop:d,initialSlide:c}=e.params;if(s&&!d&&c>0)return;const{addSlidesBefore:p,addSlidesAfter:u,slidesPerViewAutoSlideSize:m}=e.params.virtual,{from:h,to:f,slides:v,slidesGrid:w,offset:b}=e.virtual;e.params.cssMode||e.updateActiveIndex();const y=void 0===a?e.activeIndex||0:a;let E,x,S,T;if(E=e.rtlTranslate?"right":e.isHorizontal()?"left":"top","auto"===l)if(m){let t=e.size;t||(t=e.isHorizontal()?e.el.getBoundingClientRect().width:e.el.getBoundingClientRect().height),x=Math.max(1,Math.ceil(t/m))}else x=1;else x=l;n?(S=Math.floor(x/2)+r+u,T=Math.floor(x/2)+r+p):(S=x+(r-1)+u,T=(d?x:r)+p);let M=y-T,C=y+S;d||(M=Math.max(M,0),C=Math.min(C,v.length-1));let P=(e.slidesGrid[M]||0)-(e.slidesGrid[0]||0);function L(){e.updateSlides(),e.updateProgress(),e.updateSlidesClasses(),i("virtualUpdate")}if(d&&y>=T?(M-=T,n||(P+=e.slidesGrid[0])):d&&y{t.style[E]=P-Math.abs(e.cssOverflowAdjustment())+"px"}),e.updateProgress(),void i("virtualUpdate");if(e.params.virtual.renderExternal)return e.params.virtual.renderExternal.call(e,{offset:P,from:M,to:C,slides:function(){const e=[];for(let t=M;t<=C;t+=1)e.push(v[t]);return e}()}),void(e.params.virtual.renderExternalUpdate?L():i("virtualUpdate"));const I=[],z=[],A=e=>{let t=e;return e<0?t=v.length+e:t>=v.length&&(t-=v.length),t};if(t)e.slides.filter(t=>t.matches(`.${e.params.slideClass}, swiper-slide`)).forEach(e=>{e.remove()});else for(let t=h;t<=f;t+=1)if(tC){const s=A(t);e.slides.filter(t=>t.matches(`.${e.params.slideClass}[data-swiper-slide-index="${s}"], swiper-slide[data-swiper-slide-index="${s}"]`)).forEach(e=>{e.remove()})}const $=d?-v.length:0,k=d?2*v.length:v.length;for(let e=$;e=M&&e<=C){const s=A(e);void 0===f||t?z.push(s):(e>f&&z.push(s),e{e.slidesEl.append(o(v[t],t))}),d)for(let t=I.length-1;t>=0;t-=1){const s=I[t];e.slidesEl.prepend(o(v[s],s))}else I.sort((e,t)=>t-e),I.forEach(t=>{e.slidesEl.prepend(o(v[t],t))});g(e.slidesEl,".swiper-slide, swiper-slide").forEach(t=>{t.style[E]=P-Math.abs(e.cssOverflowAdjustment())+"px"}),L()}s("beforeInit",()=>{if(!e.params.virtual.enabled)return;let t;if(void 0===e.passedParams.virtual.slides){const s=[...e.slidesEl.children].filter(t=>t.matches(`.${e.params.slideClass}, swiper-slide`));s&&s.length&&(e.virtual.slides=[...s],t=!0,s.forEach((t,s)=>{t.setAttribute("data-swiper-slide-index",s),e.virtual.cache[s]=t,t.remove()}))}t||(e.virtual.slides=e.params.virtual.slides),e.classNames.push(`${e.params.containerModifierClass}virtual`),e.params.watchSlidesProgress=!0,e.originalParams.watchSlidesProgress=!0,d(!1,!0)}),s("setTranslate",()=>{e.params.virtual.enabled&&(e.params.cssMode&&!e._immediateVirtual?(clearTimeout(r),r=setTimeout(()=>{d()},100)):d())}),s("init update resize",()=>{e.params.virtual.enabled&&e.params.cssMode&&m(e.wrapperEl,"--swiper-virtual-size",`${e.virtualSize}px`)}),Object.assign(e.virtual,{appendSlide:function(t){if("object"==typeof t&&"length"in t)for(let s=0;s{const a=t[e],r=a.getAttribute("data-swiper-slide-index");r&&a.setAttribute("data-swiper-slide-index",parseInt(r,10)+i),s[parseInt(e,10)+i]=a}),e.virtual.cache=s}d(!0),e.slideTo(a,0)},removeSlide:function(t){if(null==t)return;let s=e.activeIndex;if(Array.isArray(t))for(let a=t.length-1;a>=0;a-=1)e.params.virtual.cache&&(delete e.virtual.cache[t[a]],Object.keys(e.virtual.cache).forEach(s=>{s>t&&(e.virtual.cache[s-1]=e.virtual.cache[s],e.virtual.cache[s-1].setAttribute("data-swiper-slide-index",s-1),delete e.virtual.cache[s])})),e.virtual.slides.splice(t[a],1),t[a]{s>t&&(e.virtual.cache[s-1]=e.virtual.cache[s],e.virtual.cache[s-1].setAttribute("data-swiper-slide-index",s-1),delete e.virtual.cache[s])})),e.virtual.slides.splice(t,1),t0&&0===x(e.el,`.${e.params.slideActiveClass}`).length)return;const a=e.el,i=a.clientWidth,r=a.clientHeight,n=l.innerWidth,o=l.innerHeight,d=b(a);s&&(d.left-=a.scrollLeft);const c=[[d.left,d.top],[d.left+i,d.top],[d.left,d.top+r],[d.left+i,d.top+r]];for(let e=0;e=0&&s[0]<=n&&s[1]>=0&&s[1]<=o){if(0===s[0]&&0===s[1])continue;t=!0}}if(!t)return}e.isHorizontal()?((d||c||p||u)&&(a.preventDefault?a.preventDefault():a.returnValue=!1),((c||u)&&!s||(d||p)&&s)&&e.slideNext(),((d||p)&&!s||(c||u)&&s)&&e.slidePrev()):((d||c||m||h)&&(a.preventDefault?a.preventDefault():a.returnValue=!1),(c||h)&&e.slideNext(),(d||m)&&e.slidePrev()),i("keyPress",r)}}function d(){e.keyboard.enabled||(n.addEventListener("keydown",o),e.keyboard.enabled=!0)}function c(){e.keyboard.enabled&&(n.removeEventListener("keydown",o),e.keyboard.enabled=!1)}e.keyboard={enabled:!1},t({keyboard:{enabled:!1,onlyInViewport:!0,pageUpDown:!0}}),s("init",()=>{e.params.keyboard.enabled&&d()}),s("destroy",()=>{e.keyboard.enabled&&c()}),Object.assign(e.keyboard,{enable:d,disable:c})},function({swiper:e,extendParams:t,on:s,emit:a}){const i=r();let n;t({mousewheel:{enabled:!1,releaseOnEdges:!1,invert:!1,forceToAxis:!1,sensitivity:1,eventsTarget:"container",thresholdDelta:null,thresholdTime:null,noMousewheelClass:"swiper-no-mousewheel"}}),e.mousewheel={enabled:!1};let d,c=o();const p=[];function u(){e.enabled&&(e.mouseEntered=!0)}function m(){e.enabled&&(e.mouseEntered=!1)}function h(t){return!(e.params.mousewheel.thresholdDelta&&t.delta=6&&o()-c<60||(t.direction<0?e.isEnd&&!e.params.loop||e.animating||(e.slideNext(),a("scroll",t.raw)):e.isBeginning&&!e.params.loop||e.animating||(e.slidePrev(),a("scroll",t.raw)),c=(new i.Date).getTime(),!1)))}function f(t){let s=t,i=!0;if(!e.enabled)return;if(t.target.closest(`.${e.params.mousewheel.noMousewheelClass}`))return;const r=e.params.mousewheel;e.params.cssMode&&s.preventDefault();let c=e.el;"container"!==e.params.mousewheel.eventsTarget&&(c=document.querySelector(e.params.mousewheel.eventsTarget));const u=c&&c.contains(s.target);if(!e.mouseEntered&&!u&&!r.releaseOnEdges)return!0;s.originalEvent&&(s=s.originalEvent);let m=0;const f=e.rtlTranslate?-1:1,g=function(e){let t=0,s=0,a=0,i=0;return"detail"in e&&(s=e.detail),"wheelDelta"in e&&(s=-e.wheelDelta/120),"wheelDeltaY"in e&&(s=-e.wheelDeltaY/120),"wheelDeltaX"in e&&(t=-e.wheelDeltaX/120),"axis"in e&&e.axis===e.HORIZONTAL_AXIS&&(t=s,s=0),a=10*t,i=10*s,"deltaY"in e&&(i=e.deltaY),"deltaX"in e&&(a=e.deltaX),e.shiftKey&&!a&&(a=i,i=0),(a||i)&&e.deltaMode&&(1===e.deltaMode?(a*=40,i*=40):(a*=800,i*=800)),a&&!t&&(t=a<1?-1:1),i&&!s&&(s=i<1?-1:1),{spinX:t,spinY:s,pixelX:a,pixelY:i}}(s);if(r.forceToAxis)if(e.isHorizontal()){if(!(Math.abs(g.pixelX)>Math.abs(g.pixelY)))return!0;m=-g.pixelX*f}else{if(!(Math.abs(g.pixelY)>Math.abs(g.pixelX)))return!0;m=-g.pixelY}else m=Math.abs(g.pixelX)>Math.abs(g.pixelY)?-g.pixelX*f:-g.pixelY;if(0===m)return!0;r.invert&&(m=-m);let v=e.getTranslate()+m*r.sensitivity;if(v>=e.minTranslate()&&(v=e.minTranslate()),v<=e.maxTranslate()&&(v=e.maxTranslate()),i=!!e.params.loop||!(v===e.minTranslate()||v===e.maxTranslate()),i&&e.params.nested&&s.stopPropagation(),e.params.freeMode&&e.params.freeMode.enabled){const t={time:o(),delta:Math.abs(m),direction:Math.sign(m)},i=d&&t.time=e.minTranslate()&&(o=e.minTranslate()),o<=e.maxTranslate()&&(o=e.maxTranslate()),e.setTransition(0),e.setTranslate(o),e.updateProgress(),e.updateActiveIndex(),e.updateSlidesClasses(),(!c&&e.isBeginning||!u&&e.isEnd)&&e.updateSlidesClasses(),e.params.loop&&e.loopFix({direction:t.direction<0?"next":"prev",byMousewheel:!0}),e.params.freeMode.sticky){clearTimeout(n),n=void 0,p.length>=15&&p.shift();const s=p.length?p[p.length-1]:void 0,a=p[0];if(p.push(t),s&&(t.delta>s.delta||t.direction!==s.direction))p.splice(0);else if(p.length>=15&&t.time-a.time<500&&a.delta-t.delta>=1&&t.delta<=6){const s=m>0?.8:.2;d=t,p.splice(0),n=l(()=>{!e.destroyed&&e.params&&e.slideToClosest(e.params.speed,!0,void 0,s)},0)}n||(n=l(()=>{if(e.destroyed||!e.params)return;d=t,p.splice(0),e.slideToClosest(e.params.speed,!0,void 0,.5)},500))}if(i||a("scroll",s),e.params.autoplay&&e.params.autoplay.disableOnInteraction&&e.autoplay.stop(),r.releaseOnEdges&&(o===e.minTranslate()||o===e.maxTranslate()))return!0}}else{const s={time:o(),delta:Math.abs(m),direction:Math.sign(m),raw:t};p.length>=2&&p.shift();const a=p.length?p[p.length-1]:void 0;if(p.push(s),a?(s.direction!==a.direction||s.delta>a.delta||s.time>a.time+150)&&h(s):h(s),function(t){const s=e.params.mousewheel;if(t.direction<0){if(e.isEnd&&!e.params.loop&&s.releaseOnEdges)return!0}else if(e.isBeginning&&!e.params.loop&&s.releaseOnEdges)return!0;return!1}(s))return!0}return s.preventDefault?s.preventDefault():s.returnValue=!1,!1}function g(t){let s=e.el;"container"!==e.params.mousewheel.eventsTarget&&(s=document.querySelector(e.params.mousewheel.eventsTarget)),s[t]("mouseenter",u),s[t]("mouseleave",m),s[t]("wheel",f)}function v(){return e.params.cssMode?(e.wrapperEl.removeEventListener("wheel",f),!0):!e.mousewheel.enabled&&(g("addEventListener"),e.mousewheel.enabled=!0,!0)}function w(){return e.params.cssMode?(e.wrapperEl.addEventListener(event,f),!0):!!e.mousewheel.enabled&&(g("removeEventListener"),e.mousewheel.enabled=!1,!0)}s("init",()=>{!e.params.mousewheel.enabled&&e.params.cssMode&&w(),e.params.mousewheel.enabled&&v()}),s("destroy",()=>{e.params.cssMode&&v(),e.mousewheel.enabled&&w()}),Object.assign(e.mousewheel,{enable:v,disable:w})},function({swiper:e,extendParams:t,on:s,emit:a}){function i(t){let s;return t&&"string"==typeof t&&e.isElement&&(s=e.el.querySelector(t)||e.hostEl.querySelector(t),s)?s:(t&&("string"==typeof t&&(s=[...document.querySelectorAll(t)]),e.params.uniqueNavElements&&"string"==typeof t&&s&&s.length>1&&1===e.el.querySelectorAll(t).length?s=e.el.querySelector(t):s&&1===s.length&&(s=s[0])),t&&!s?t:s)}function r(t,s){const a=e.params.navigation;(t=M(t)).forEach(t=>{t&&(t.classList[s?"add":"remove"](...a.disabledClass.split(" ")),"BUTTON"===t.tagName&&(t.disabled=s),e.params.watchOverflow&&e.enabled&&t.classList[e.isLocked?"add":"remove"](a.lockClass))})}function n(){const{nextEl:t,prevEl:s}=e.navigation;if(e.params.loop)return r(s,!1),void r(t,!1);r(s,e.isBeginning&&!e.params.rewind),r(t,e.isEnd&&!e.params.rewind)}function l(t){t.preventDefault(),(!e.isBeginning||e.params.loop||e.params.rewind)&&(e.slidePrev(),a("navigationPrev"))}function o(t){t.preventDefault(),(!e.isEnd||e.params.loop||e.params.rewind)&&(e.slideNext(),a("navigationNext"))}function d(){const t=e.params.navigation;if(e.params.navigation=le(e,e.originalParams.navigation,e.params.navigation,{nextEl:"swiper-button-next",prevEl:"swiper-button-prev"}),!t.nextEl&&!t.prevEl)return;let s=i(t.nextEl),a=i(t.prevEl);Object.assign(e.navigation,{nextEl:s,prevEl:a}),s=M(s),a=M(a);const r=(s,a)=>{if(s){if(t.addIcons&&s.matches(".swiper-button-next,.swiper-button-prev")&&!s.querySelector("svg")){const e=document.createElement("div");P(e,oe),s.appendChild(e.querySelector("svg")),e.remove()}s.addEventListener("click","next"===a?o:l)}!e.enabled&&s&&s.classList.add(...t.lockClass.split(" "))};s.forEach(e=>r(e,"next")),a.forEach(e=>r(e,"prev"))}function c(){let{nextEl:t,prevEl:s}=e.navigation;t=M(t),s=M(s);const a=(t,s)=>{t.removeEventListener("click","next"===s?o:l),t.classList.remove(...e.params.navigation.disabledClass.split(" "))};t.forEach(e=>a(e,"next")),s.forEach(e=>a(e,"prev"))}t({navigation:{nextEl:null,prevEl:null,addIcons:!0,hideOnClick:!1,disabledClass:"swiper-button-disabled",hiddenClass:"swiper-button-hidden",lockClass:"swiper-button-lock",navigationDisabledClass:"swiper-navigation-disabled"}}),e.navigation={nextEl:null,prevEl:null,arrowSvg:oe},s("init",()=>{!1===e.params.navigation.enabled?p():(d(),n())}),s("toEdge fromEdge lock unlock",()=>{n()}),s("destroy",()=>{c()}),s("enable disable",()=>{let{nextEl:t,prevEl:s}=e.navigation;t=M(t),s=M(s),e.enabled?n():[...t,...s].filter(e=>!!e).forEach(t=>t.classList.add(e.params.navigation.lockClass))}),s("click",(t,s)=>{let{nextEl:i,prevEl:r}=e.navigation;i=M(i),r=M(r);const n=s.target;let l=r.includes(n)||i.includes(n);if(e.isElement&&!l){const e=s.path||s.composedPath&&s.composedPath();e&&(l=e.find(e=>i.includes(e)||r.includes(e)))}if(e.params.navigation.hideOnClick&&!l){if(e.pagination&&e.params.pagination&&e.params.pagination.clickable&&(e.pagination.el===n||e.pagination.el.contains(n)))return;let t;i.length?t=i[0].classList.contains(e.params.navigation.hiddenClass):r.length&&(t=r[0].classList.contains(e.params.navigation.hiddenClass)),a(!0===t?"navigationShow":"navigationHide"),[...i,...r].filter(e=>!!e).forEach(t=>t.classList.toggle(e.params.navigation.hiddenClass))}});const p=()=>{e.el.classList.add(...e.params.navigation.navigationDisabledClass.split(" ")),c()};Object.assign(e.navigation,{enable:()=>{e.el.classList.remove(...e.params.navigation.navigationDisabledClass.split(" ")),d(),n()},disable:p,update:n,init:d,destroy:c})},function({swiper:e,extendParams:t,on:s,emit:a}){const i="swiper-pagination";let r;t({pagination:{el:null,bulletElement:"span",clickable:!1,hideOnClick:!1,renderBullet:null,renderProgressbar:null,renderFraction:null,renderCustom:null,progressbarOpposite:!1,type:"bullets",dynamicBullets:!1,dynamicMainBullets:1,formatFractionCurrent:e=>e,formatFractionTotal:e=>e,bulletClass:`${i}-bullet`,bulletActiveClass:`${i}-bullet-active`,modifierClass:`${i}-`,currentClass:`${i}-current`,totalClass:`${i}-total`,hiddenClass:`${i}-hidden`,progressbarFillClass:`${i}-progressbar-fill`,progressbarOppositeClass:`${i}-progressbar-opposite`,clickableClass:`${i}-clickable`,lockClass:`${i}-lock`,horizontalClass:`${i}-horizontal`,verticalClass:`${i}-vertical`,paginationDisabledClass:`${i}-disabled`}}),e.pagination={el:null,bullets:[]};let n=0;function l(){return!e.params.pagination.el||!e.pagination.el||Array.isArray(e.pagination.el)&&0===e.pagination.el.length}function o(t,s){const{bulletActiveClass:a}=e.params.pagination;t&&(t=t[("prev"===s?"previous":"next")+"ElementSibling"])&&(t.classList.add(`${a}-${s}`),(t=t[("prev"===s?"previous":"next")+"ElementSibling"])&&t.classList.add(`${a}-${s}-${s}`))}function d(t){const s=t.target.closest(de(e.params.pagination.bulletClass));if(!s)return;t.preventDefault();const a=E(s)*e.params.slidesPerGroup;if(e.params.loop){if(e.realIndex===a)return;const t=(i=e.realIndex,r=a,n=e.slides.length,(r%=n)===1+(i%=n)?"next":r===i-1?"previous":void 0);"next"===t?e.slideNext():"previous"===t?e.slidePrev():e.slideToLoop(a)}else e.slideTo(a);var i,r,n}function c(){const t=e.rtl,s=e.params.pagination;if(l())return;let i,d,c=e.pagination.el;c=M(c);const p=e.virtual&&e.params.virtual.enabled?e.virtual.slides.length:e.slides.length,u=e.params.loop?Math.ceil(p/e.params.slidesPerGroup):e.snapGrid.length;if(e.params.loop?(d=e.previousRealIndex||0,i=e.params.slidesPerGroup>1?Math.floor(e.realIndex/e.params.slidesPerGroup):e.realIndex):void 0!==e.snapIndex?(i=e.snapIndex,d=e.previousSnapIndex):(d=e.previousIndex||0,i=e.activeIndex||0),"bullets"===s.type&&e.pagination.bullets&&e.pagination.bullets.length>0){const a=e.pagination.bullets;let l,p,u;if(s.dynamicBullets&&(r=T(a[0],e.isHorizontal()?"width":"height",!0),c.forEach(t=>{t.style[e.isHorizontal()?"width":"height"]=r*(s.dynamicMainBullets+4)+"px"}),s.dynamicMainBullets>1&&void 0!==d&&(n+=i-(d||0),n>s.dynamicMainBullets-1?n=s.dynamicMainBullets-1:n<0&&(n=0)),l=Math.max(i-n,0),p=l+(Math.min(a.length,s.dynamicMainBullets)-1),u=(p+l)/2),a.forEach(e=>{const t=[...["","-next","-next-next","-prev","-prev-prev","-main"].map(e=>`${s.bulletActiveClass}${e}`)].map(e=>"string"==typeof e&&e.includes(" ")?e.split(" "):e).flat();e.classList.remove(...t)}),c.length>1)a.forEach(t=>{const a=E(t);a===i?t.classList.add(...s.bulletActiveClass.split(" ")):e.isElement&&t.setAttribute("part","bullet"),s.dynamicBullets&&(a>=l&&a<=p&&t.classList.add(...`${s.bulletActiveClass}-main`.split(" ")),a===l&&o(t,"prev"),a===p&&o(t,"next"))});else{const t=a[i];if(t&&t.classList.add(...s.bulletActiveClass.split(" ")),e.isElement&&a.forEach((e,t)=>{e.setAttribute("part",t===i?"bullet-active":"bullet")}),s.dynamicBullets){const e=a[l],t=a[p];for(let e=l;e<=p;e+=1)a[e]&&a[e].classList.add(...`${s.bulletActiveClass}-main`.split(" "));o(e,"prev"),o(t,"next")}}if(s.dynamicBullets){const i=Math.min(a.length,s.dynamicMainBullets+4),n=(r*i-r)/2-u*r,l=t?"right":"left";a.forEach(t=>{t.style[e.isHorizontal()?l:"top"]=`${n}px`})}}c.forEach((t,r)=>{if("fraction"===s.type&&(t.querySelectorAll(de(s.currentClass)).forEach(e=>{e.textContent=s.formatFractionCurrent(i+1)}),t.querySelectorAll(de(s.totalClass)).forEach(e=>{e.textContent=s.formatFractionTotal(u)})),"progressbar"===s.type){let a;a=s.progressbarOpposite?e.isHorizontal()?"vertical":"horizontal":e.isHorizontal()?"horizontal":"vertical";const r=(i+1)/u;let n=1,l=1;"horizontal"===a?n=r:l=r,t.querySelectorAll(de(s.progressbarFillClass)).forEach(t=>{t.style.transform=`translate3d(0,0,0) scaleX(${n}) scaleY(${l})`,t.style.transitionDuration=`${e.params.speed}ms`})}"custom"===s.type&&s.renderCustom?(P(t,s.renderCustom(e,i+1,u)),0===r&&a("paginationRender",t)):(0===r&&a("paginationRender",t),a("paginationUpdate",t)),e.params.watchOverflow&&e.enabled&&t.classList[e.isLocked?"add":"remove"](s.lockClass)})}function p(){const t=e.params.pagination;if(l())return;const s=e.virtual&&e.params.virtual.enabled?e.virtual.slides.length:e.grid&&e.params.grid.rows>1?e.slides.length/Math.ceil(e.params.grid.rows):e.slides.length;let i=e.pagination.el;i=M(i);let r="";if("bullets"===t.type){let a=e.params.loop?Math.ceil(s/e.params.slidesPerGroup):e.snapGrid.length;e.params.freeMode&&e.params.freeMode.enabled&&a>s&&(a=s);for(let s=0;s`}"fraction"===t.type&&(r=t.renderFraction?t.renderFraction.call(e,t.currentClass,t.totalClass):` / `),"progressbar"===t.type&&(r=t.renderProgressbar?t.renderProgressbar.call(e,t.progressbarFillClass):``),e.pagination.bullets=[],i.forEach(s=>{"custom"!==t.type&&P(s,r||""),"bullets"===t.type&&e.pagination.bullets.push(...s.querySelectorAll(de(t.bulletClass)))}),"custom"!==t.type&&a("paginationRender",i[0])}function u(){e.params.pagination=le(e,e.originalParams.pagination,e.params.pagination,{el:"swiper-pagination"});const t=e.params.pagination;if(!t.el)return;let s;"string"==typeof t.el&&e.isElement&&(s=e.el.querySelector(t.el)),s||"string"!=typeof t.el||(s=[...document.querySelectorAll(t.el)]),s||(s=t.el),s&&0!==s.length&&(e.params.uniqueNavElements&&"string"==typeof t.el&&Array.isArray(s)&&s.length>1&&(s=[...e.el.querySelectorAll(t.el)],s.length>1&&(s=s.find(t=>x(t,".swiper")[0]===e.el))),Array.isArray(s)&&1===s.length&&(s=s[0]),Object.assign(e.pagination,{el:s}),s=M(s),s.forEach(s=>{"bullets"===t.type&&t.clickable&&s.classList.add(...(t.clickableClass||"").split(" ")),s.classList.add(t.modifierClass+t.type),s.classList.add(e.isHorizontal()?t.horizontalClass:t.verticalClass),"bullets"===t.type&&t.dynamicBullets&&(s.classList.add(`${t.modifierClass}${t.type}-dynamic`),n=0,t.dynamicMainBullets<1&&(t.dynamicMainBullets=1)),"progressbar"===t.type&&t.progressbarOpposite&&s.classList.add(t.progressbarOppositeClass),t.clickable&&s.addEventListener("click",d),e.enabled||s.classList.add(t.lockClass)}))}function m(){const t=e.params.pagination;if(l())return;let s=e.pagination.el;s&&(s=M(s),s.forEach(s=>{s.classList.remove(t.hiddenClass),s.classList.remove(t.modifierClass+t.type),s.classList.remove(e.isHorizontal()?t.horizontalClass:t.verticalClass),t.clickable&&(s.classList.remove(...(t.clickableClass||"").split(" ")),s.removeEventListener("click",d))})),e.pagination.bullets&&e.pagination.bullets.forEach(e=>e.classList.remove(...t.bulletActiveClass.split(" ")))}s("changeDirection",()=>{if(!e.pagination||!e.pagination.el)return;const t=e.params.pagination;let{el:s}=e.pagination;s=M(s),s.forEach(s=>{s.classList.remove(t.horizontalClass,t.verticalClass),s.classList.add(e.isHorizontal()?t.horizontalClass:t.verticalClass)})}),s("init",()=>{!1===e.params.pagination.enabled?h():(u(),p(),c())}),s("activeIndexChange",()=>{void 0===e.snapIndex&&c()}),s("snapIndexChange",()=>{c()}),s("snapGridLengthChange",()=>{p(),c()}),s("destroy",()=>{m()}),s("enable disable",()=>{let{el:t}=e.pagination;t&&(t=M(t),t.forEach(t=>t.classList[e.enabled?"remove":"add"](e.params.pagination.lockClass)))}),s("lock unlock",()=>{c()}),s("click",(t,s)=>{const i=s.target,r=M(e.pagination.el);if(e.params.pagination.el&&e.params.pagination.hideOnClick&&r&&r.length>0&&!i.classList.contains(e.params.pagination.bulletClass)){if(e.navigation&&(e.navigation.nextEl&&i===e.navigation.nextEl||e.navigation.prevEl&&i===e.navigation.prevEl))return;const t=r[0].classList.contains(e.params.pagination.hiddenClass);a(!0===t?"paginationShow":"paginationHide"),r.forEach(t=>t.classList.toggle(e.params.pagination.hiddenClass))}});const h=()=>{e.el.classList.add(e.params.pagination.paginationDisabledClass);let{el:t}=e.pagination;t&&(t=M(t),t.forEach(t=>t.classList.add(e.params.pagination.paginationDisabledClass))),m()};Object.assign(e.pagination,{enable:()=>{e.el.classList.remove(e.params.pagination.paginationDisabledClass);let{el:t}=e.pagination;t&&(t=M(t),t.forEach(t=>t.classList.remove(e.params.pagination.paginationDisabledClass))),u(),p(),c()},disable:h,render:p,update:c,init:u,destroy:m})},function({swiper:e,extendParams:t,on:s,emit:i}){const r=a();let o,d,c,p,u=!1,m=null,h=null;function f(){if(!e.params.scrollbar.el||!e.scrollbar.el)return;const{scrollbar:t,rtlTranslate:s}=e,{dragEl:a,el:i}=t,r=e.params.scrollbar,n=e.params.loop?e.progressLoop:e.progress;let l=d,o=(c-d)*n;s?(o=-o,o>0?(l=d-o,o=0):-o+d>c&&(l=c+o)):o<0?(l=d+o,o=0):o+d>c&&(l=c-o),e.isHorizontal()?(a.style.transform=`translate3d(${o}px, 0, 0)`,a.style.width=`${l}px`):(a.style.transform=`translate3d(0px, ${o}px, 0)`,a.style.height=`${l}px`),r.hide&&(clearTimeout(m),i.style.opacity=1,m=setTimeout(()=>{i.style.opacity=0,i.style.transitionDuration="400ms"},1e3))}function g(){if(!e.params.scrollbar.el||!e.scrollbar.el)return;const{scrollbar:t}=e,{dragEl:s,el:a}=t;s.style.width="",s.style.height="",c=e.isHorizontal()?a.offsetWidth:a.offsetHeight,p=e.size/(e.virtualSize+e.params.slidesOffsetBefore-(e.params.centeredSlides?e.snapGrid[0]:0)),d="auto"===e.params.scrollbar.dragSize?c*p:parseInt(e.params.scrollbar.dragSize,10),e.isHorizontal()?s.style.width=`${d}px`:s.style.height=`${d}px`,a.style.display=p>=1?"none":"",e.params.scrollbar.hide&&(a.style.opacity=0),e.params.watchOverflow&&e.enabled&&t.el.classList[e.isLocked?"add":"remove"](e.params.scrollbar.lockClass)}function v(t){return e.isHorizontal()?t.clientX:t.clientY}function y(t){const{scrollbar:s,rtlTranslate:a}=e,{el:i}=s;let r;r=(v(t)-b(i)[e.isHorizontal()?"left":"top"]-(null!==o?o:d/2))/(c-d),r=Math.max(Math.min(r,1),0),a&&(r=1-r);const n=e.minTranslate()+(e.maxTranslate()-e.minTranslate())*r;e.updateProgress(n),e.setTranslate(n),e.updateActiveIndex(),e.updateSlidesClasses()}function E(t){const s=e.params.scrollbar,{scrollbar:a,wrapperEl:r}=e,{el:n,dragEl:l}=a;u=!0,o=t.target===l?v(t)-t.target.getBoundingClientRect()[e.isHorizontal()?"left":"top"]:null,t.preventDefault(),t.stopPropagation(),r.style.transitionDuration="100ms",l.style.transitionDuration="100ms",y(t),clearTimeout(h),n.style.transitionDuration="0ms",s.hide&&(n.style.opacity=1),e.params.cssMode&&(e.wrapperEl.style["scroll-snap-type"]="none"),i("scrollbarDragStart",t)}function x(t){const{scrollbar:s,wrapperEl:a}=e,{el:r,dragEl:n}=s;u&&(t.preventDefault&&t.cancelable?t.preventDefault():t.returnValue=!1,y(t),a.style.transitionDuration="0ms",r.style.transitionDuration="0ms",n.style.transitionDuration="0ms",i("scrollbarDragMove",t))}function S(t){const s=e.params.scrollbar,{scrollbar:a,wrapperEl:r}=e,{el:n}=a;u&&(u=!1,e.params.cssMode&&(e.wrapperEl.style["scroll-snap-type"]="",r.style.transitionDuration=""),s.hide&&(clearTimeout(h),h=l(()=>{n.style.opacity=0,n.style.transitionDuration="400ms"},1e3)),i("scrollbarDragEnd",t),s.snapOnRelease&&e.slideToClosest())}function T(t){const{scrollbar:s,params:a}=e,i=s.el;if(!i)return;const n=i,l=!!a.passiveListeners&&{passive:!1,capture:!1},o=!!a.passiveListeners&&{passive:!0,capture:!1};if(!n)return;const d="on"===t?"addEventListener":"removeEventListener";n[d]("pointerdown",E,l),r[d]("pointermove",x,l),r[d]("pointerup",S,o)}function C(){const{scrollbar:t,el:s}=e;e.params.scrollbar=le(e,e.originalParams.scrollbar,e.params.scrollbar,{el:"swiper-scrollbar"});const a=e.params.scrollbar;if(!a.el)return;let i,l;if("string"==typeof a.el&&e.isElement&&(i=e.el.querySelector(a.el)),i||"string"!=typeof a.el)i||(i=a.el);else if(i=r.querySelectorAll(a.el),!i.length)return;e.params.uniqueNavElements&&"string"==typeof a.el&&i.length>1&&1===s.querySelectorAll(a.el).length&&(i=s.querySelector(a.el)),i.length>0&&(i=i[0]),i.classList.add(e.isHorizontal()?a.horizontalClass:a.verticalClass),i&&(l=i.querySelector(de(e.params.scrollbar.dragClass)),l||(l=w("div",e.params.scrollbar.dragClass),i.append(l))),Object.assign(t,{el:i,dragEl:l}),a.draggable&&e.params.scrollbar.el&&e.scrollbar.el&&T("on"),i&&i.classList[e.enabled?"remove":"add"](...n(e.params.scrollbar.lockClass))}function P(){const t=e.params.scrollbar,s=e.scrollbar.el;s&&s.classList.remove(...n(e.isHorizontal()?t.horizontalClass:t.verticalClass)),e.params.scrollbar.el&&e.scrollbar.el&&T("off")}t({scrollbar:{el:null,dragSize:"auto",hide:!1,draggable:!1,snapOnRelease:!0,lockClass:"swiper-scrollbar-lock",dragClass:"swiper-scrollbar-drag",scrollbarDisabledClass:"swiper-scrollbar-disabled",horizontalClass:"swiper-scrollbar-horizontal",verticalClass:"swiper-scrollbar-vertical"}}),e.scrollbar={el:null,dragEl:null},s("changeDirection",()=>{if(!e.scrollbar||!e.scrollbar.el)return;const t=e.params.scrollbar;let{el:s}=e.scrollbar;s=M(s),s.forEach(s=>{s.classList.remove(t.horizontalClass,t.verticalClass),s.classList.add(e.isHorizontal()?t.horizontalClass:t.verticalClass)})}),s("init",()=>{!1===e.params.scrollbar.enabled?L():(C(),g(),f())}),s("update resize observerUpdate lock unlock changeDirection",()=>{g()}),s("setTranslate",()=>{f()}),s("setTransition",(t,s)=>{!function(t){e.params.scrollbar.el&&e.scrollbar.el&&(e.scrollbar.dragEl.style.transitionDuration=`${t}ms`)}(s)}),s("enable disable",()=>{const{el:t}=e.scrollbar;t&&t.classList[e.enabled?"remove":"add"](...n(e.params.scrollbar.lockClass))}),s("destroy",()=>{P()});const L=()=>{e.el.classList.add(...n(e.params.scrollbar.scrollbarDisabledClass)),e.scrollbar.el&&e.scrollbar.el.classList.add(...n(e.params.scrollbar.scrollbarDisabledClass)),P()};Object.assign(e.scrollbar,{enable:()=>{e.el.classList.remove(...n(e.params.scrollbar.scrollbarDisabledClass)),e.scrollbar.el&&e.scrollbar.el.classList.remove(...n(e.params.scrollbar.scrollbarDisabledClass)),C(),g(),f()},disable:L,updateSize:g,setTranslate:f,init:C,destroy:P})},function({swiper:e,extendParams:t,on:s}){t({parallax:{enabled:!1}});const a="[data-swiper-parallax], [data-swiper-parallax-x], [data-swiper-parallax-y], [data-swiper-parallax-opacity], [data-swiper-parallax-scale]",i=(t,s)=>{const{rtl:a}=e,i=a?-1:1,r=t.getAttribute("data-swiper-parallax")||"0";let n=t.getAttribute("data-swiper-parallax-x"),l=t.getAttribute("data-swiper-parallax-y");const o=t.getAttribute("data-swiper-parallax-scale"),d=t.getAttribute("data-swiper-parallax-opacity"),c=t.getAttribute("data-swiper-parallax-rotate");if(n||l?(n=n||"0",l=l||"0"):e.isHorizontal()?(n=r,l="0"):(l=r,n="0"),n=n.indexOf("%")>=0?parseInt(n,10)*s*i+"%":n*s*i+"px",l=l.indexOf("%")>=0?parseInt(l,10)*s+"%":l*s+"px",null!=d){const e=d-(d-1)*(1-Math.abs(s));t.style.opacity=e}let p=`translate3d(${n}, ${l}, 0px)`;if(null!=o){p+=` scale(${o-(o-1)*(1-Math.abs(s))})`}if(c&&null!=c){p+=` rotate(${c*s*-1}deg)`}t.style.transform=p},r=()=>{const{el:t,slides:s,progress:r,snapGrid:n,isElement:l}=e,o=g(t,a);e.isElement&&o.push(...g(e.hostEl,a)),o.forEach(e=>{i(e,r)}),s.forEach((t,s)=>{let l=t.progress;e.params.slidesPerGroup>1&&"auto"!==e.params.slidesPerView&&(l+=Math.ceil(s/2)-r*(n.length-1)),l=Math.min(Math.max(l,-1),1),t.querySelectorAll(`${a}, [data-swiper-parallax-rotate]`).forEach(e=>{i(e,l)})})};s("beforeInit",()=>{e.params.parallax.enabled&&(e.params.watchSlidesProgress=!0,e.originalParams.watchSlidesProgress=!0)}),s("init",()=>{e.params.parallax.enabled&&r()}),s("setTranslate",()=>{e.params.parallax.enabled&&r()}),s("setTransition",(t,s)=>{e.params.parallax.enabled&&((t=e.params.speed)=>{const{el:s,hostEl:i}=e,r=[...s.querySelectorAll(a)];e.isElement&&r.push(...i.querySelectorAll(a)),r.forEach(e=>{let s=parseInt(e.getAttribute("data-swiper-parallax-duration"),10)||t;0===t&&(s=0),e.style.transitionDuration=`${s}ms`})})(s)})},function({swiper:e,extendParams:t,on:s,emit:a}){const i=r();t({zoom:{enabled:!1,limitToOriginalSize:!1,maxRatio:3,minRatio:1,panOnMouseMove:!1,toggle:!0,containerClass:"swiper-zoom-container",zoomedSlideClass:"swiper-slide-zoomed"}}),e.zoom={enabled:!1};let n,l,o=1,c=!1,p=!1,u={x:0,y:0};const m=[],h={originX:0,originY:0,slideEl:void 0,slideWidth:void 0,slideHeight:void 0,imageEl:void 0,imageWrapEl:void 0,maxRatio:3},f={isTouched:void 0,isMoved:void 0,currentX:void 0,currentY:void 0,minX:void 0,minY:void 0,maxX:void 0,maxY:void 0,width:void 0,height:void 0,startX:void 0,startY:void 0,touchesStart:{},touchesCurrent:{}},v={x:void 0,y:void 0,prevPositionX:void 0,prevPositionY:void 0,prevTime:void 0};let w,y=1;function E(){if(m.length<2)return 1;const e=m[0].pageX,t=m[0].pageY,s=m[1].pageX,a=m[1].pageY;return Math.sqrt((s-e)**2+(a-t)**2)}function S(){const t=e.params.zoom,s=h.imageWrapEl.getAttribute("data-swiper-zoom")||t.maxRatio;if(t.limitToOriginalSize&&h.imageEl&&h.imageEl.naturalWidth){const e=h.imageEl.naturalWidth/h.imageEl.offsetWidth;return Math.min(e,s)}return s}function T(t){const s=e.isElement?"swiper-slide":`.${e.params.slideClass}`;return!!t.target.matches(s)||e.slides.filter(e=>e.contains(t.target)).length>0}function M(t){const s=`.${e.params.zoom.containerClass}`;return!!t.target.matches(s)||[...e.hostEl.querySelectorAll(s)].filter(e=>e.contains(t.target)).length>0}function C(t){if("mouse"===t.pointerType&&m.splice(0,m.length),!T(t))return;const s=e.params.zoom;if(n=!1,l=!1,m.push(t),!(m.length<2)){if(n=!0,h.scaleStart=E(),!h.slideEl){h.slideEl=t.target.closest(`.${e.params.slideClass}, swiper-slide`),h.slideEl||(h.slideEl=e.slides[e.activeIndex]);let a=h.slideEl.querySelector(`.${s.containerClass}`);if(a&&(a=a.querySelectorAll("picture, img, svg, canvas, .swiper-zoom-target")[0]),h.imageEl=a,h.imageWrapEl=a?x(h.imageEl,`.${s.containerClass}`)[0]:void 0,!h.imageWrapEl)return void(h.imageEl=void 0);h.maxRatio=S()}if(h.imageEl){const[e,t]=function(){if(m.length<2)return{x:null,y:null};const e=h.imageEl.getBoundingClientRect();return[(m[0].pageX+(m[1].pageX-m[0].pageX)/2-e.x-i.scrollX)/o,(m[0].pageY+(m[1].pageY-m[0].pageY)/2-e.y-i.scrollY)/o]}();h.originX=e,h.originY=t,h.imageEl.style.transitionDuration="0ms"}c=!0}}function P(t){if(!T(t))return;const s=e.params.zoom,a=e.zoom,i=m.findIndex(e=>e.pointerId===t.pointerId);i>=0&&(m[i]=t),m.length<2||(l=!0,h.scaleMove=E(),h.imageEl&&(a.scale=h.scaleMove/h.scaleStart*o,a.scale>h.maxRatio&&(a.scale=h.maxRatio-1+(a.scale-h.maxRatio+1)**.5),a.scalee.pointerId===t.pointerId);i>=0&&m.splice(i,1),n&&l&&(n=!1,l=!1,h.imageEl&&(a.scale=Math.max(Math.min(a.scale,h.maxRatio),s.minRatio),h.imageEl.style.transitionDuration=`${e.params.speed}ms`,h.imageEl.style.transform=`translate3d(0,0,0) scale(${a.scale})`,o=a.scale,c=!1,a.scale>1&&h.slideEl?h.slideEl.classList.add(`${s.zoomedSlideClass}`):a.scale<=1&&h.slideEl&&h.slideEl.classList.remove(`${s.zoomedSlideClass}`),1===a.scale&&(h.originX=0,h.originY=0,h.slideEl=void 0)))}function I(){e.touchEventsData.preventTouchMoveFromPointerMove=!1}function z(t){const s="mouse"===t.pointerType&&e.params.zoom.panOnMouseMove;if(!T(t)||!M(t))return;const a=e.zoom;if(!h.imageEl)return;if(!f.isTouched||!h.slideEl)return void(s&&$(t));if(s)return void $(t);f.isMoved||(f.width=h.imageEl.offsetWidth||h.imageEl.clientWidth,f.height=h.imageEl.offsetHeight||h.imageEl.clientHeight,f.startX=d(h.imageWrapEl,"x")||0,f.startY=d(h.imageWrapEl,"y")||0,h.slideWidth=h.slideEl.offsetWidth,h.slideHeight=h.slideEl.offsetHeight,h.imageWrapEl.style.transitionDuration="0ms");const i=f.width*a.scale,r=f.height*a.scale;f.minX=Math.min(h.slideWidth/2-i/2,0),f.maxX=-f.minX,f.minY=Math.min(h.slideHeight/2-r/2,0),f.maxY=-f.minY,f.touchesCurrent.x=m.length>0?m[0].pageX:t.pageX,f.touchesCurrent.y=m.length>0?m[0].pageY:t.pageY;if(Math.max(Math.abs(f.touchesCurrent.x-f.touchesStart.x),Math.abs(f.touchesCurrent.y-f.touchesStart.y))>5&&(e.allowClick=!1),!f.isMoved&&!c){if(e.isHorizontal()&&(Math.floor(f.minX)===Math.floor(f.startX)&&f.touchesCurrent.xf.touchesStart.x))return f.isTouched=!1,void I();if(!e.isHorizontal()&&(Math.floor(f.minY)===Math.floor(f.startY)&&f.touchesCurrent.yf.touchesStart.y))return f.isTouched=!1,void I()}t.cancelable&&t.preventDefault(),t.stopPropagation(),clearTimeout(w),e.touchEventsData.preventTouchMoveFromPointerMove=!0,w=setTimeout(()=>{e.destroyed||I()}),f.isMoved=!0;const n=(a.scale-o)/(h.maxRatio-e.params.zoom.minRatio),{originX:l,originY:p}=h;f.currentX=f.touchesCurrent.x-f.touchesStart.x+f.startX+n*(f.width-2*l),f.currentY=f.touchesCurrent.y-f.touchesStart.y+f.startY+n*(f.height-2*p),f.currentXf.maxX&&(f.currentX=f.maxX-1+(f.currentX-f.maxX+1)**.8),f.currentYf.maxY&&(f.currentY=f.maxY-1+(f.currentY-f.maxY+1)**.8),v.prevPositionX||(v.prevPositionX=f.touchesCurrent.x),v.prevPositionY||(v.prevPositionY=f.touchesCurrent.y),v.prevTime||(v.prevTime=Date.now()),v.x=(f.touchesCurrent.x-v.prevPositionX)/(Date.now()-v.prevTime)/2,v.y=(f.touchesCurrent.y-v.prevPositionY)/(Date.now()-v.prevTime)/2,Math.abs(f.touchesCurrent.x-v.prevPositionX)<2&&(v.x=0),Math.abs(f.touchesCurrent.y-v.prevPositionY)<2&&(v.y=0),v.prevPositionX=f.touchesCurrent.x,v.prevPositionY=f.touchesCurrent.y,v.prevTime=Date.now(),h.imageWrapEl.style.transform=`translate3d(${f.currentX}px, ${f.currentY}px,0)`}function A(){const t=e.zoom;h.slideEl&&e.activeIndex!==e.slides.indexOf(h.slideEl)&&(h.imageEl&&(h.imageEl.style.transform="translate3d(0,0,0) scale(1)"),h.imageWrapEl&&(h.imageWrapEl.style.transform="translate3d(0,0,0)"),h.slideEl.classList.remove(`${e.params.zoom.zoomedSlideClass}`),t.scale=1,o=1,h.slideEl=void 0,h.imageEl=void 0,h.imageWrapEl=void 0,h.originX=0,h.originY=0)}function $(e){if(o<=1||!h.imageWrapEl)return;if(!T(e)||!M(e))return;const t=i.getComputedStyle(h.imageWrapEl).transform,s=new i.DOMMatrix(t);if(!p)return p=!0,u.x=e.clientX,u.y=e.clientY,f.startX=s.e,f.startY=s.f,f.width=h.imageEl.offsetWidth||h.imageEl.clientWidth,f.height=h.imageEl.offsetHeight||h.imageEl.clientHeight,h.slideWidth=h.slideEl.offsetWidth,void(h.slideHeight=h.slideEl.offsetHeight);const a=-3*(e.clientX-u.x),r=-3*(e.clientY-u.y),n=f.width*o,l=f.height*o,d=h.slideWidth,c=h.slideHeight,m=Math.min(d/2-n/2,0),g=-m,v=Math.min(c/2-l/2,0),w=-v,b=Math.max(Math.min(f.startX+a,g),m),y=Math.max(Math.min(f.startY+r,w),v);h.imageWrapEl.style.transitionDuration="0ms",h.imageWrapEl.style.transform=`translate3d(${b}px, ${y}px, 0)`,u.x=e.clientX,u.y=e.clientY,f.startX=b,f.startY=y,f.currentX=b,f.currentY=y}function k(t){const s=e.zoom,a=e.params.zoom;if(!h.slideEl){t&&t.target&&(h.slideEl=t.target.closest(`.${e.params.slideClass}, swiper-slide`)),h.slideEl||(e.params.virtual&&e.params.virtual.enabled&&e.virtual?h.slideEl=g(e.slidesEl,`.${e.params.slideActiveClass}`)[0]:h.slideEl=e.slides[e.activeIndex]);let s=h.slideEl.querySelector(`.${a.containerClass}`);s&&(s=s.querySelectorAll("picture, img, svg, canvas, .swiper-zoom-target")[0]),h.imageEl=s,h.imageWrapEl=s?x(h.imageEl,`.${a.containerClass}`)[0]:void 0}if(!h.imageEl||!h.imageWrapEl)return;let r,n,l,d,c,p,u,m,v,w,y,E,T,M,C,P,L,I;e.params.cssMode&&(e.wrapperEl.style.overflow="hidden",e.wrapperEl.style.touchAction="none"),h.slideEl.classList.add(`${a.zoomedSlideClass}`),void 0===f.touchesStart.x&&t?(r=t.pageX,n=t.pageY):(r=f.touchesStart.x,n=f.touchesStart.y);const z=o,A="number"==typeof t?t:null;1===o&&A&&(r=void 0,n=void 0,f.touchesStart.x=void 0,f.touchesStart.y=void 0);const $=S();s.scale=A||$,o=A||$,!t||1===o&&A?(u=0,m=0):(L=h.slideEl.offsetWidth,I=h.slideEl.offsetHeight,l=b(h.slideEl).left+i.scrollX,d=b(h.slideEl).top+i.scrollY,c=l+L/2-r,p=d+I/2-n,v=h.imageEl.offsetWidth||h.imageEl.clientWidth,w=h.imageEl.offsetHeight||h.imageEl.clientHeight,y=v*s.scale,E=w*s.scale,T=Math.min(L/2-y/2,0),M=Math.min(I/2-E/2,0),C=-T,P=-M,z>0&&A&&"number"==typeof f.currentX&&"number"==typeof f.currentY?(u=f.currentX*s.scale/z,m=f.currentY*s.scale/z):(u=c*s.scale,m=p*s.scale),uC&&(u=C),mP&&(m=P)),A&&1===s.scale&&(h.originX=0,h.originY=0),f.currentX=u,f.currentY=m,h.imageWrapEl.style.transitionDuration="300ms",h.imageWrapEl.style.transform=`translate3d(${u}px, ${m}px,0)`,h.imageEl.style.transitionDuration="300ms",h.imageEl.style.transform=`translate3d(0,0,0) scale(${s.scale})`}function O(){const t=e.zoom,s=e.params.zoom;if(!h.slideEl){e.params.virtual&&e.params.virtual.enabled&&e.virtual?h.slideEl=g(e.slidesEl,`.${e.params.slideActiveClass}`)[0]:h.slideEl=e.slides[e.activeIndex];let t=h.slideEl.querySelector(`.${s.containerClass}`);t&&(t=t.querySelectorAll("picture, img, svg, canvas, .swiper-zoom-target")[0]),h.imageEl=t,h.imageWrapEl=t?x(h.imageEl,`.${s.containerClass}`)[0]:void 0}h.imageEl&&h.imageWrapEl&&(e.params.cssMode&&(e.wrapperEl.style.overflow="",e.wrapperEl.style.touchAction=""),t.scale=1,o=1,f.currentX=void 0,f.currentY=void 0,f.touchesStart.x=void 0,f.touchesStart.y=void 0,h.imageWrapEl.style.transitionDuration="300ms",h.imageWrapEl.style.transform="translate3d(0,0,0)",h.imageEl.style.transitionDuration="300ms",h.imageEl.style.transform="translate3d(0,0,0) scale(1)",h.slideEl.classList.remove(`${s.zoomedSlideClass}`),h.slideEl=void 0,h.originX=0,h.originY=0,e.params.zoom.panOnMouseMove&&(u={x:0,y:0},p&&(p=!1,f.startX=0,f.startY=0)))}function D(t){const s=e.zoom;s.scale&&1!==s.scale?O():k(t)}function G(){return{passiveListener:!!e.params.passiveListeners&&{passive:!0,capture:!1},activeListenerWithCapture:!e.params.passiveListeners||{passive:!1,capture:!0}}}function X(){const t=e.zoom;if(t.enabled)return;t.enabled=!0;const{passiveListener:s,activeListenerWithCapture:a}=G();e.wrapperEl.addEventListener("pointerdown",C,s),e.wrapperEl.addEventListener("pointermove",P,a),["pointerup","pointercancel","pointerout"].forEach(t=>{e.wrapperEl.addEventListener(t,L,s)}),e.wrapperEl.addEventListener("pointermove",z,a)}function B(){const t=e.zoom;if(!t.enabled)return;t.enabled=!1;const{passiveListener:s,activeListenerWithCapture:a}=G();e.wrapperEl.removeEventListener("pointerdown",C,s),e.wrapperEl.removeEventListener("pointermove",P,a),["pointerup","pointercancel","pointerout"].forEach(t=>{e.wrapperEl.removeEventListener(t,L,s)}),e.wrapperEl.removeEventListener("pointermove",z,a)}Object.defineProperty(e.zoom,"scale",{get:()=>y,set(e){if(y!==e){const t=h.imageEl,s=h.slideEl;a("zoomChange",e,t,s)}y=e}}),s("init",()=>{e.params.zoom.enabled&&X()}),s("destroy",()=>{B()}),s("touchStart",(t,s)=>{e.zoom.enabled&&function(t){const s=e.device;if(!h.imageEl)return;if(f.isTouched)return;s.android&&t.cancelable&&t.preventDefault(),f.isTouched=!0;const a=m.length>0?m[0]:t;f.touchesStart.x=a.pageX,f.touchesStart.y=a.pageY}(s)}),s("touchEnd",(t,s)=>{e.zoom.enabled&&function(){const t=e.zoom;if(m.length=0,!h.imageEl)return;if(!f.isTouched||!f.isMoved)return f.isTouched=!1,void(f.isMoved=!1);f.isTouched=!1,f.isMoved=!1;let s=300,a=300;const i=v.x*s,r=f.currentX+i,n=v.y*a,l=f.currentY+n;0!==v.x&&(s=Math.abs((r-f.currentX)/v.x)),0!==v.y&&(a=Math.abs((l-f.currentY)/v.y));const o=Math.max(s,a);f.currentX=r,f.currentY=l;const d=f.width*t.scale,c=f.height*t.scale;f.minX=Math.min(h.slideWidth/2-d/2,0),f.maxX=-f.minX,f.minY=Math.min(h.slideHeight/2-c/2,0),f.maxY=-f.minY,f.currentX=Math.max(Math.min(f.currentX,f.maxX),f.minX),f.currentY=Math.max(Math.min(f.currentY,f.maxY),f.minY),h.imageWrapEl.style.transitionDuration=`${o}ms`,h.imageWrapEl.style.transform=`translate3d(${f.currentX}px, ${f.currentY}px,0)`}()}),s("doubleTap",(t,s)=>{!e.animating&&e.params.zoom.enabled&&e.zoom.enabled&&e.params.zoom.toggle&&D(s)}),s("transitionEnd",()=>{e.zoom.enabled&&e.params.zoom.enabled&&A()}),s("slideChange",()=>{e.zoom.enabled&&e.params.zoom.enabled&&e.params.cssMode&&A()}),Object.assign(e.zoom,{enable:X,disable:B,in:k,out:O,toggle:D})},function({swiper:e,extendParams:t,on:s}){function a(e,t){const s=function(){let e,t,s;return(a,i)=>{for(t=-1,e=a.length;e-t>1;)s=e+t>>1,a[s]<=i?t=s:e=s;return e}}();let a,i;return this.x=e,this.y=t,this.lastIndex=e.length-1,this.interpolate=function(e){return e?(i=s(this.x,e),a=i-1,(e-this.x[a])*(this.y[i]-this.y[a])/(this.x[i]-this.x[a])+this.y[a]):0},this}function i(){e.controller.control&&e.controller.spline&&(e.controller.spline=void 0,delete e.controller.spline)}t({controller:{control:void 0,inverse:!1,by:"slide"}}),e.controller={control:void 0},s("beforeInit",()=>{if("undefined"!=typeof window&&("string"==typeof e.params.controller.control||e.params.controller.control instanceof HTMLElement)){return void("string"==typeof e.params.controller.control?[...document.querySelectorAll(e.params.controller.control)]:[e.params.controller.control]).forEach(t=>{if(e.controller.control||(e.controller.control=[]),t&&t.swiper)e.controller.control.push(t.swiper);else if(t){const s=`${e.params.eventsPrefix}init`,a=i=>{e.controller.control.push(i.detail[0]),e.update(),t.removeEventListener(s,a)};t.addEventListener(s,a)}})}e.controller.control=e.params.controller.control}),s("update",()=>{i()}),s("resize",()=>{i()}),s("observerUpdate",()=>{i()}),s("setTranslate",(t,s,a)=>{e.controller.control&&!e.controller.control.destroyed&&e.controller.setTranslate(s,a)}),s("setTransition",(t,s,a)=>{e.controller.control&&!e.controller.control.destroyed&&e.controller.setTransition(s,a)}),Object.assign(e.controller,{setTranslate:function(t,s){const i=e.controller.control;let r,n;const l=e.constructor;function o(t){if(t.destroyed)return;const s=e.rtlTranslate?-e.translate:e.translate;"slide"===e.params.controller.by&&(!function(t){e.controller.spline=e.params.loop?new a(e.slidesGrid,t.slidesGrid):new a(e.snapGrid,t.snapGrid)}(t),n=-e.controller.spline.interpolate(-s)),n&&"container"!==e.params.controller.by||(r=(t.maxTranslate()-t.minTranslate())/(e.maxTranslate()-e.minTranslate()),!Number.isNaN(r)&&Number.isFinite(r)||(r=1),n=(s-e.minTranslate())*r+t.minTranslate()),e.params.controller.inverse&&(n=t.maxTranslate()-n),t.updateProgress(n),t.setTranslate(n,e),t.updateActiveIndex(),t.updateSlidesClasses()}if(Array.isArray(i))for(let e=0;e{s.updateAutoHeight()}),S(s.wrapperEl,()=>{i&&s.transitionEnd()})))}if(Array.isArray(i))for(r=0;r{e.setAttribute("tabIndex","0")})}function c(e){(e=M(e)).forEach(e=>{e.setAttribute("tabIndex","-1")})}function p(e,t){(e=M(e)).forEach(e=>{e.setAttribute("role",t)})}function u(e,t){(e=M(e)).forEach(e=>{e.setAttribute("aria-roledescription",t)})}function m(e,t){(e=M(e)).forEach(e=>{e.setAttribute("aria-label",t)})}function h(e){(e=M(e)).forEach(e=>{e.setAttribute("aria-disabled",!0)})}function f(e){(e=M(e)).forEach(e=>{e.setAttribute("aria-disabled",!1)})}function g(t){if(13!==t.keyCode&&32!==t.keyCode)return;const s=e.params.a11y,a=t.target;if(!e.pagination||!e.pagination.el||a!==e.pagination.el&&!e.pagination.el.contains(t.target)||t.target.matches(de(e.params.pagination.bulletClass))){if(e.navigation&&e.navigation.prevEl&&e.navigation.nextEl){const t=M(e.navigation.prevEl);M(e.navigation.nextEl).includes(a)&&(e.isEnd&&!e.params.loop||e.slideNext(),e.isEnd?o(s.lastSlideMessage):o(s.nextSlideMessage)),t.includes(a)&&(e.isBeginning&&!e.params.loop||e.slidePrev(),e.isBeginning?o(s.firstSlideMessage):o(s.prevSlideMessage))}e.pagination&&a.matches(de(e.params.pagination.bulletClass))&&a.click()}}function v(){return e.pagination&&e.pagination.bullets&&e.pagination.bullets.length}function b(){return v()&&e.params.pagination.clickable}const y=(e,t,s)=>{d(e),"BUTTON"!==e.tagName&&(p(e,"button"),e.addEventListener("keydown",g)),m(e,s),function(e,t){(e=M(e)).forEach(e=>{e.setAttribute("aria-controls",t)})}(e,t)},x=t=>{r&&r!==t.target&&!r.contains(t.target)&&(i=!0),e.a11y.clicked=!0},S=()=>{i=!1,requestAnimationFrame(()=>{requestAnimationFrame(()=>{e.destroyed||(e.a11y.clicked=!1)})})},T=e=>{l=(new Date).getTime()},C=t=>{if(e.a11y.clicked||!e.params.a11y.scrollOnFocus)return;if((new Date).getTime()-l<100)return;const s=t.target.closest(`.${e.params.slideClass}, swiper-slide`);if(!s||!e.slides.includes(s))return;r=s;const a=e.slides.indexOf(s)===e.activeIndex,n=e.params.watchSlidesProgress&&e.visibleSlides&&e.visibleSlides.includes(s);a||n||t.sourceCapabilities&&t.sourceCapabilities.firesTouchEvents||(e.isHorizontal()?e.el.scrollLeft=0:e.el.scrollTop=0,requestAnimationFrame(()=>{i||(e.params.loop?e.slideToLoop(e.getSlideIndexWhenGrid(parseInt(s.getAttribute("data-swiper-slide-index"))),0):e.slideTo(e.getSlideIndexWhenGrid(e.slides.indexOf(s)),0),i=!1)}))},L=()=>{const t=e.params.a11y;t.itemRoleDescriptionMessage&&u(e.slides,t.itemRoleDescriptionMessage),t.slideRole&&p(e.slides,t.slideRole);const s=e.slides.length;t.slideLabelMessage&&e.slides.forEach((a,i)=>{const r=e.params.loop?parseInt(a.getAttribute("data-swiper-slide-index"),10):i;m(a,t.slideLabelMessage.replace(/\{\{index\}\}/,r+1).replace(/\{\{slidesLength\}\}/,s))})},I=()=>{const t=e.params.a11y;e.el.append(n);const s=e.el;t.containerRoleDescriptionMessage&&u(s,t.containerRoleDescriptionMessage),t.containerMessage&&m(s,t.containerMessage),t.containerRole&&p(s,t.containerRole);const i=e.wrapperEl,r=t.id||i.getAttribute("id")||`swiper-wrapper-${function(e=16){return"x".repeat(e).replace(/x/g,()=>Math.round(16*Math.random()).toString(16))}(16)}`;var l;if(l=r,M(i).forEach(e=>{e.setAttribute("id",l)}),t.wrapperLiveRegion){!function(e,t){(e=M(e)).forEach(e=>{e.setAttribute("aria-live",t)})}(i,e.params.autoplay&&e.params.autoplay.enabled?"off":"polite")}L();let{nextEl:o,prevEl:d}=e.navigation?e.navigation:{};if(o=M(o),d=M(d),o&&o.forEach(e=>y(e,r,t.nextSlideMessage)),d&&d.forEach(e=>y(e,r,t.prevSlideMessage)),b()){M(e.pagination.el).forEach(e=>{e.addEventListener("keydown",g)})}a().addEventListener("visibilitychange",T),e.el.addEventListener("focus",C,!0),e.el.addEventListener("focus",C,!0),e.el.addEventListener("pointerdown",x,!0),e.el.addEventListener("pointerup",S,!0)};s("beforeInit",()=>{n=w("span",e.params.a11y.notificationClass),n.setAttribute("aria-live","assertive"),n.setAttribute("aria-atomic","true")}),s("afterInit",()=>{e.params.a11y.enabled&&I()}),s("slidesLengthChange snapGridLengthChange slidesGridLengthChange",()=>{e.params.a11y.enabled&&L()}),s("fromEdge toEdge afterInit lock unlock",()=>{e.params.a11y.enabled&&function(){if(e.params.loop||e.params.rewind||!e.navigation)return;const{nextEl:t,prevEl:s}=e.navigation;s&&(e.isBeginning?(h(s),c(s)):(f(s),d(s))),t&&(e.isEnd?(h(t),c(t)):(f(t),d(t)))}()}),s("paginationUpdate",()=>{e.params.a11y.enabled&&function(){const t=e.params.a11y;v()&&e.pagination.bullets.forEach(s=>{e.params.pagination.clickable&&(d(s),e.params.pagination.renderBullet||(p(s,"button"),m(s,t.paginationBulletMessage.replace(/\{\{index\}\}/,E(s)+1)))),s.matches(de(e.params.pagination.bulletActiveClass))?s.setAttribute("aria-current","true"):s.removeAttribute("aria-current")})}()}),s("destroy",()=>{e.params.a11y.enabled&&function(){n&&n.remove();let{nextEl:t,prevEl:s}=e.navigation?e.navigation:{};t=M(t),s=M(s),t&&t.forEach(e=>e.removeEventListener("keydown",g)),s&&s.forEach(e=>e.removeEventListener("keydown",g)),b()&&M(e.pagination.el).forEach(e=>{e.removeEventListener("keydown",g)});a().removeEventListener("visibilitychange",T),e.el&&"string"!=typeof e.el&&(e.el.removeEventListener("focus",C,!0),e.el.removeEventListener("pointerdown",x,!0),e.el.removeEventListener("pointerup",S,!0))}()})},function({swiper:e,extendParams:t,on:s}){t({history:{enabled:!1,root:"",replaceState:!1,key:"slides",keepQuery:!1}});let a=!1,i={};const n=e=>e.toString().replace(/\s+/g,"-").replace(/[^\w-]+/g,"").replace(/--+/g,"-").replace(/^-+/,"").replace(/-+$/,""),l=e=>{const t=r();let s;s=e?new URL(e):t.location;const a=s.pathname.slice(1).split("/").filter(e=>""!==e),i=a.length;return{key:a[i-2],value:a[i-1]}},o=(t,s)=>{const i=r();if(!a||!e.params.history.enabled)return;let l;l=e.params.url?new URL(e.params.url):i.location;const o=e.virtual&&e.params.virtual.enabled?e.slidesEl.querySelector(`[data-swiper-slide-index="${s}"]`):e.slides[s];let d=n(o.getAttribute("data-history"));if(e.params.history.root.length>0){let s=e.params.history.root;"/"===s[s.length-1]&&(s=s.slice(0,s.length-1)),d=`${s}/${t?`${t}/`:""}${d}`}else l.pathname.includes(t)||(d=`${t?`${t}/`:""}${d}`);e.params.history.keepQuery&&(d+=l.search);const c=i.history.state;c&&c.value===d||(e.params.history.replaceState?i.history.replaceState({value:d},null,d):i.history.pushState({value:d},null,d))},d=(t,s,a)=>{if(s)for(let i=0,r=e.slides.length;i{i=l(e.params.url),d(e.params.speed,i.value,!1)};s("init",()=>{e.params.history.enabled&&(()=>{const t=r();if(e.params.history){if(!t.history||!t.history.pushState)return e.params.history.enabled=!1,void(e.params.hashNavigation.enabled=!0);a=!0,i=l(e.params.url),i.key||i.value?(d(0,i.value,e.params.runCallbacksOnInit),e.params.history.replaceState||t.addEventListener("popstate",c)):e.params.history.replaceState||t.addEventListener("popstate",c)}})()}),s("destroy",()=>{e.params.history.enabled&&(()=>{const t=r();e.params.history.replaceState||t.removeEventListener("popstate",c)})()}),s("transitionEnd _freeModeNoMomentumRelease",()=>{a&&o(e.params.history.key,e.activeIndex)}),s("slideChange",()=>{a&&e.params.cssMode&&o(e.params.history.key,e.activeIndex)})},function({swiper:e,extendParams:t,emit:s,on:i}){let n=!1;const l=a(),o=r();t({hashNavigation:{enabled:!1,replaceState:!1,watchState:!1,getSlideIndex(t,s){if(e.virtual&&e.params.virtual.enabled){const t=e.slides.find(e=>e.getAttribute("data-hash")===s);if(!t)return 0;return parseInt(t.getAttribute("data-swiper-slide-index"),10)}return e.getSlideIndex(g(e.slidesEl,`.${e.params.slideClass}[data-hash="${s}"], swiper-slide[data-hash="${s}"]`)[0])}}});const d=()=>{s("hashChange");const t=l.location.hash.replace("#",""),a=e.virtual&&e.params.virtual.enabled?e.slidesEl.querySelector(`[data-swiper-slide-index="${e.activeIndex}"]`):e.slides[e.activeIndex];if(t!==(a?a.getAttribute("data-hash"):"")){const s=e.params.hashNavigation.getSlideIndex(e,t);if(void 0===s||Number.isNaN(s))return;e.slideTo(s)}},c=()=>{if(!n||!e.params.hashNavigation.enabled)return;const t=e.virtual&&e.params.virtual.enabled?e.slidesEl.querySelector(`[data-swiper-slide-index="${e.activeIndex}"]`):e.slides[e.activeIndex],a=t?t.getAttribute("data-hash")||t.getAttribute("data-history"):"";e.params.hashNavigation.replaceState&&o.history&&o.history.replaceState?(o.history.replaceState(null,null,`#${a}`||""),s("hashSet")):(l.location.hash=a||"",s("hashSet"))};i("init",()=>{e.params.hashNavigation.enabled&&(()=>{if(!e.params.hashNavigation.enabled||e.params.history&&e.params.history.enabled)return;n=!0;const t=l.location.hash.replace("#","");if(t){const s=0,a=e.params.hashNavigation.getSlideIndex(e,t);e.slideTo(a||0,s,e.params.runCallbacksOnInit,!0)}e.params.hashNavigation.watchState&&o.addEventListener("hashchange",d)})()}),i("destroy",()=>{e.params.hashNavigation.enabled&&e.params.hashNavigation.watchState&&o.removeEventListener("hashchange",d)}),i("transitionEnd _freeModeNoMomentumRelease",()=>{n&&c()}),i("slideChange",()=>{n&&e.params.cssMode&&c()})},function({swiper:e,extendParams:t,on:s,emit:i,params:r}){let n,l;e.autoplay={running:!1,paused:!1,timeLeft:0},t({autoplay:{enabled:!1,delay:3e3,waitForTransition:!0,disableOnInteraction:!1,stopOnLastSlide:!1,reverseDirection:!1,pauseOnMouseEnter:!1}});let o,d,c,p,u,m,h,f,g=r&&r.autoplay?r.autoplay.delay:3e3,v=r&&r.autoplay?r.autoplay.delay:3e3,w=(new Date).getTime();function b(t){e&&!e.destroyed&&e.wrapperEl&&t.target===e.wrapperEl&&(e.wrapperEl.removeEventListener("transitionend",b),f||t.detail&&t.detail.bySwiperTouchMove||M())}const y=()=>{if(e.destroyed||!e.autoplay.running)return;e.autoplay.paused?d=!0:d&&(v=o,d=!1);const t=e.autoplay.paused?o:w+v-(new Date).getTime();e.autoplay.timeLeft=t,i("autoplayTimeLeft",t,t/g),l=requestAnimationFrame(()=>{y()})},E=t=>{if(e.destroyed||!e.autoplay.running)return;cancelAnimationFrame(l),y();let s=void 0===t?e.params.autoplay.delay:t;g=e.params.autoplay.delay,v=e.params.autoplay.delay;const a=(()=>{let t;if(t=e.virtual&&e.params.virtual.enabled?e.slides.find(e=>e.classList.contains("swiper-slide-active")):e.slides[e.activeIndex],!t)return;return parseInt(t.getAttribute("data-swiper-autoplay"),10)})();!Number.isNaN(a)&&a>0&&void 0===t&&(s=a,g=a,v=a),o=s;const r=e.params.speed,d=()=>{e&&!e.destroyed&&(e.params.autoplay.reverseDirection?!e.isBeginning||e.params.loop||e.params.rewind?(e.slidePrev(r,!0,!0),i("autoplay")):e.params.autoplay.stopOnLastSlide||(e.slideTo(e.slides.length-1,r,!0,!0),i("autoplay")):!e.isEnd||e.params.loop||e.params.rewind?(e.slideNext(r,!0,!0),i("autoplay")):e.params.autoplay.stopOnLastSlide||(e.slideTo(0,r,!0,!0),i("autoplay")),e.params.cssMode&&(w=(new Date).getTime(),requestAnimationFrame(()=>{E()})))};return s>0?(clearTimeout(n),n=setTimeout(()=>{d()},s)):requestAnimationFrame(()=>{d()}),s},x=()=>{w=(new Date).getTime(),e.autoplay.running=!0,E(),i("autoplayStart")},S=()=>{e.autoplay.running=!1,clearTimeout(n),cancelAnimationFrame(l),i("autoplayStop")},T=(t,s)=>{if(e.destroyed||!e.autoplay.running)return;clearTimeout(n),t||(h=!0);const a=()=>{i("autoplayPause"),e.params.autoplay.waitForTransition?e.wrapperEl.addEventListener("transitionend",b):M()};if(e.autoplay.paused=!0,s)return m&&(o=e.params.autoplay.delay),m=!1,void a();const r=o||e.params.autoplay.delay;o=r-((new Date).getTime()-w),e.isEnd&&o<0&&!e.params.loop||(o<0&&(o=0),a())},M=()=>{e.isEnd&&o<0&&!e.params.loop||e.destroyed||!e.autoplay.running||(w=(new Date).getTime(),h?(h=!1,E(o)):E(),e.autoplay.paused=!1,i("autoplayResume"))},C=()=>{if(e.destroyed||!e.autoplay.running)return;const t=a();"hidden"===t.visibilityState&&(h=!0,T(!0)),"visible"===t.visibilityState&&M()},P=t=>{"mouse"===t.pointerType&&(h=!0,f=!0,e.animating||e.autoplay.paused||T(!0))},L=t=>{"mouse"===t.pointerType&&(f=!1,e.autoplay.paused&&M())};s("init",()=>{e.params.autoplay.enabled&&(e.params.autoplay.pauseOnMouseEnter&&(e.el.addEventListener("pointerenter",P),e.el.addEventListener("pointerleave",L)),a().addEventListener("visibilitychange",C),x())}),s("destroy",()=>{e.el&&"string"!=typeof e.el&&(e.el.removeEventListener("pointerenter",P),e.el.removeEventListener("pointerleave",L)),a().removeEventListener("visibilitychange",C),e.autoplay.running&&S()}),s("_freeModeStaticRelease",()=>{(p||h)&&M()}),s("_freeModeNoMomentumRelease",()=>{e.params.autoplay.disableOnInteraction?S():T(!0,!0)}),s("beforeTransitionStart",(t,s,a)=>{!e.destroyed&&e.autoplay.running&&(a||!e.params.autoplay.disableOnInteraction?T(!0,!0):S())}),s("sliderFirstMove",()=>{!e.destroyed&&e.autoplay.running&&(e.params.autoplay.disableOnInteraction?S():(c=!0,p=!1,h=!1,u=setTimeout(()=>{h=!0,p=!0,T(!0)},200)))}),s("touchEnd",()=>{if(!e.destroyed&&e.autoplay.running&&c){if(clearTimeout(u),clearTimeout(n),e.params.autoplay.disableOnInteraction)return p=!1,void(c=!1);p&&e.params.cssMode&&M(),p=!1,c=!1}}),s("slideChange",()=>{!e.destroyed&&e.autoplay.running&&(m=!0)}),Object.assign(e.autoplay,{start:x,stop:S,pause:T,resume:M})},function({swiper:e,extendParams:t,on:s}){t({thumbs:{swiper:null,multipleActiveThumbs:!0,autoScrollOffset:0,slideThumbActiveClass:"swiper-slide-thumb-active",thumbsContainerClass:"swiper-thumbs"}});let i=!1,r=!1;function n(){const t=e.thumbs.swiper;if(!t||t.destroyed)return;const s=t.clickedIndex,a=t.clickedSlide;if(a&&a.classList.contains(e.params.thumbs.slideThumbActiveClass))return;if(null==s)return;let i;i=t.params.loop?parseInt(t.clickedSlide.getAttribute("data-swiper-slide-index"),10):s,e.params.loop?e.slideToLoop(i):e.slideTo(i)}function l(){const{thumbs:t}=e.params;if(i)return!1;i=!0;const s=e.constructor;if(t.swiper instanceof s){if(t.swiper.destroyed)return i=!1,!1;e.thumbs.swiper=t.swiper,Object.assign(e.thumbs.swiper.originalParams,{watchSlidesProgress:!0,slideToClickedSlide:!1}),Object.assign(e.thumbs.swiper.params,{watchSlidesProgress:!0,slideToClickedSlide:!1}),e.thumbs.swiper.update()}else if(c(t.swiper)){const a=Object.assign({},t.swiper);Object.assign(a,{watchSlidesProgress:!0,slideToClickedSlide:!1}),e.thumbs.swiper=new s(a),r=!0}return e.thumbs.swiper.el.classList.add(e.params.thumbs.thumbsContainerClass),e.thumbs.swiper.on("tap",n),!0}function o(t){const s=e.thumbs.swiper;if(!s||s.destroyed)return;const a="auto"===s.params.slidesPerView?s.slidesPerViewDynamic():s.params.slidesPerView;let i=1;const r=e.params.thumbs.slideThumbActiveClass;if(e.params.slidesPerView>1&&!e.params.centeredSlides&&(i=e.params.slidesPerView),e.params.thumbs.multipleActiveThumbs||(i=1),i=Math.floor(i),s.slides.forEach(e=>e.classList.remove(r)),s.params.loop||s.params.virtual&&s.params.virtual.enabled)for(let t=0;t{e.classList.add(r)});else for(let t=0;tt.getAttribute("data-swiper-slide-index")===`${e.realIndex}`);r=s.slides.indexOf(t),o=e.activeIndex>e.previousIndex?"next":"prev"}else r=e.realIndex,o=r>e.previousIndex?"next":"prev";l&&(r+="next"===o?n:-1*n),s.visibleSlidesIndexes&&s.visibleSlidesIndexes.indexOf(r)<0&&(s.params.centeredSlides?r=r>i?r-Math.floor(a/2)+1:r+Math.floor(a/2)-1:r>i&&s.params.slidesPerGroup,s.slideTo(r,t?0:void 0))}}e.thumbs={swiper:null},s("beforeInit",()=>{const{thumbs:t}=e.params;if(t&&t.swiper)if("string"==typeof t.swiper||t.swiper instanceof HTMLElement){const s=a(),i=()=>{const a="string"==typeof t.swiper?s.querySelector(t.swiper):t.swiper;if(a&&a.swiper)t.swiper=a.swiper,l(),o(!0);else if(a){const s=`${e.params.eventsPrefix}init`,i=r=>{t.swiper=r.detail[0],a.removeEventListener(s,i),l(),o(!0),t.swiper.update(),e.update()};a.addEventListener(s,i)}return a},r=()=>{if(e.destroyed)return;i()||requestAnimationFrame(r)};requestAnimationFrame(r)}else l(),o(!0)}),s("slideChange update resize observerUpdate",()=>{o()}),s("setTransition",(t,s)=>{const a=e.thumbs.swiper;a&&!a.destroyed&&a.setTransition(s)}),s("beforeDestroy",()=>{const t=e.thumbs.swiper;t&&!t.destroyed&&r&&t.destroy()}),Object.assign(e.thumbs,{init:l,update:o})},function({swiper:e,extendParams:t,emit:s,once:a}){t({freeMode:{enabled:!1,momentum:!0,momentumRatio:1,momentumBounce:!0,momentumBounceRatio:1,momentumVelocityRatio:1,sticky:!1,minimumVelocity:.02}}),Object.assign(e,{freeMode:{onTouchStart:function(){if(e.params.cssMode)return;const t=e.getTranslate();e.setTranslate(t),e.setTransition(0),e.touchEventsData.velocities.length=0,e.freeMode.onTouchEnd({currentPos:e.rtl?e.translate:-e.translate})},onTouchMove:function(){if(e.params.cssMode)return;const{touchEventsData:t,touches:s}=e;0===t.velocities.length&&t.velocities.push({position:s[e.isHorizontal()?"startX":"startY"],time:t.touchStartTime}),t.velocities.push({position:s[e.isHorizontal()?"currentX":"currentY"],time:o()})},onTouchEnd:function({currentPos:t}){if(e.params.cssMode)return;const{params:i,wrapperEl:r,rtlTranslate:n,snapGrid:l,touchEventsData:d}=e,c=o()-d.touchStartTime;if(t<-e.minTranslate())e.slideTo(e.activeIndex);else if(t>-e.maxTranslate())e.slides.length1){const t=d.velocities.pop(),s=d.velocities.pop(),a=t.position-s.position,r=t.time-s.time;e.velocity=a/r,e.velocity/=2,Math.abs(e.velocity)150||o()-t.time>300)&&(e.velocity=0)}else e.velocity=0;e.velocity*=i.freeMode.momentumVelocityRatio,d.velocities.length=0;let t=1e3*i.freeMode.momentumRatio;const c=e.velocity*t;let p=e.translate+c;n&&(p=-p);let u,m=!1;const h=20*Math.abs(e.velocity)*i.freeMode.momentumBounceRatio;let f;if(pe.minTranslate())i.freeMode.momentumBounce?(p-e.minTranslate()>h&&(p=e.minTranslate()+h),u=e.minTranslate(),m=!0,d.allowMomentumBounce=!0):p=e.minTranslate(),i.loop&&i.centeredSlides&&(f=!0);else if(i.freeMode.sticky){let t;for(let e=0;e-p){t=e;break}p=Math.abs(l[t]-p){e.loopFix()}),0!==e.velocity){if(t=n?Math.abs((-p-e.translate)/e.velocity):Math.abs((p-e.translate)/e.velocity),i.freeMode.sticky){const s=Math.abs((n?-p:p)-e.translate),a=e.slidesSizesGrid[e.activeIndex];t=s{e&&!e.destroyed&&d.allowMomentumBounce&&(s("momentumBounce"),e.setTransition(i.speed),setTimeout(()=>{e.setTranslate(u),S(r,()=>{e&&!e.destroyed&&e.transitionEnd()})},0))})):e.velocity?(s("_freeModeNoMomentumRelease"),e.updateProgress(p),e.setTransition(t),e.setTranslate(p),e.transitionStart(!0,e.swipeDirection),e.animating||(e.animating=!0,S(r,()=>{e&&!e.destroyed&&e.transitionEnd()}))):e.updateProgress(p),e.updateActiveIndex(),e.updateSlidesClasses()}else{if(i.freeMode.sticky)return void e.slideToClosest();i.freeMode&&s("_freeModeNoMomentumRelease")}(!i.freeMode.momentum||c>=i.longSwipesMs)&&(s("_freeModeStaticRelease"),e.updateProgress(),e.updateActiveIndex(),e.updateSlidesClasses())}}}})},function({swiper:e,extendParams:t,on:s}){let a,i,r,n;t({grid:{rows:1,fill:"column"}});const l=()=>{let t=e.params.spaceBetween;return"string"==typeof t&&t.indexOf("%")>=0?t=parseFloat(t.replace("%",""))/100*e.size:"string"==typeof t&&(t=parseFloat(t)),t};s("init",()=>{n=e.params.grid&&e.params.grid.rows>1}),s("update",()=>{const{params:t,el:s}=e,a=t.grid&&t.grid.rows>1;n&&!a?(s.classList.remove(`${t.containerModifierClass}grid`,`${t.containerModifierClass}grid-column`),r=1,e.emitContainerClasses()):!n&&a&&(s.classList.add(`${t.containerModifierClass}grid`),"column"===t.grid.fill&&s.classList.add(`${t.containerModifierClass}grid-column`),e.emitContainerClasses()),n=a}),e.grid={initSlides:t=>{const{slidesPerView:s}=e.params,{rows:n,fill:l}=e.params.grid,o=e.virtual&&e.params.virtual.enabled?e.virtual.slides.length:t.length;r=Math.floor(o/n),a=Math.floor(o/n)===o/n?o:Math.ceil(o/n)*n,"auto"!==s&&"row"===l&&(a=Math.max(a,s*n)),i=a/n},unsetSlides:()=>{e.slides&&e.slides.forEach(t=>{t.swiperSlideGridSet&&(t.style.height="",t.style[e.getDirectionLabel("margin-top")]="")})},updateSlide:(t,s,n)=>{const{slidesPerGroup:o}=e.params,d=l(),{rows:c,fill:p}=e.params.grid,u=e.virtual&&e.params.virtual.enabled?e.virtual.slides.length:n.length;let m,h,f;if("row"===p&&o>1){const e=Math.floor(t/(o*c)),i=t-c*o*e,r=0===e?o:Math.min(Math.ceil((u-e*c*o)/c),o);f=Math.floor(i/r),h=i-f*r+e*o,m=h+f*a/c,s.style.order=m}else"column"===p?(h=Math.floor(t/c),f=t-h*c,(h>r||h===r&&f===c-1)&&(f+=1,f>=c&&(f=0,h+=1))):(f=Math.floor(t/i),h=t-f*i);s.row=f,s.column=h,s.style.height=`calc((100% - ${(c-1)*d}px) / ${c})`,s.style[e.getDirectionLabel("margin-top")]=0!==f?d&&`${d}px`:"",s.swiperSlideGridSet=!0},updateWrapperSize:(t,s)=>{const{centeredSlides:i,roundLengths:r}=e.params,n=l(),{rows:o}=e.params.grid;if(e.virtualSize=(t+n)*a,e.virtualSize=Math.ceil(e.virtualSize/o)-n,e.params.cssMode||(e.wrapperEl.style[e.getDirectionLabel("width")]=`${e.virtualSize+n}px`),i){const t=[];for(let a=0;a{const{slides:t}=e;e.params.fadeEffect;for(let s=0;s{const s=e.slides.map(e=>f(e));s.forEach(e=>{e.style.transitionDuration=`${t}ms`}),ve({swiper:e,duration:t,transformElements:s,allSlides:!0})},overwriteParams:()=>({slidesPerView:1,slidesPerGroup:1,watchSlidesProgress:!0,spaceBetween:0,virtualTranslate:!e.params.cssMode})})},function({swiper:e,extendParams:t,on:s}){t({cubeEffect:{slideShadows:!0,shadow:!0,shadowOffset:20,shadowScale:.94}});const a=(e,t,s)=>{let a=s?e.querySelector(".swiper-slide-shadow-left"):e.querySelector(".swiper-slide-shadow-top"),i=s?e.querySelector(".swiper-slide-shadow-right"):e.querySelector(".swiper-slide-shadow-bottom");a||(a=w("div",("swiper-slide-shadow-cube swiper-slide-shadow-"+(s?"left":"top")).split(" ")),e.append(a)),i||(i=w("div",("swiper-slide-shadow-cube swiper-slide-shadow-"+(s?"right":"bottom")).split(" ")),e.append(i)),a&&(a.style.opacity=Math.max(-t,0)),i&&(i.style.opacity=Math.max(t,0))};fe({effect:"cube",swiper:e,on:s,setTranslate:()=>{const{el:t,wrapperEl:s,slides:i,width:r,height:n,rtlTranslate:l,size:o,browser:d}=e,c=C(e),p=e.params.cubeEffect,u=e.isHorizontal(),m=e.virtual&&e.params.virtual.enabled;let h,f=0;p.shadow&&(u?(h=e.wrapperEl.querySelector(".swiper-cube-shadow"),h||(h=w("div","swiper-cube-shadow"),e.wrapperEl.append(h)),h.style.height=`${r}px`):(h=t.querySelector(".swiper-cube-shadow"),h||(h=w("div","swiper-cube-shadow"),t.append(h))));for(let e=0;e-1&&(f=90*s+90*d,l&&(f=90*-s-90*d)),t.style.transform=w,p.slideShadows&&a(t,d,u)}if(s.style.transformOrigin=`50% 50% -${o/2}px`,s.style["-webkit-transform-origin"]=`50% 50% -${o/2}px`,p.shadow)if(u)h.style.transform=`translate3d(0px, ${r/2+p.shadowOffset}px, ${-r/2}px) rotateX(89.99deg) rotateZ(0deg) scale(${p.shadowScale})`;else{const e=Math.abs(f)-90*Math.floor(Math.abs(f)/90),t=1.5-(Math.sin(2*e*Math.PI/360)/2+Math.cos(2*e*Math.PI/360)/2),s=p.shadowScale,a=p.shadowScale/t,i=p.shadowOffset;h.style.transform=`scale3d(${s}, 1, ${a}) translate3d(0px, ${n/2+i}px, ${-n/2/a}px) rotateX(-89.99deg)`}const g=(d.isSafari||d.isWebView)&&d.needPerspectiveFix?-o/2:0;s.style.transform=`translate3d(0px,0,${g}px) rotateX(${c(e.isHorizontal()?0:f)}deg) rotateY(${c(e.isHorizontal()?-f:0)}deg)`,s.style.setProperty("--swiper-cube-translate-z",`${g}px`)},setTransition:t=>{const{el:s,slides:a}=e;if(a.forEach(e=>{e.style.transitionDuration=`${t}ms`,e.querySelectorAll(".swiper-slide-shadow-top, .swiper-slide-shadow-right, .swiper-slide-shadow-bottom, .swiper-slide-shadow-left").forEach(e=>{e.style.transitionDuration=`${t}ms`})}),e.params.cubeEffect.shadow&&!e.isHorizontal()){const e=s.querySelector(".swiper-cube-shadow");e&&(e.style.transitionDuration=`${t}ms`)}},recreateShadows:()=>{const t=e.isHorizontal();e.slides.forEach(e=>{const s=Math.max(Math.min(e.progress,1),-1);a(e,s,t)})},getEffectParams:()=>e.params.cubeEffect,perspective:()=>!0,overwriteParams:()=>({slidesPerView:1,slidesPerGroup:1,watchSlidesProgress:!0,resistanceRatio:0,spaceBetween:0,centeredSlides:!1,virtualTranslate:!0})})},function({swiper:e,extendParams:t,on:s}){t({flipEffect:{slideShadows:!0,limitRotation:!0}});const a=(t,s)=>{let a=e.isHorizontal()?t.querySelector(".swiper-slide-shadow-left"):t.querySelector(".swiper-slide-shadow-top"),i=e.isHorizontal()?t.querySelector(".swiper-slide-shadow-right"):t.querySelector(".swiper-slide-shadow-bottom");a||(a=we("flip",t,e.isHorizontal()?"left":"top")),i||(i=we("flip",t,e.isHorizontal()?"right":"bottom")),a&&(a.style.opacity=Math.max(-s,0)),i&&(i.style.opacity=Math.max(s,0))};fe({effect:"flip",swiper:e,on:s,setTranslate:()=>{const{slides:t,rtlTranslate:s}=e,i=e.params.flipEffect,r=C(e);for(let n=0;n{const s=e.slides.map(e=>f(e));s.forEach(e=>{e.style.transitionDuration=`${t}ms`,e.querySelectorAll(".swiper-slide-shadow-top, .swiper-slide-shadow-right, .swiper-slide-shadow-bottom, .swiper-slide-shadow-left").forEach(e=>{e.style.transitionDuration=`${t}ms`})}),ve({swiper:e,duration:t,transformElements:s})},recreateShadows:()=>{e.params.flipEffect,e.slides.forEach(t=>{let s=t.progress;e.params.flipEffect.limitRotation&&(s=Math.max(Math.min(t.progress,1),-1)),a(t,s)})},getEffectParams:()=>e.params.flipEffect,perspective:()=>!0,overwriteParams:()=>({slidesPerView:1,slidesPerGroup:1,watchSlidesProgress:!0,spaceBetween:0,virtualTranslate:!e.params.cssMode})})},function({swiper:e,extendParams:t,on:s}){t({coverflowEffect:{rotate:50,stretch:0,depth:100,scale:1,modifier:1,slideShadows:!0}}),fe({effect:"coverflow",swiper:e,on:s,setTranslate:()=>{const{width:t,height:s,slides:a,slidesSizesGrid:i}=e,r=e.params.coverflowEffect,n=e.isHorizontal(),l=e.translate,o=n?t/2-l:s/2-l,d=n?r.rotate:-r.rotate,c=r.depth,p=C(e);for(let e=0,t=a.length;e0?u:0),s&&(s.style.opacity=-u>0?-u:0)}}},setTransition:t=>{e.slides.map(e=>f(e)).forEach(e=>{e.style.transitionDuration=`${t}ms`,e.querySelectorAll(".swiper-slide-shadow-top, .swiper-slide-shadow-right, .swiper-slide-shadow-bottom, .swiper-slide-shadow-left").forEach(e=>{e.style.transitionDuration=`${t}ms`})})},perspective:()=>!0,overwriteParams:()=>({watchSlidesProgress:!0})})},function({swiper:e,extendParams:t,on:s}){t({creativeEffect:{limitProgress:1,shadowPerProgress:!1,progressMultiplier:1,perspective:!0,prev:{translate:[0,0,0],rotate:[0,0,0],opacity:1,scale:1},next:{translate:[0,0,0],rotate:[0,0,0],opacity:1,scale:1}}});const a=e=>"string"==typeof e?e:`${e}px`;fe({effect:"creative",swiper:e,on:s,setTranslate:()=>{const{slides:t,wrapperEl:s,slidesSizesGrid:i}=e,r=e.params.creativeEffect,{progressMultiplier:n}=r,l=e.params.centeredSlides,o=C(e);if(l){const t=i[0]/2-e.params.slidesOffsetBefore||0;s.style.transform=`translateX(calc(50% - ${t}px))`}for(let s=0;s0&&(g=r.prev,f=!0),m.forEach((e,t)=>{m[t]=`calc(${e}px + (${a(g.translate[t])} * ${Math.abs(c*n)}))`}),h.forEach((e,t)=>{let s=g.rotate[t]*Math.abs(c*n);h[t]=s}),i.style.zIndex=-Math.abs(Math.round(d))+t.length;const v=m.join(", "),w=`rotateX(${o(h[0])}deg) rotateY(${o(h[1])}deg) rotateZ(${o(h[2])}deg)`,b=p<0?`scale(${1+(1-g.scale)*p*n})`:`scale(${1-(1-g.scale)*p*n})`,y=p<0?1+(1-g.opacity)*p*n:1-(1-g.opacity)*p*n,E=`translate3d(${v}) ${w} ${b}`;if(f&&g.shadow||!f){let e=i.querySelector(".swiper-slide-shadow");if(!e&&g.shadow&&(e=we("creative",i)),e){const t=r.shadowPerProgress?c*(1/r.limitProgress):c;e.style.opacity=Math.min(Math.max(Math.abs(t),0),1)}}const x=ge(0,i);x.style.transform=E,x.style.opacity=y,g.origin&&(x.style.transformOrigin=g.origin)}},setTransition:t=>{const s=e.slides.map(e=>f(e));s.forEach(e=>{e.style.transitionDuration=`${t}ms`,e.querySelectorAll(".swiper-slide-shadow").forEach(e=>{e.style.transitionDuration=`${t}ms`})}),ve({swiper:e,duration:t,transformElements:s,allSlides:!0})},perspective:()=>e.params.creativeEffect.perspective,overwriteParams:()=>({watchSlidesProgress:!0,virtualTranslate:!e.params.cssMode})})},function({swiper:e,extendParams:t,on:s}){t({cardsEffect:{slideShadows:!0,rotate:!0,perSlideRotate:2,perSlideOffset:8}}),fe({effect:"cards",swiper:e,on:s,setTranslate:()=>{const{slides:t,activeIndex:s,rtlTranslate:a}=e,i=e.params.cardsEffect,{startTranslate:r,isTouched:n}=e.touchEventsData,l=a?-e.translate:e.translate;for(let o=0;o0&&p<1&&(n||e.params.cssMode)&&l-1&&(n||e.params.cssMode)&&l>r;if(y||E){const t=(1-Math.abs((Math.abs(p)-.5)/.5))**.5;v+=-28*p*t,g+=-.5*t,w+=96*t,h=(i.rotate||e.isHorizontal()?-25:0)*t*Math.abs(p)+"%"}if(m=p<0?`calc(${m}px ${a?"-":"+"} (${w*Math.abs(p)}%))`:p>0?`calc(${m}px ${a?"-":"+"} (-${w*Math.abs(p)}%))`:`${m}px`,!e.isHorizontal()){const e=h;h=m,m=e}const x=p<0?""+(1+(1-g)*p):""+(1-(1-g)*p),S=`\n translate3d(${m}, ${h}, ${f}px)\n rotateZ(${i.rotate?a?-v:v:0}deg)\n scale(${x})\n `;if(i.slideShadows){let e=d.querySelector(".swiper-slide-shadow");e||(e=we("cards",d)),e&&(e.style.opacity=Math.min(Math.max((Math.abs(p)-.5)/.5,0),1))}d.style.zIndex=-Math.abs(Math.round(c))+t.length;ge(0,d).style.transform=S}},setTransition:t=>{const s=e.slides.map(e=>f(e));s.forEach(e=>{e.style.transitionDuration=`${t}ms`,e.querySelectorAll(".swiper-slide-shadow").forEach(e=>{e.style.transitionDuration=`${t}ms`})}),ve({swiper:e,duration:t,transformElements:s})},perspective:()=>!0,overwriteParams:()=>({_loopSwapReset:!1,watchSlidesProgress:!0,loopAdditionalSlides:e.params.cardsEffect.rotate?3:2,centeredSlides:!0,virtualTranslate:!e.params.cssMode})})}];return ne.use(be),ne}(); +//# sourceMappingURL=swiper-bundle.min.js.map \ No newline at end of file diff --git a/sections/product.liquid b/sections/product.liquid index d46307f3f..31d3e7d2d 100644 --- a/sections/product.liquid +++ b/sections/product.liquid @@ -1,13 +1,32 @@ -
- {% for image in product.images %} - {% render 'image', class: 'product-image', image: image %} - {% endfor %} -
+ +{{ 'swiper-bundle.min.css' | asset_url | stylesheet_tag }} -
-

{{ product.title }}

-

{{ product.price | money }}

-

{{ product.description }}

+
+
+ + +
+
+
+

{{ product.title }}

+

{{ product.price | money }}

+

{{ product.description }}

+
+
@@ -34,6 +53,29 @@ {% endform %}
+{% stylesheet %} + .product-main { + display: grid; + grid-template-columns: repeat(2, 50%); + gap: 2rem; + } + + .product-gallery-nav { + display: grid; + grid-template-columns: repeat(auto-fit, 10rem); + gap: 1rem; + margin-top: 1rem; + } +{% endstylesheet %} + +{% javascript %} + document.addEventListener('DOMContentLoaded', function () { + new Swiper('.product-images', { + slidesPerView: 1, + }); + }); +{% endjavascript %} + {% schema %} { "name": "t:general.product", From 3d749b333062ab2479f60db40edba7562c42af6b Mon Sep 17 00:00:00 2001 From: MisterDetour Date: Wed, 26 Aug 2026 12:34:19 -0700 Subject: [PATCH 005/110] Working on pdp --- TODO.md | 6 +- config/settings_schema.json | 6 +- locales/en.default.schema.json | 1 - sections/header.liquid | 2 +- sections/product.liquid | 157 +++++++++++++++++++++++-------- snippets/product-form.liquid | 22 +++++ snippets/variant-selector.liquid | 10 ++ templates/product.json | 29 ++++++ 8 files changed, 186 insertions(+), 47 deletions(-) create mode 100644 snippets/product-form.liquid create mode 100644 snippets/variant-selector.liquid diff --git a/TODO.md b/TODO.md index 19072a065..c8577c0ac 100644 --- a/TODO.md +++ b/TODO.md @@ -1,7 +1,8 @@ # To Dos -- PDP +- PDP - Clean up existing files & code +- Color swatches - Cart drawer - Collection (quick add to cart) - Cart @@ -14,7 +15,10 @@ - Header & Footer styles should be moved to critical - Review class names on all exisiting code - Go through each template file +- Multiple currencies/markets - Review all Dawn settings to see what we might want - See how apps work with in general - Theme Blocks https://shopify.dev/docs/storefronts/themes/architecture/blocks/theme-blocks/quick-start?framework=liquid - Modify readme +- Work on performance +- html validation diff --git a/config/settings_schema.json b/config/settings_schema.json index 8d3b532d1..c84c76ae0 100644 --- a/config/settings_schema.json +++ b/config/settings_schema.json @@ -31,15 +31,15 @@ "label": "t:labels.page_width", "options": [ { - "value": "90rem", + "value": "120rem", "label": "t:options.page_width.narrow" }, { - "value": "110rem", + "value": "150rem", "label": "t:options.page_width.wide" } ], - "default": "90rem" + "default": "120rem" }, { "type": "range", diff --git a/locales/en.default.schema.json b/locales/en.default.schema.json index 2dfbc2f7d..5157208bd 100644 --- a/locales/en.default.schema.json +++ b/locales/en.default.schema.json @@ -22,7 +22,6 @@ "page": "Page", "password": "Password", "primary": "Primary", - "product": "Product", "row": "Row", "search": "Search", "secondary": "Secondary", diff --git a/sections/header.liquid b/sections/header.liquid index d873bdda7..f0cee7624 100644 --- a/sections/header.liquid +++ b/sections/header.liquid @@ -48,7 +48,7 @@ max-width: var(--page-margin); } header svg { - width: 2rem; + width: 3rem; } header .header__menu, header .header__icons { diff --git a/sections/product.liquid b/sections/product.liquid index 31d3e7d2d..3fb722ba8 100644 --- a/sections/product.liquid +++ b/sections/product.liquid @@ -12,47 +12,38 @@ {% endfor %}
-
-

{{ product.title }}

-

{{ product.price | money }}

-

{{ product.description }}

+ {%- for block in section.blocks -%} + {%- case block.type -%} + {%- when '@app' -%} + {% render block %} + {%- when 'title' -%} +

{{ product.title }}

+ {%- when 'description' -%} +
{{ product.description }}
+ {%- when 'price' -%} +
{{ product.selected_or_first_available_variant.price | money }}
+ {%- when 'form' -%} + {% render 'product-form', product: product %} + {%- when 'variant_selector' -%} + {% render 'variant-selector', product: product %} + {%- endcase -%} + {%- endfor -%}
-
- {% form 'product', product %} - {% assign current_variant = product.selected_or_first_available_variant %} - - - - - - - {{ form | payment_button }} - {% endform %} -
- {% stylesheet %} .product-main { display: grid; @@ -61,27 +52,111 @@ } .product-gallery-nav { - display: grid; - grid-template-columns: repeat(auto-fit, 10rem); - gap: 1rem; margin-top: 1rem; } + + .product-gallery-nav .swiper-slide { + width: 10rem; + cursor: pointer; + } {% endstylesheet %} -{% javascript %} + {% schema %} { - "name": "t:general.product", + "name": "Product", "settings": [], "disabled_on": { "groups": ["header", "footer"] - } + }, + "blocks": [ + { + "type": "@app" + }, + { + "type": "title", + "name": "Title", + "limit": 1 + }, + { + "type": "description", + "name": "Description", + "limit": 1 + }, + { + "type": "price", + "name": "Price", + "limit": 1 + }, + { + "type": "form", + "name": "Product Form", + "limit": 1 + }, + { + "type": "variant_selector", + "name": "Variant Selector", + "limit": 1 + } + ] } {% endschema %} diff --git a/snippets/product-form.liquid b/snippets/product-form.liquid new file mode 100644 index 000000000..381618ad6 --- /dev/null +++ b/snippets/product-form.liquid @@ -0,0 +1,22 @@ +
+ {% form 'product', product %} + {% assign current_variant = product.selected_or_first_available_variant %} + + + + + + + {% endform %} +
diff --git a/snippets/variant-selector.liquid b/snippets/variant-selector.liquid new file mode 100644 index 000000000..e1c37caa4 --- /dev/null +++ b/snippets/variant-selector.liquid @@ -0,0 +1,10 @@ +
+ {%- for option in product.options -%} + + + {%- endfor -%} +
diff --git a/templates/product.json b/templates/product.json index 8329ddc68..ee52cf9bc 100644 --- a/templates/product.json +++ b/templates/product.json @@ -11,6 +11,35 @@ "sections": { "main": { "type": "product", + "blocks": { + "title_4eBUym": { + "type": "title", + "settings": {} + }, + "description_iBVXD6": { + "type": "description", + "settings": {} + }, + "price_drzA33": { + "type": "price", + "settings": {} + }, + "variant_selector_QdpqhD": { + "type": "variant_selector", + "settings": {} + }, + "form_XXfMhU": { + "type": "form", + "settings": {} + } + }, + "block_order": [ + "title_4eBUym", + "description_iBVXD6", + "price_drzA33", + "variant_selector_QdpqhD", + "form_XXfMhU" + ], "settings": {} } }, From 8a6b296da63bcded0f01d3ef3637753fceef5852 Mon Sep 17 00:00:00 2001 From: MisterDetour Date: Wed, 26 Aug 2026 14:33:36 -0700 Subject: [PATCH 006/110] Stub out cart drawer --- TODO.md | 5 ++--- layout/theme.liquid | 2 ++ sections/collection.liquid | 9 +-------- sections/product.liquid | 24 +++++++++++++++++++++--- snippets/cart-drawer.liquid | 22 ++++++++++++++++++++++ 5 files changed, 48 insertions(+), 14 deletions(-) create mode 100644 snippets/cart-drawer.liquid diff --git a/TODO.md b/TODO.md index c8577c0ac..3e155efbe 100644 --- a/TODO.md +++ b/TODO.md @@ -1,9 +1,8 @@ # To Dos -- PDP +- Cart drawer (add setting to turn on/off) - Clean up existing files & code - Color swatches -- Cart drawer - Collection (quick add to cart) - Cart - Mobile menu @@ -14,7 +13,7 @@ - Password page - Header & Footer styles should be moved to critical - Review class names on all exisiting code -- Go through each template file +- Go through each template file - https://shopify.dev/docs/storefronts/themes/architecture/templates - Multiple currencies/markets - Review all Dawn settings to see what we might want - See how apps work with in general diff --git a/layout/theme.liquid b/layout/theme.liquid index 0e3fd39ca..6dc91d886 100644 --- a/layout/theme.liquid +++ b/layout/theme.liquid @@ -31,6 +31,8 @@ {{ content_for_layout }} + {%- render 'cart-drawer' -%} + {% sections 'footer-group' %} diff --git a/sections/collection.liquid b/sections/collection.liquid index 9b7ecaded..d77ac0f4f 100644 --- a/sections/collection.liquid +++ b/sections/collection.liquid @@ -5,14 +5,7 @@ {% for product in collection.products %}
{% if product.featured_image %} - {% render 'image', - class: 'collection-product__image', - image: product.featured_image, - url: product.url, - width: 400, - height: 400, - crop: 'center' - %} + {% render 'image', image: product.featured_image, url: product.url, width: 400, height: 400, crop: 'center' %} {% endif %}

{{ product.title | escape | link_to: product.url }}

diff --git a/sections/product.liquid b/sections/product.liquid index 3fb722ba8..04a2539eb 100644 --- a/sections/product.liquid +++ b/sections/product.liquid @@ -113,10 +113,28 @@ variantSelect.addEventListener('change', updateProductPrice); } - document.querySelectorAll('.product-option-select').forEach((select) => { - select.addEventListener('change', () => { - alert('test'); + // Update variant based on selected options + function updateVariantFromOptions() { + const selectedOptions = Array.from(document.querySelectorAll('.product-option-select')).map((select) => select.value); + + const matchingVariant = productVariants.find((variant) => { + return variant.options && variant.options.length === selectedOptions.length && + variant.options.every((option, index) => option === selectedOptions[index]); }); + + if (!matchingVariant) return; + + variantSelect.value = String(matchingVariant.id); + updateProductPrice(); + } + + document.querySelectorAll('.product-option-select').forEach((select) => { + select.addEventListener('change', updateVariantFromOptions); + }); + + document.querySelector('.product-form').addEventListener('submit', (e) => { + e.preventDefault(); + alert('Form submitted'); }); }); diff --git a/snippets/cart-drawer.liquid b/snippets/cart-drawer.liquid new file mode 100644 index 000000000..d133229ec --- /dev/null +++ b/snippets/cart-drawer.liquid @@ -0,0 +1,22 @@ +
+ {% if cart.item_count > 0 %} +
    + {% for item in cart.items %} +
  • + {% if item.image %} + {% render 'image', image: item.image, url: item.url, width: 120, height: 120, crop: 'center' %} + {% endif %} + +
    +

    {{ item.product.title }}

    +

    {{ item.variant.title }}

    +

    {{ item.price | money }}

    +

    Qty: {{ item.quantity }}

    +
    +
  • + {% endfor %} +
+ {% else %} +

Your cart is empty.

+ {% endif %} +
From 326399d1d0f14a6bc32d859d3fe694f8b0d1aee9 Mon Sep 17 00:00:00 2001 From: MisterDetour Date: Wed, 26 Aug 2026 14:55:10 -0700 Subject: [PATCH 007/110] Basic cart drawer functionality --- TODO.md | 2 + sections/product.liquid | 51 +++++++++++++++++--- snippets/cart-drawer.liquid | 92 ++++++++++++++++++++++++++++-------- snippets/product-form.liquid | 2 +- 4 files changed, 120 insertions(+), 27 deletions(-) diff --git a/TODO.md b/TODO.md index 3e155efbe..eab7caa30 100644 --- a/TODO.md +++ b/TODO.md @@ -2,6 +2,8 @@ - Cart drawer (add setting to turn on/off) - Clean up existing files & code +- Cart icon in header needs to update count on ajax add to cart +- Open cart drawer when clicking on cart icon in header - Color swatches - Collection (quick add to cart) - Cart diff --git a/sections/product.liquid b/sections/product.liquid index 04a2539eb..c7b52d881 100644 --- a/sections/product.liquid +++ b/sections/product.liquid @@ -62,7 +62,7 @@ {% endstylesheet %} diff --git a/snippets/cart-drawer.liquid b/snippets/cart-drawer.liquid index d133229ec..cff24102e 100644 --- a/snippets/cart-drawer.liquid +++ b/snippets/cart-drawer.liquid @@ -1,22 +1,74 @@ -
- {% if cart.item_count > 0 %} -
    - {% for item in cart.items %} -
  • - {% if item.image %} - {% render 'image', image: item.image, url: item.url, width: 120, height: 120, crop: 'center' %} - {% endif %} +
    +
    + {% if cart.item_count > 0 %} +
      + {% for item in cart.items %} +
    • + {% if item.image %} + {% render 'image', image: item.image, url: item.url, width: 120, height: 120, crop: 'center' %} + {% endif %} -
      -

      {{ item.product.title }}

      -

      {{ item.variant.title }}

      -

      {{ item.price | money }}

      -

      Qty: {{ item.quantity }}

      -
      -
    • - {% endfor %} -
    - {% else %} -

    Your cart is empty.

    - {% endif %} +
    +

    {{ item.product.title }}

    +

    {{ item.variant.title }}

    +

    {{ item.price | money }}

    +

    Qty: {{ item.quantity }}

    +
    +
  • + {% endfor %} +
+ {% else %} +

Your cart is empty.

+ {% endif %} +
+ +{% stylesheet %} + .cart-drawer-container { + visibility: hidden; + position: fixed; + z-index: 1000; + left: 0; + top: 0; + width: 100vw; + height: 100%; + display: flex; + justify-content: flex-end; + background-color: rgba(0, 0, 0, 0.5); + transition: visibility 0.3s ease; + } + + .cart-drawer { + height: 100%; + width: 40rem; + max-width: calc(100vw - 3rem); + padding: 0 1.5rem; + border: 0.1rem solid rgba(0, 0, 0, 0.2); + border-right: 0; + overflow: hidden; + display: flex; + flex-direction: column; + background: var(--color-background); + transform: translate(100%); + transition: transform 0.3s ease; + } + + .cart-drawer-container.active { + visibility: visible; + } + + .cart-drawer-container.active .cart-drawer { + transform: translate(0); + } +{% endstylesheet %} + +{% javascript %} + document.addEventListener('DOMContentLoaded', () => { + // Close cart drawer when clicking outside of it + document.querySelector('.cart-drawer-container').addEventListener('click', (e) => { + if (e.target === document.querySelector('.cart-drawer-container')) { + document.querySelector('.cart-drawer-container').classList.remove('active'); + } + }); + }); +{% endjavascript %} diff --git a/snippets/product-form.liquid b/snippets/product-form.liquid index 381618ad6..bc1b4986c 100644 --- a/snippets/product-form.liquid +++ b/snippets/product-form.liquid @@ -17,6 +17,6 @@ - + {% endform %}
From 55a4a07dce561980a9dda0df460fd073c9e04125 Mon Sep 17 00:00:00 2001 From: MisterDetour Date: Thu, 27 Aug 2026 09:57:58 -0700 Subject: [PATCH 008/110] Review all files --- TODO.md | 11 ++++++++-- blocks/group.liquid | 16 +++++++-------- blocks/text.liquid | 14 +++++++------ locales/en.default.schema.json | 1 + sections/collection.liquid | 2 +- sections/collections.liquid | 17 +++++++++------- sections/custom-section.liquid | 16 +++++++++------ sections/footer.liquid | 18 ++++++++-------- sections/header.liquid | 34 +++++++++++++++++++++---------- sections/product.liquid | 35 +++++++++++++++++++++++--------- snippets/variant-selector.liquid | 2 +- templates/list-collections.json | 5 ++++- templates/product.json | 20 +++++++++--------- 13 files changed, 120 insertions(+), 71 deletions(-) diff --git a/TODO.md b/TODO.md index eab7caa30..a20d48d6b 100644 --- a/TODO.md +++ b/TODO.md @@ -1,7 +1,9 @@ # To Dos -- Cart drawer (add setting to turn on/off) +- Figure out translation stuff - config & locales - Clean up existing files & code +- Cart drawer (add setting to turn on/off) +- Cart quantity isn't correct after ajax add - Cart icon in header needs to update count on ajax add to cart - Open cart drawer when clicking on cart icon in header - Color swatches @@ -19,7 +21,12 @@ - Multiple currencies/markets - Review all Dawn settings to see what we might want - See how apps work with in general -- Theme Blocks https://shopify.dev/docs/storefronts/themes/architecture/blocks/theme-blocks/quick-start?framework=liquid +- Theme Blocks https://shopify.dev/docs/storefronts/themes/architecture/blocks/theme-blocks/quick-start?framework=liquid (deal with custom-section.liquid & blocks folder) - Modify readme - Work on performance - html validation +- Custom solution for product gallery instead of swiper? +- Make sure all section previews work +- Image sizes attributes +- Remove comments from main layout file? +- Add title elements to svg's diff --git a/blocks/group.liquid b/blocks/group.liquid index baea31c58..8d59e8648 100644 --- a/blocks/group.liquid +++ b/blocks/group.liquid @@ -30,14 +30,14 @@ width: 100%; } - .group--horizontal { + .group-horizontal { flex-direction: row; justify-content: space-between; align-items: center; padding: 0 var(--padding); } - .group--vertical { + .group-vertical { flex-direction: column; align-items: var(--alignment); padding: var(--padding) 0; @@ -53,14 +53,14 @@ "type": "select", "id": "layout_direction", "label": "t:labels.layout_direction", - "default": "group--vertical", + "default": "group-vertical", "options": [ - { "value": "group--horizontal", "label": "t:options.direction.horizontal" }, - { "value": "group--vertical", "label": "t:options.direction.vertical" } + { "value": "group-horizontal", "label": "t:options.direction.horizontal" }, + { "value": "group-vertical", "label": "t:options.direction.vertical" } ] }, { - "visible_if": "{{ block.settings.layout_direction == 'group--vertical' }}", + "visible_if": "{{ block.settings.layout_direction == 'group-vertical' }}", "type": "select", "id": "alignment", "label": "t:labels.alignment", @@ -87,7 +87,7 @@ "name": "t:general.column", "category": "t:general.layout", "settings": { - "layout_direction": "group--vertical", + "layout_direction": "group-vertical", "alignment": "flex-start", "padding": 0 } @@ -96,7 +96,7 @@ "name": "t:general.row", "category": "t:general.layout", "settings": { - "layout_direction": "group--horizontal", + "layout_direction": "group-horizontal", "padding": 0 } } diff --git a/blocks/text.liquid b/blocks/text.liquid index b985c3894..c8c32584a 100644 --- a/blocks/text.liquid +++ b/blocks/text.liquid @@ -17,11 +17,13 @@ .text { text-align: var(--text-align); } - .text--title { + + .text-title { font-size: 2rem; font-weight: 700; } - .text--subtitle { + + .text-subtitle { font-size: 1.5rem; } {% endstylesheet %} @@ -41,11 +43,11 @@ "id": "text_style", "label": "t:labels.text_style", "options": [ - { "value": "text--title", "label": "t:options.text_style.title" }, - { "value": "text--subtitle", "label": "t:options.text_style.subtitle" }, - { "value": "text--normal", "label": "t:options.text_style.normal" } + { "value": "text-title", "label": "t:options.text_style.title" }, + { "value": "text-subtitle", "label": "t:options.text_style.subtitle" }, + { "value": "text-normal", "label": "t:options.text_style.normal" } ], - "default": "text--title" + "default": "text-title" }, { "type": "text_alignment", diff --git a/locales/en.default.schema.json b/locales/en.default.schema.json index 5157208bd..2dfbc2f7d 100644 --- a/locales/en.default.schema.json +++ b/locales/en.default.schema.json @@ -22,6 +22,7 @@ "page": "Page", "password": "Password", "primary": "Primary", + "product": "Product", "row": "Row", "search": "Search", "secondary": "Secondary", diff --git a/sections/collection.liquid b/sections/collection.liquid index d77ac0f4f..e84b779e7 100644 --- a/sections/collection.liquid +++ b/sections/collection.liquid @@ -7,7 +7,7 @@ {% if product.featured_image %} {% render 'image', image: product.featured_image, url: product.url, width: 400, height: 400, crop: 'center' %} {% endif %} -
+

{{ product.title | escape | link_to: product.url }}

{{ product.price | money }}

diff --git a/sections/collections.liquid b/sections/collections.liquid index e0e099fd5..96f87e65c 100644 --- a/sections/collections.liquid +++ b/sections/collections.liquid @@ -8,7 +8,7 @@ {% if collection.featured_image %} {% render 'image', - class: 'collection-card__image', + class: 'collection-card-image', image: collection.featured_image, width: 600, height: 600, @@ -16,7 +16,7 @@ %} {% endif %} -
+

{{ collection.title }}

{% if collection.description %} @@ -33,12 +33,15 @@ grid-template-columns: repeat(auto-fill, minmax(min(var(--collection-card-size), 100%), 1fr)); gap: var(--grid-gap); } - .collections--compact { + + .collections-compact { --collection-card-size: 160px; } - .collections--full { + + .collections-full { --collection-card-size: 280px; } + .collection-card { display: flex; flex-direction: column; @@ -55,10 +58,10 @@ "id": "grid_item_width", "label": "t:labels.grid_item_width", "options": [ - { "value": "collections--compact", "label": "t:options.size.small" }, - { "value": "collections--full", "label": "t:options.size.large" } + { "value": "collections-compact", "label": "t:options.size.small" }, + { "value": "collections-full", "label": "t:options.size.large" } ], - "default": "collections--full" + "default": "collections-full" }, { "type": "range", diff --git a/sections/custom-section.liquid b/sections/custom-section.liquid index 529d497bf..efc4edec9 100644 --- a/sections/custom-section.liquid +++ b/sections/custom-section.liquid @@ -1,11 +1,11 @@
{% if section.settings.background_image %} -
+
{{ section.settings.background_image | image_url: width: 2000 | image_tag }}
{% endif %} -
+
{% content_for 'blocks' %}
@@ -16,14 +16,16 @@ overflow: hidden; width: 100%; } - .custom-section__background { + + .custom-section-bg { position: absolute; width: 100%; height: 100%; z-index: -1; overflow: hidden; } - .custom-section__background img { + + .custom-section-bg img { position: absolute; width: 100%; height: auto; @@ -31,11 +33,13 @@ left: 50%; transform: translate(-50%, -50%); } - .custom-section__content { + + .custom-section-content { display: grid; grid-template-columns: var(--content-grid); } - .custom-section__content > * { + + .custom-section-content > * { grid-column: 2; } {% endstylesheet %} diff --git a/sections/footer.liquid b/sections/footer.liquid index 66e6f0978..52379dfef 100644 --- a/sections/footer.liquid +++ b/sections/footer.liquid @@ -1,17 +1,17 @@ -
+ +{% stylesheet %} + .product-option { + display: grid; + justify-content: flex-start; + } +{% endstylesheet %} From c3d30bbf30525d5d2ef5acbad1069bfafa72eb96 Mon Sep 17 00:00:00 2001 From: MisterDetour Date: Fri, 25 Sep 2026 08:57:28 -0700 Subject: [PATCH 037/110] Working on pdp --- TODO.md | 4 +- config/settings_schema.json | 2 +- sections/product.liquid | 80 +++++++++++++++++++++++--------- snippets/product-form.liquid | 22 --------- snippets/variant-selector.liquid | 70 +++++++++++++++++++++++++--- templates/product.json | 16 +++---- 6 files changed, 133 insertions(+), 61 deletions(-) delete mode 100644 snippets/product-form.liquid diff --git a/TODO.md b/TODO.md index ae33ded73..769c15ef0 100644 --- a/TODO.md +++ b/TODO.md @@ -2,7 +2,6 @@ - Product - Search - - Password - Collection List - Collection @@ -15,6 +14,7 @@ # MVP +- Make sure pdp out of stock is working - Display active variant image in gallery - Password page subscribe form validation and testing - Add pagination and filters to collection page @@ -24,6 +24,7 @@ - Responsive cart drawer styles and better styles overall - If you quickly update the quantity twice it fails - Form spam protection? +- Go through all templates again # Complex Functionality - create skills for @@ -32,6 +33,7 @@ - Predictive search - Multiple currencies/markets - Subscription app support +- Related products # Sections diff --git a/config/settings_schema.json b/config/settings_schema.json index 95ff022a3..80400bac3 100644 --- a/config/settings_schema.json +++ b/config/settings_schema.json @@ -92,7 +92,7 @@ "step": 1, "unit": "px", "label": "t:labels.button_corner_radius", - "default": 4 + "default": 0 } ] }, diff --git a/sections/product.liquid b/sections/product.liquid index bc527857f..79bf15464 100644 --- a/sections/product.liquid +++ b/sections/product.liquid @@ -24,22 +24,43 @@
- {%- for block in section.blocks -%} - {%- case block.type -%} - {%- when '@app' -%} - {% render block %} - {%- when 'title' -%} -

{{ product.title }}

- {%- when 'description' -%} -
{{ product.description }}
- {%- when 'price' -%} - {% render 'price' %} - {%- when 'form' -%} - {% render 'product-form', product: product %} - {%- when 'variant_selector' -%} - {% render 'variant-selector', product: product %} - {%- endcase -%} - {%- endfor -%} +
+ {% form 'product', product %} + {%- for block in section.blocks -%} + {%- case block.type -%} + {%- when '@app' -%} + {% render block %} + {%- when 'title' -%} +

{{ product.title }}

+ {%- when 'description' -%} +
{{ product.description }}
+ {%- when 'price' -%} + {% render 'price' %} + {%- when 'buy_button' -%} +
+ +
+ {%- when 'quantity' -%} + + {%- when 'variant_selector' -%} + {% render 'variant-selector', product: product %} + {%- endcase -%} + {%- endfor -%} + + + {% endform %} +
@@ -51,6 +72,11 @@ gap: 2rem; } + .shopify-product-form { + display: grid; + gap: 1rem; + } + .product-price { display: flex; align-items: baseline; @@ -166,9 +192,12 @@ // Update variant based on selected options function updateVariantFromOptions() { - const selectedOptions = Array.from(document.querySelectorAll('.product-option-select')).map( - (select) => select.value - ); + const selectedOptions = Array.from(document.querySelectorAll('.product-option-group')) + .map((group) => { + const checkedInput = group.querySelector('.product-option-input:checked'); + return checkedInput ? checkedInput.value : null; + }) + .filter(Boolean); const matchingVariant = productVariants.find((variant) => { return ( @@ -185,8 +214,8 @@ updateProductPrice(); } - document.querySelectorAll('.product-option-select').forEach((select) => { - select.addEventListener('change', updateVariantFromOptions); + document.querySelectorAll('.product-option-input').forEach((input) => { + input.addEventListener('change', updateVariantFromOptions); }); }); {% endjavascript %} @@ -218,8 +247,13 @@ "limit": 1 }, { - "type": "form", - "name": "Product Form", + "type": "quantity", + "name": "Quantity", + "limit": 1 + }, + { + "type": "buy_button", + "name": "Buy Button", "limit": 1 }, { diff --git a/snippets/product-form.liquid b/snippets/product-form.liquid deleted file mode 100644 index bc1b4986c..000000000 --- a/snippets/product-form.liquid +++ /dev/null @@ -1,22 +0,0 @@ -
- {% form 'product', product %} - {% assign current_variant = product.selected_or_first_available_variant %} - - - - - - - {% endform %} -
diff --git a/snippets/variant-selector.liquid b/snippets/variant-selector.liquid index ce2d70dc2..1e6028b74 100644 --- a/snippets/variant-selector.liquid +++ b/snippets/variant-selector.liquid @@ -1,17 +1,75 @@ +{% assign selected_variant = product.selected_or_first_available_variant %} +
{%- for option in product.options -%} - - +
+ +
+ {% for value in product.options_with_values[forloop.index0].values %} + + + {% endfor %} +
+
{%- endfor -%}
{% stylesheet %} .product-option { display: grid; + gap: 1rem; justify-content: flex-start; } + + .product-option-group { + display: grid; + gap: 0.5rem; + } + + .product-option-values { + display: flex; + flex-wrap: wrap; + gap: 0.5rem; + } + + .product-option-input { + position: absolute; + opacity: 0; + pointer-events: none; + } + + .product-option-button { + display: inline-flex; + align-items: center; + justify-content: center; + font-size: 1.4rem; + min-width: 4rem; + padding: 0.6rem 1rem; + border: 1px solid var(--color-border); + border-radius: 999px; + background: var(--color-background); + color: var(--color-foreground); + cursor: pointer; + transition: all 0.2s ease; + } + + .product-option-input:checked + .product-option-button { + background: var(--color-foreground); + border-color: var(--color-foreground); + color: var(--color-background); + } {% endstylesheet %} diff --git a/templates/product.json b/templates/product.json index 02a9652f2..7ad16a96c 100644 --- a/templates/product.json +++ b/templates/product.json @@ -16,10 +16,6 @@ "type": "title", "settings": {} }, - "description": { - "type": "description", - "settings": {} - }, "price": { "type": "price", "settings": {} @@ -28,17 +24,21 @@ "type": "variant_selector", "settings": {} }, - "form": { - "type": "form", + "button": { + "type": "buy_button", + "settings": {} + }, + "description": { + "type": "description", "settings": {} } }, "block_order": [ "title", - "description", "price", "variant_selector", - "form" + "button", + "description" ], "settings": {} } From 17cba5fe50341b049043544bbc2fdcdeb5d18b0c Mon Sep 17 00:00:00 2001 From: MisterDetour Date: Fri, 25 Sep 2026 09:26:21 -0700 Subject: [PATCH 038/110] Cleanup variant change js --- TODO.md | 1 - assets/global.css | 5 +++++ sections/product.liquid | 50 ++++++++++++++++++++++++++++++++++------- 3 files changed, 47 insertions(+), 9 deletions(-) diff --git a/TODO.md b/TODO.md index 769c15ef0..f47af63a5 100644 --- a/TODO.md +++ b/TODO.md @@ -14,7 +14,6 @@ # MVP -- Make sure pdp out of stock is working - Display active variant image in gallery - Password page subscribe form validation and testing - Add pagination and filters to collection page diff --git a/assets/global.css b/assets/global.css index 0a511ee83..e389b7300 100644 --- a/assets/global.css +++ b/assets/global.css @@ -268,6 +268,11 @@ h6 { cursor: pointer; } +.btn:disabled { + cursor: not-allowed; + opacity: 0.5; +} + /* --------------- Utility Classes --------------- */ .btn-reset { diff --git a/sections/product.liquid b/sections/product.liquid index 79bf15464..ace6ff72c 100644 --- a/sections/product.liquid +++ b/sections/product.liquid @@ -37,8 +37,21 @@ {%- when 'price' -%} {% render 'price' %} {%- when 'buy_button' -%} -
- +
+
{%- when 'quantity' -%} @@ -161,12 +174,21 @@ return '$' + (cents / 100).toFixed(2); } - function updateProductPrice() { - if (!variantSelect || !priceCurrentEl) return; + function updateBuyButtonState(selectedVariant) { + const buyButton = document.querySelector('.product-buy-button'); - const selectedVariant = productVariants.find((variant) => Number(variant.id) === Number(variantSelect.value)); + if (!buyButton) return; - if (!selectedVariant) return; + const isAvailable = !!selectedVariant && selectedVariant.available !== false; + const label = isAvailable ? 'Add to cart' : 'Sold out'; + + buyButton.disabled = !isAvailable; + buyButton.setAttribute('aria-disabled', String(!isAvailable)); + buyButton.textContent = label; + } + + function updatePriceDisplay(selectedVariant) { + if (!priceCurrentEl) return; priceCurrentEl.textContent = formatMoney(selectedVariant.price); @@ -176,10 +198,21 @@ priceCompareEl.textContent = hasComparePrice ? formatMoney(selectedVariant.compare_at_price) : ''; priceCompareEl.hidden = !hasComparePrice; } + } + + function updateVariantUI() { + if (!variantSelect) return; + + const selectedVariant = productVariants.find((variant) => Number(variant.id) === Number(variantSelect.value)); + + if (!selectedVariant) return; + updateBuyButtonState(selectedVariant); + updatePriceDisplay(selectedVariant); updateURLWithVariant(selectedVariant.id); } + // Initialize variant selection based on URL parameter if (variantSelect) { const initialVariantId = new URLSearchParams(window.location.search).get('variant'); @@ -187,7 +220,8 @@ variantSelect.value = String(initialVariantId); } - variantSelect.addEventListener('change', updateProductPrice); + updateVariantUI(); + variantSelect.addEventListener('change', updateVariantUI); } // Update variant based on selected options @@ -211,7 +245,7 @@ variantSelect.value = String(matchingVariant.id); updateURLWithVariant(matchingVariant.id); - updateProductPrice(); + updateVariantUI(); } document.querySelectorAll('.product-option-input').forEach((input) => { From 4e26edfac25cd566123c2ad05088d10ea317f360 Mon Sep 17 00:00:00 2001 From: MisterDetour Date: Fri, 25 Sep 2026 09:42:25 -0700 Subject: [PATCH 039/110] Add class to oos options --- sections/product.liquid | 36 ++++++++++++++++++++++++++++++++ snippets/variant-selector.liquid | 5 +++++ 2 files changed, 41 insertions(+) diff --git a/sections/product.liquid b/sections/product.liquid index ace6ff72c..821ea6cde 100644 --- a/sections/product.liquid +++ b/sections/product.liquid @@ -174,6 +174,41 @@ return '$' + (cents / 100).toFixed(2); } + function matchesVariantOptions(variant, optionValues) { + if (!variant || !variant.options || variant.options.length !== optionValues.length) { + return false; + } + + return optionValues.every((option, index) => option === null || variant.options[index] === option); + } + + // Mark options as unavailable based on current selection + function updateOptionAvailability() { + const optionGroups = Array.from(document.querySelectorAll('.product-option-group')); + + if (!optionGroups.length) return; + + optionGroups.forEach((group, groupIndex) => { + const optionInputs = Array.from(group.querySelectorAll('.product-option-input')); + const currentSelection = optionGroups.map((optionGroup) => { + const checkedInput = optionGroup.querySelector('.product-option-input:checked'); + return checkedInput ? checkedInput.value : null; + }); + + optionInputs.forEach((input) => { + const candidateOptions = [...currentSelection]; + candidateOptions[groupIndex] = input.value; + + const hasAvailableVariant = productVariants.some((variant) => { + return variant.available && matchesVariantOptions(variant, candidateOptions); + }); + + const isCurrentlySelected = input.checked; + input.classList.toggle('disabled', !hasAvailableVariant && !isCurrentlySelected); + }); + }); + } + function updateBuyButtonState(selectedVariant) { const buyButton = document.querySelector('.product-buy-button'); @@ -207,6 +242,7 @@ if (!selectedVariant) return; + updateOptionAvailability(); updateBuyButtonState(selectedVariant); updatePriceDisplay(selectedVariant); updateURLWithVariant(selectedVariant.id); diff --git a/snippets/variant-selector.liquid b/snippets/variant-selector.liquid index 1e6028b74..470f55e58 100644 --- a/snippets/variant-selector.liquid +++ b/snippets/variant-selector.liquid @@ -72,4 +72,9 @@ border-color: var(--color-foreground); color: var(--color-background); } + + .product-option-input.disabled + .product-option-button { + opacity: 0.45; + text-decoration: line-through; + } {% endstylesheet %} From 5e676ba24716164d3d65be6c4ac2410f6d5d3aa7 Mon Sep 17 00:00:00 2001 From: MisterDetour Date: Fri, 25 Sep 2026 11:48:06 -0700 Subject: [PATCH 040/110] Show active variant images --- TODO.md | 1 + sections/product.liquid | 67 +++++++++++++++++++++++++++++++++-------- 2 files changed, 55 insertions(+), 13 deletions(-) diff --git a/TODO.md b/TODO.md index f47af63a5..b7dff2348 100644 --- a/TODO.md +++ b/TODO.md @@ -24,6 +24,7 @@ - If you quickly update the quantity twice it fails - Form spam protection? - Go through all templates again +- Clean up all code # Complex Functionality - create skills for diff --git a/sections/product.liquid b/sections/product.liquid index 821ea6cde..ef5ce4c45 100644 --- a/sections/product.liquid +++ b/sections/product.liquid @@ -1,12 +1,20 @@ {{ 'swiper-bundle.min.css' | asset_url | stylesheet_tag }} +{% assign variant_images = product.images | where: 'attached_to_variant?', true %} +{% assign other_images = product.images | reject: 'attached_to_variant?', true %} +
diff --git a/sections/collection.liquid b/sections/collection.liquid index ad0a5f13b..fd416b497 100644 --- a/sections/collection.liquid +++ b/sections/collection.liquid @@ -1,6 +1,8 @@

{{ collection.title }}

+ {% render 'filters' %} +
{% paginate collection.products by 4 %}
diff --git a/sections/header.liquid b/sections/header.liquid index 39401acf8..ae806a1e6 100644 --- a/sections/header.liquid +++ b/sections/header.liquid @@ -21,7 +21,7 @@
{% form 'storefront_password', class: 'password-modal-form' %} @@ -39,7 +39,7 @@
-
@@ -79,7 +79,7 @@
-
@@ -172,7 +172,7 @@ margin-top: 2rem; } - .password-subscribe .btn-submit { + .password-subscribe .button-submit { position: absolute; right: 1rem; top: 1rem; diff --git a/sections/product.liquid b/sections/product.liquid index ef5ce4c45..b076f56c8 100644 --- a/sections/product.liquid +++ b/sections/product.liquid @@ -58,7 +58,7 @@
- diff --git a/snippets/cart-drawer.liquid b/snippets/cart-drawer.liquid index d772ad72d..9b2fac595 100644 --- a/snippets/cart-drawer.liquid +++ b/snippets/cart-drawer.liquid @@ -2,7 +2,7 @@

{{ 'cart.title' | t }}

-
@@ -76,7 +76,7 @@
- {{ 'cart.checkout' | t }} + {{ 'cart.checkout' | t }}
@@ -313,7 +313,7 @@ justify-content: space-between; } - .cart-drawer-btn { + .cart-drawer-button { width: 100%; text-align: center; } diff --git a/snippets/filters.liquid b/snippets/filters.liquid new file mode 100644 index 000000000..c7dc67805 --- /dev/null +++ b/snippets/filters.liquid @@ -0,0 +1,216 @@ +{% if collection.filters != empty %} + {% assign active_filter_count = 0 %} + {% for filter in collection.filters %} + {% assign active_filter_count = active_filter_count | plus: filter.active_values.size %} + {% endfor %} + +
+
+

Filter products

+ + {% if active_filter_count > 0 %} + Clear all + {% endif %} +
+ + {% for filter in collection.filters %} +
0 %} + open + {% endif %} + > + + {{ filter.label }} + {% if filter.active_values.size > 0 %} + {{ filter.active_values.size }} + {% endif %} + + +
+ {% case filter.type %} + {% when 'list', 'boolean' %} +
    + {% for filter_value in filter.values %} +
  • + +
  • + {% endfor %} +
+ + {% when 'price_range' %} +
+ + + +
+ {% endcase %} +
+
+ {% endfor %} + + +
+{% endif %} + +{% stylesheet %} + .collection-filters { + display: flex; + gap: 1rem; + margin-block: 1.5rem; + } + + .collection-filters-header { + display: flex; + align-items: center; + justify-content: space-between; + gap: 1rem; + } + + .collection-filters-heading { + margin: 0; + font-size: 1.25rem; + } + + .collection-filters-clear { + color: inherit; + text-underline-offset: 0.2em; + } + + .collection-filter summary { + display: flex; + align-items: center; + justify-content: space-between; + cursor: pointer; + padding-block: 0.875rem; + } + + .collection-filter-active-count { + display: grid; + width: 1.5rem; + height: 1.5rem; + place-items: center; + border-radius: 50%; + background: currentColor; + color: white; + font-size: 0.75rem; + } + + .collection-filter-content { + padding-bottom: 1rem; + } + + .collection-filter-values { + display: grid; + gap: 0.625rem; + margin: 0; + padding: 0; + } + + .collection-filter-value { + display: flex; + align-items: center; + gap: 0.5rem; + cursor: pointer; + } + + .collection-filter-value.is-disabled { + cursor: not-allowed; + opacity: 0.5; + } + + .collection-filter-value input { + margin: 0; + } + + .collection-filter-swatch { + width: 1rem; + height: 1rem; + border: 1px solid; + border-radius: 50%; + background-position: center; + background-size: cover; + } + + .collection-filter-value-count { + margin-left: auto; + opacity: 0.7; + } + + .collection-filter-price-range { + display: grid; + grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 0.75rem; + } + + .collection-filter-price-range label { + display: grid; + gap: 0.375rem; + font-size: 0.875rem; + } + + .collection-filter-price-range input { + width: 100%; + padding: 0.5rem; + border: 1px solid; + background: transparent; + color: inherit; + } + + .collection-filters-apply { + justify-self: start; + padding: 0.625rem 1rem; + border: 1px solid; + color: white; + cursor: pointer; + } +{% endstylesheet %} From 67a537b542adf846b241bed01f7969ad9373e430 Mon Sep 17 00:00:00 2001 From: MisterDetour Date: Fri, 25 Sep 2026 14:24:29 -0700 Subject: [PATCH 044/110] Update collection on filter change event --- snippets/filters.liquid | 120 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 110 insertions(+), 10 deletions(-) diff --git a/snippets/filters.liquid b/snippets/filters.liquid index c7dc67805..31ad85afe 100644 --- a/snippets/filters.liquid +++ b/snippets/filters.liquid @@ -14,12 +14,7 @@ {% for filter in collection.filters %} -
0 %} - open - {% endif %} - > +
{{ filter.label }} {% if filter.active_values.size > 0 %} @@ -30,7 +25,7 @@
{% case filter.type %} {% when 'list', 'boolean' %} -
    +
      {% for filter_value in filter.values %}
    • @@ -88,7 +82,6 @@ name="{{ filter.max_value.param_name }}" value="{{ filter.max_value.value | money_without_currency | replace: ',', '' }}" min="0" - max="{{ filter.range_max | money_without_currency | replace: ',', '' }}" placeholder="{{ filter.range_max | money_without_currency | replace: ',', '' }}" > @@ -107,6 +100,8 @@ display: flex; gap: 1rem; margin-block: 1.5rem; + position: relative; + z-index: 2; } .collection-filters-header { @@ -126,6 +121,10 @@ text-underline-offset: 0.2em; } + .collection-filter { + position: relative; + } + .collection-filter summary { display: flex; align-items: center; @@ -146,7 +145,13 @@ } .collection-filter-content { - padding-bottom: 1rem; + padding: 1rem; + position: absolute; + left: 0; + top: 100%; + width: 35rem; + background: var(--color-background); + border: 1px solid var(--color-border); } .collection-filter-values { @@ -214,3 +219,98 @@ cursor: pointer; } {% endstylesheet %} + +{% javascript %} + // Close other filter groups when one is opened + const filterGroups = document.querySelectorAll('.collection-filter'); + + if (filterGroups.length) { + filterGroups.forEach((group) => { + group.addEventListener('toggle', () => { + if (!group.open) return; + + filterGroups.forEach((otherGroup) => { + if (otherGroup !== group) { + otherGroup.removeAttribute('open'); + } + }); + }); + }); + } + + // Update collection products via AJAX when filters change + const collectionFiltersForm = document.querySelector('.collection-filters'); + const collectionProducts = document.querySelector('.collection-products'); + + if (collectionFiltersForm && collectionProducts) { + // Build the URL for the filtered collection based on the form data + const getFilteredCollectionUrl = (form) => { + const params = new URLSearchParams(); + const formData = new FormData(form); + + formData.forEach((value, key) => { + if (String(value).trim() !== '') { + params.append(key, String(value)); + } + }); + + const url = new URL(form.action || window.location.href, window.location.origin); + url.search = params.toString(); + return url.toString(); + }; + + // Update the product grid + const updateCollectionProducts = async (url) => { + const response = await fetch(url, { + headers: { + 'X-Requested-With': 'XMLHttpRequest', + }, + }); + + if (!response.ok) { + throw new Error(`Collection update failed: ${response.status}`); + } + + const html = await response.text(); + const doc = new DOMParser().parseFromString(html, 'text/html'); + const nextCollectionProducts = doc.querySelector('.collection-products'); + + if (!nextCollectionProducts) { + return; + } + + collectionProducts.innerHTML = nextCollectionProducts.innerHTML; + window.history.pushState({ path: url }, '', url); + }; + + // Listen for submit + collectionFiltersForm.addEventListener('submit', (event) => { + event.preventDefault(); + + const url = getFilteredCollectionUrl(event.currentTarget); + + updateCollectionProducts(url).catch((error) => { + console.error(error); + window.location.assign(event.currentTarget.action); + }); + }); + + // Listen for change + collectionFiltersForm.addEventListener('change', (event) => { + if (!(event.target instanceof HTMLElement)) { + return; + } + + if (!event.target.matches('input, select')) { + return; + } + + const url = getFilteredCollectionUrl(event.currentTarget); + + updateCollectionProducts(url).catch((error) => { + console.error(error); + window.location.assign(event.currentTarget.action); + }); + }); + } +{% endjavascript %} From fc4d34d97d731b5cf828346864ba22a97147390e Mon Sep 17 00:00:00 2001 From: MisterDetour Date: Mon, 28 Sep 2026 08:03:31 -0700 Subject: [PATCH 045/110] Working on filters --- assets/icon-dropdown.svg | 1 + sections/hello-world.liquid | 4 ++-- snippets/filters.liquid | 30 ++++++++++++++++++++---------- 3 files changed, 23 insertions(+), 12 deletions(-) create mode 100644 assets/icon-dropdown.svg diff --git a/assets/icon-dropdown.svg b/assets/icon-dropdown.svg new file mode 100644 index 000000000..a554a07c1 --- /dev/null +++ b/assets/icon-dropdown.svg @@ -0,0 +1 @@ + diff --git a/sections/hello-world.liquid b/sections/hello-world.liquid index 543edfd10..560eb533e 100644 --- a/sections/hello-world.liquid +++ b/sections/hello-world.liquid @@ -13,7 +13,7 @@ using Liquid, Shopify's theme templating language, along with HTML, CSS, JavaScript, and JSON.

-
+
@@ -76,7 +76,7 @@ margin-top: 1.5rem; } - .icon { + .welcome-icon { width: 300px; } diff --git a/snippets/filters.liquid b/snippets/filters.liquid index 31ad85afe..c8fddd395 100644 --- a/snippets/filters.liquid +++ b/snippets/filters.liquid @@ -6,17 +6,20 @@
-

Filter products

+

Filter:

- {% if active_filter_count > 0 %} - Clear all - {% endif %} + {% comment %} + {% if active_filter_count > 0 %} + Clear all + {% endif %} + {% endcomment %}
{% for filter in collection.filters %}
{{ filter.label }} + {{ 'icon-dropdown.svg' | inline_asset_content }} {% if filter.active_values.size > 0 %} {{ filter.active_values.size }} {% endif %} @@ -56,7 +59,7 @@ {% endif %} {{ filter_value.label }} - {{ filter_value.count }} + ({{ filter_value.count }}) {% endfor %} @@ -90,15 +93,13 @@
{% endfor %} - - {% endif %} {% stylesheet %} .collection-filters { display: flex; - gap: 1rem; + gap: 2rem; margin-block: 1.5rem; position: relative; z-index: 2; @@ -113,7 +114,7 @@ .collection-filters-heading { margin: 0; - font-size: 1.25rem; + font-size: 1.4rem; } .collection-filters-clear { @@ -129,8 +130,18 @@ display: flex; align-items: center; justify-content: space-between; + gap: 0.5rem; cursor: pointer; padding-block: 0.875rem; + font-size: 1.4rem; + line-height: 1; + } + + .collection-filter .icon-dropdown { + width: 1rem; + height: 0.6rem; + position: relative; + top: 0.1rem; } .collection-filter-active-count { @@ -187,7 +198,6 @@ } .collection-filter-value-count { - margin-left: auto; opacity: 0.7; } From 1d811eaf5d108c4cfc77e6af37f6c786b9cece40 Mon Sep 17 00:00:00 2001 From: MisterDetour Date: Mon, 28 Sep 2026 08:09:13 -0700 Subject: [PATCH 046/110] Add Sort By dropdown --- snippets/filters.liquid | 215 ++++++++++++++++++++++++---------------- 1 file changed, 128 insertions(+), 87 deletions(-) diff --git a/snippets/filters.liquid b/snippets/filters.liquid index c8fddd395..49b41670b 100644 --- a/snippets/filters.liquid +++ b/snippets/filters.liquid @@ -1,110 +1,151 @@ -{% if collection.filters != empty %} +{% if collection.filters != empty or collection.sort_options.size > 0 %} {% assign active_filter_count = 0 %} {% for filter in collection.filters %} {% assign active_filter_count = active_filter_count | plus: filter.active_values.size %} {% endfor %}
-
-

Filter:

- - {% comment %} - {% if active_filter_count > 0 %} - Clear all - {% endif %} - {% endcomment %} -
- - {% for filter in collection.filters %} -
- - {{ filter.label }} - {{ 'icon-dropdown.svg' | inline_asset_content }} - {% if filter.active_values.size > 0 %} - {{ filter.active_values.size }} + {% if collection.filters != empty %} +
+

Filter:

+ + {% comment %} + {% if active_filter_count > 0 %} + Clear all {% endif %} -
- -
- {% case filter.type %} - {% when 'list', 'boolean' %} -
    - {% for filter_value in filter.values %} -
  • - +
  • + {% endfor %} +
+ + {% when 'price_range' %} +
+ + + +
+ {% endcase %} +
+
+ {% endfor %} + {% endif %} + {% if collection.sort_options.size > 0 %} + + {% endif %}
{% endif %} {% stylesheet %} .collection-filters { display: flex; + align-items: center; + flex-wrap: wrap; gap: 2rem; margin-block: 1.5rem; position: relative; z-index: 2; } + .collection-sort { + display: inline-flex; + align-items: center; + gap: 0.75rem; + margin-left: auto; + font-size: 1.4rem; + } + + .collection-sort-label { + white-space: nowrap; + } + + .collection-sort select { + padding: 0.65rem 1rem; + border: 1px solid var(--color-border); + background: transparent; + color: inherit; + font: inherit; + } + .collection-filters-header { display: flex; align-items: center; From 19b3f6e444831e2c1c7ca131788f745d92396a0e Mon Sep 17 00:00:00 2001 From: MisterDetour Date: Mon, 28 Sep 2026 08:24:03 -0700 Subject: [PATCH 047/110] Working on filters --- snippets/filters.liquid | 258 +++++++++++++++++++++++----------------- 1 file changed, 146 insertions(+), 112 deletions(-) diff --git a/snippets/filters.liquid b/snippets/filters.liquid index 49b41670b..2efd5bf6a 100644 --- a/snippets/filters.liquid +++ b/snippets/filters.liquid @@ -1,131 +1,165 @@ -{% if collection.filters != empty or collection.sort_options.size > 0 %} - {% assign active_filter_count = 0 %} - {% for filter in collection.filters %} - {% assign active_filter_count = active_filter_count | plus: filter.active_values.size %} - {% endfor %} - -
- {% if collection.filters != empty %} -
-

Filter:

- - {% comment %} - {% if active_filter_count > 0 %} - Clear all - {% endif %} - {% endcomment %} -
- - {% for filter in collection.filters %} -
- - {{ filter.label }} - {{ 'icon-dropdown.svg' | inline_asset_content }} - {% if filter.active_values.size > 0 %} - {{ filter.active_values.size }} - {% endif %} - - -
- {% case filter.type %} - {% when 'list', 'boolean' %} -
    - {% for filter_value in filter.values %} -
  • -
  • - {% endfor %} -
- - {% when 'price_range' %} -
- - - -
- {% endcase %} -
-
- {% endfor %} - {% endif %} - {% if collection.sort_options.size > 0 %} - - {% endif %} -
-{% endif %} + {% endif %} +
+
+ {% if collection.sort_options.size > 0 %} + + {% endif %} + {% if visible_product_count > 0 %} +
{{ visible_product_count }} of {{ total_product_count }} products
+ {% endif %} +
+
+ + {% endif %} + {% stylesheet %} + .collection-toolbar { + margin-block: 1.5rem; + } + + .collection-product-count { + margin: 0; + font-size: 1.6rem; + font-weight: 600; + } + .collection-filters { + display: flex; + align-items: center; + justify-content: space-between; + } + + .collection-filters-wrap { display: flex; align-items: center; flex-wrap: wrap; gap: 2rem; - margin-block: 1.5rem; position: relative; z-index: 2; } + .collection-sort-wrap { + display: flex; + align-items: center; + gap: 1rem; + } + .collection-sort { display: inline-flex; align-items: center; From 2e3c8a245bed8217de7aa37e58efae4377e71dc2 Mon Sep 17 00:00:00 2001 From: MisterDetour Date: Mon, 28 Sep 2026 09:40:37 -0700 Subject: [PATCH 048/110] Working on filters --- snippets/filters.liquid | 289 +++++++++++++++++++++++++++++++--------- 1 file changed, 225 insertions(+), 64 deletions(-) diff --git a/snippets/filters.liquid b/snippets/filters.liquid index 2efd5bf6a..53045f0a2 100644 --- a/snippets/filters.liquid +++ b/snippets/filters.liquid @@ -26,9 +26,6 @@ {{ filter.label }} {{ 'icon-dropdown.svg' | inline_asset_content }} - {% if filter.active_values.size > 0 %} - {{ filter.active_values.size }} - {% endif %}
@@ -37,7 +34,7 @@
    {% for filter_value in filter.values %}
  • -
  • @@ -101,6 +98,19 @@ {% endfor %} {% endif %}
+ {% assign has_active_filters = false %} + {% for filter in collection.filters %} + {% if filter.type == 'price_range' %} + {% if filter.min_value.value != blank %} + {% assign has_active_filters = true %} + {% elsif filter.max_value.value != blank %} + {% assign has_active_filters = true %} + {% endif %} + {% elsif filter.active_values.size > 0 %} + {% assign has_active_filters = true %} + {% endif %} + {% endfor %} +
{% if collection.sort_options.size > 0 %}
-
+ {% if has_active_filters %} +
+
+ {% for filter in collection.filters %} + {% if filter.type == 'price_range' %} + {% if filter.min_value.value != blank %} + + + {% if filter.max_value.value != blank %} + {{ filter.min_value.value | money }} – {{ filter.max_value.value | money }} + {% else %} + {{ filter.min_value.value | money }}+ + {% endif %} + + + + {% elsif filter.max_value.value != blank %} + + Up to {{ filter.max_value.value | money }} + + + {% endif %} + {% else %} + {% for filter_value in filter.active_values %} + + {{ filter_value.label }} + + + {% endfor %} + {% endif %} + {% endfor %} +
+ + Remove all +
+ {% endif %} {% endif %} @@ -133,15 +178,10 @@ margin-block: 1.5rem; } - .collection-product-count { - margin: 0; - font-size: 1.6rem; - font-weight: 600; - } - .collection-filters { - display: flex; + display: grid; align-items: center; + grid-template-columns: 1fr 1fr; justify-content: space-between; } @@ -154,6 +194,44 @@ z-index: 2; } + .collection-active-filters-wrapper { + display: flex; + align-items: center; + gap: 1rem; + margin-bottom: 1rem; + } + + .collection-active-filters { + display: flex; + flex-wrap: wrap; + gap: 0.75rem; + } + + .collection-active-filters-clear { + color: inherit; + text-decoration: underline; + text-underline-offset: 0.2em; + white-space: nowrap; + } + + .collection-active-filter { + display: inline-flex; + align-items: center; + gap: 0.5rem; + padding: 0.5rem 0.75rem; + border: 1px solid var(--color-border); + background: transparent; + color: inherit; + text-decoration: none; + font-size: 1.4rem; + line-height: 1; + } + + .collection-active-filter span:last-child { + font-size: 1.6rem; + line-height: 1; + } + .collection-sort-wrap { display: flex; align-items: center; @@ -219,17 +297,6 @@ top: 0.1rem; } - .collection-filter-active-count { - display: grid; - width: 1.5rem; - height: 1.5rem; - place-items: center; - border-radius: 50%; - background: currentColor; - color: white; - font-size: 0.75rem; - } - .collection-filter-content { padding: 1rem; position: absolute; @@ -264,12 +331,54 @@ } .collection-filter-swatch { - width: 1rem; - height: 1rem; - border: 1px solid; + width: 1.75rem; + height: 1.75rem; + border: 1px solid var(--color-border); border-radius: 50%; background-position: center; background-size: cover; + display: inline-block; + transition: transform 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease, opacity 0.2s ease; + } + + .collection-filter-value.is-color-value { + justify-content: flex-start; + } + + .collection-filter-value.is-color-value input { + position: absolute; + opacity: 0; + pointer-events: none; + width: 0; + height: 0; + } + + .collection-filter-value.is-color-value .collection-filter-value-label, + .collection-filter-value.is-color-value .collection-filter-value-count { + display: block; + font-size: 1.1rem; + line-height: 1.2; + text-align: center; + } + + .collection-filter-value.is-color-value .collection-filter-value-count { + opacity: 0.7; + } + + .collection-filter-value.is-color-value .collection-filter-swatch { + width: 1.75rem; + height: 1.75rem; + border-color: rgba(0, 0, 0, 0.15); + } + + .collection-filter-value.is-color-value input:checked + .collection-filter-swatch, + .collection-filter-value.is-color-value input:focus-visible + .collection-filter-swatch { + outline: 0.1rem solid var(--color-foreground); + outline-offset: 0.1rem; + } + + .collection-filter-value.is-color-value input:disabled + .collection-filter-swatch { + opacity: 0.2; } .collection-filter-value-count { @@ -306,11 +415,15 @@ {% endstylesheet %} {% javascript %} - // Close other filter groups when one is opened - const filterGroups = document.querySelectorAll('.collection-filter'); + const setupCollectionFilterInteractions = () => { + const filterGroups = document.querySelectorAll('.collection-filter'); - if (filterGroups.length) { filterGroups.forEach((group) => { + if (group.dataset.collectionFilterBound === 'true') { + return; + } + + group.dataset.collectionFilterBound = 'true'; group.addEventListener('toggle', () => { if (!group.open) return; @@ -321,14 +434,21 @@ }); }); }); - } - // Update collection products via AJAX when filters change - const collectionFiltersForm = document.querySelector('.collection-filters'); - const collectionProducts = document.querySelector('.collection-products'); + const collectionFiltersForm = document.querySelector('.collection-filters'); + const collectionProducts = document.querySelector('.collection-products'); + const collectionClearAllLink = document.querySelector('.collection-active-filters-clear'); + + if (!collectionFiltersForm || !collectionProducts) { + return; + } + + if (collectionFiltersForm.dataset.collectionBound === 'true') { + return; + } + + collectionFiltersForm.dataset.collectionBound = 'true'; - if (collectionFiltersForm && collectionProducts) { - // Build the URL for the filtered collection based on the form data const getFilteredCollectionUrl = (form) => { const params = new URLSearchParams(); const formData = new FormData(form); @@ -344,43 +464,77 @@ return url.toString(); }; - // Update the product grid - const updateCollectionProducts = async (url) => { - const response = await fetch(url, { - headers: { - 'X-Requested-With': 'XMLHttpRequest', - }, - }); + let activeCollectionRequest = null; - if (!response.ok) { - throw new Error(`Collection update failed: ${response.status}`); + const updateCollectionProducts = async (url) => { + if (activeCollectionRequest) { + activeCollectionRequest.abort(); } - const html = await response.text(); - const doc = new DOMParser().parseFromString(html, 'text/html'); - const nextCollectionProducts = doc.querySelector('.collection-products'); + activeCollectionRequest = new AbortController(); - if (!nextCollectionProducts) { - return; - } + try { + const response = await fetch(url, { + headers: { + 'X-Requested-With': 'XMLHttpRequest', + }, + signal: activeCollectionRequest.signal, + }); - collectionProducts.innerHTML = nextCollectionProducts.innerHTML; - window.history.pushState({ path: url }, '', url); + if (!response.ok) { + throw new Error(`Collection update failed: ${response.status}`); + } + + const html = await response.text(); + const doc = new DOMParser().parseFromString(html, 'text/html'); + const nextCollectionProducts = doc.querySelector('.collection-products'); + const nextActiveFiltersWrapper = doc.querySelector('.collection-active-filters-wrapper'); + + if (nextCollectionProducts) { + collectionProducts.innerHTML = nextCollectionProducts.innerHTML; + } + + if (nextActiveFiltersWrapper) { + const currentActiveFiltersWrapper = document.querySelector('.collection-active-filters-wrapper'); + + if (currentActiveFiltersWrapper) { + currentActiveFiltersWrapper.replaceWith(nextActiveFiltersWrapper); + } else { + collectionFiltersForm.append(nextActiveFiltersWrapper); + } + } else { + const currentActiveFiltersWrapper = document.querySelector('.collection-active-filters-wrapper'); + + if (currentActiveFiltersWrapper) { + currentActiveFiltersWrapper.remove(); + } + } + + window.history.pushState({ path: url }, '', url); + } catch (error) { + if (error.name === 'AbortError') { + return; + } + + console.error(error); + window.location.assign(collectionFiltersForm.action); + } finally { + if (activeCollectionRequest && activeCollectionRequest.signal.aborted) { + activeCollectionRequest = null; + } else { + activeCollectionRequest = null; + } + } }; - // Listen for submit collectionFiltersForm.addEventListener('submit', (event) => { event.preventDefault(); const url = getFilteredCollectionUrl(event.currentTarget); - updateCollectionProducts(url).catch((error) => { - console.error(error); - window.location.assign(event.currentTarget.action); - }); + updateCollectionProducts(url); }); - // Listen for change collectionFiltersForm.addEventListener('change', (event) => { if (!(event.target instanceof HTMLElement)) { return; @@ -392,10 +546,17 @@ const url = getFilteredCollectionUrl(event.currentTarget); - updateCollectionProducts(url).catch((error) => { - console.error(error); - window.location.assign(event.currentTarget.action); - }); + updateCollectionProducts(url); }); - } + + if (collectionClearAllLink) { + collectionClearAllLink.addEventListener('click', (event) => { + event.preventDefault(); + + updateCollectionProducts(collectionClearAllLink.href); + }); + } + }; + + setupCollectionFilterInteractions(); {% endjavascript %} From 033a8851ca83b33402821a7213fcabdae1817fd7 Mon Sep 17 00:00:00 2001 From: MisterDetour Date: Mon, 28 Sep 2026 09:58:57 -0700 Subject: [PATCH 049/110] Working on filters --- snippets/filters.liquid | 286 ++++++++++++++++++++++++++++++++++++---- 1 file changed, 264 insertions(+), 22 deletions(-) diff --git a/snippets/filters.liquid b/snippets/filters.liquid index 53045f0a2..4cc5a2e55 100644 --- a/snippets/filters.liquid +++ b/snippets/filters.liquid @@ -69,28 +69,88 @@ {% when 'price_range' %} -
-
@@ -387,7 +447,13 @@ .collection-filter-price-range { display: grid; - grid-template-columns: repeat(2, minmax(0, 1fr)); + gap: 1rem; + } + + .collection-filter-price-range-inputs { + display: grid; + grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr); + align-items: end; gap: 0.75rem; } @@ -397,12 +463,110 @@ font-size: 0.875rem; } - .collection-filter-price-range input { + .collection-filter-price-range-separator { + display: grid; + place-items: center; + padding-bottom: 0.75rem; + font-size: 1.8rem; + line-height: 1; + opacity: 0.8; + } + + .collection-filter-price-range input[type='number'] { width: 100%; - padding: 0.5rem; - border: 1px solid; + padding: 0.75rem 0.875rem; + border: 1px solid var(--color-border); background: transparent; color: inherit; + font: inherit; + } + + .collection-price-slider { + position: relative; + height: 2.5rem; + display: grid; + align-items: center; + } + + .collection-price-track { + position: absolute; + left: 0; + right: 0; + top: 50%; + height: 0.25rem; + transform: translateY(-50%); + border-radius: 999px; + background: rgba(0, 0, 0, 0.2); + } + + .collection-price-range-line { + position: absolute; + inset: 0; + border-radius: inherit; + background: var(--color-foreground); + transform-origin: left center; + } + + .collection-price-range-slider { + position: absolute; + inset: 0; + width: 100%; + height: 100%; + margin: 0; + background: transparent; + pointer-events: none; + -webkit-appearance: none; + appearance: none; + z-index: 1; + } + + .collection-price-range-slider-max { + z-index: 2; + } + + .collection-price-range-slider::-webkit-slider-runnable-track { + height: 0.25rem; + background: transparent; + } + + .collection-price-range-slider::-moz-range-track { + height: 0.25rem; + background: transparent; + } + + .collection-price-range-slider::-webkit-slider-thumb { + -webkit-appearance: none; + appearance: none; + width: 1.5rem; + height: 1.5rem; + border-radius: 50%; + border: 2px solid var(--color-background); + background: var(--color-foreground); + margin-top: -0.625rem; + pointer-events: auto; + cursor: pointer; + box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.15); + transform: translateX(-50%); + } + + .collection-price-range-slider-max::-webkit-slider-thumb { + transform: translateX(50%); + } + + .collection-price-range-slider::-moz-range-thumb { + width: 1.5rem; + height: 1.5rem; + border-radius: 50%; + border: 2px solid var(--color-background); + background: var(--color-foreground); + pointer-events: auto; + cursor: pointer; + box-shadow: 0 0 0 1px rgba(0, 0, 0, 0.15); + transform: translateX(-50%); + } + + .collection-price-range-slider-max::-moz-range-thumb { + transform: translateX(50%); } .collection-filters-apply { @@ -464,6 +628,84 @@ return url.toString(); }; + const syncPriceRangeSlider = (group) => { + const minInput = group.querySelector('.collection-price-input-min'); + const maxInput = group.querySelector('.collection-price-input-max'); + const minSlider = group.querySelector('.collection-price-range-slider-min'); + const maxSlider = group.querySelector('.collection-price-range-slider-max'); + const rangeLine = group.querySelector('.collection-price-range-line'); + + if (!minInput || !maxInput || !minSlider || !maxSlider || !rangeLine) { + return; + } + + const min = Number(group.dataset.rangeMin || 0); + const max = Number(group.dataset.rangeMax || 100); + + if (!minInput.value && !maxInput.value) { + minInput.value = min; + maxInput.value = max; + } + + const minValue = Math.min(Number(minInput.value || min), Number(maxInput.value || max)); + const maxValue = Math.max(Number(minInput.value || min), Number(maxInput.value || max)); + + const leftPercent = ((Math.min(Math.max(minValue, min), max) - min) / (max - min || 1)) * 100; + const rightPercent = ((Math.min(Math.max(maxValue, min), max) - min) / (max - min || 1)) * 100; + + minSlider.value = Math.min(minValue, maxValue); + maxSlider.value = Math.max(minValue, maxValue); + minInput.value = minSlider.value; + maxInput.value = maxSlider.value; + rangeLine.style.left = `${leftPercent}%`; + rangeLine.style.width = `${Math.max(rightPercent - leftPercent, 0)}%`; + }; + + document.querySelectorAll('.collection-filter-price-range').forEach((group) => { + const minInput = group.querySelector('.collection-price-input-min'); + const maxInput = group.querySelector('.collection-price-input-max'); + const minSlider = group.querySelector('.collection-price-range-slider-min'); + const maxSlider = group.querySelector('.collection-price-range-slider-max'); + + [minInput, maxInput].forEach((input) => { + input.addEventListener('input', () => { + const minValue = Number(minInput.value || 0); + const maxValue = Number(maxInput.value || 0); + + if (minValue > maxValue) { + if (document.activeElement === minInput) { + maxInput.value = minInput.value; + } else { + minInput.value = maxInput.value; + } + } + + syncPriceRangeSlider(group); + }); + }); + + [minSlider, maxSlider].forEach((slider) => { + slider.addEventListener('input', () => { + const minValue = Number(minSlider.value || 0); + const maxValue = Number(maxSlider.value || 0); + + if (minValue > maxValue) { + if (slider === minSlider) { + maxSlider.value = minSlider.value; + } else { + minSlider.value = maxSlider.value; + } + } + + minInput.value = minSlider.value; + maxInput.value = maxSlider.value; + syncPriceRangeSlider(group); + }); + }); + + syncPriceRangeSlider(group); + }); + let activeCollectionRequest = null; const updateCollectionProducts = async (url) => { From d78080a771e9948d2fea99db6f4e49cd006aa260 Mon Sep 17 00:00:00 2001 From: MisterDetour Date: Mon, 28 Sep 2026 10:07:26 -0700 Subject: [PATCH 050/110] Working on filters --- TODO.md | 2 +- snippets/filters.liquid | 13 +++++++++---- 2 files changed, 10 insertions(+), 5 deletions(-) diff --git a/TODO.md b/TODO.md index 9404931c8..dbb74299a 100644 --- a/TODO.md +++ b/TODO.md @@ -1,6 +1,6 @@ # MVP -- Add pagination and filters to collection page +- Add filters to collection page - Compare at prices - Responsive images (article, blog) - need to take site width into account - preload pdp images - Make sure all section previews work diff --git a/snippets/filters.liquid b/snippets/filters.liquid index 4cc5a2e55..02f4ba399 100644 --- a/snippets/filters.liquid +++ b/snippets/filters.liquid @@ -69,24 +69,29 @@ {% when 'price_range' %} + {% assign product_price_min = collection.price_range.min | default: filter.range_min %} + {% assign product_price_max = collection.price_range.max | default: filter.range_max %} + {% assign min_value = filter.min_value.value | money_without_currency | replace: ',', '' - | default: '0' + | default: product_price_min + | money_without_currency + | replace: ',', '' %} {% assign max_value = filter.max_value.value | money_without_currency | replace: ',', '' - | default: filter.range_max + | default: product_price_max | money_without_currency | replace: ',', '' %} - {% assign slider_min = filter.range_min + {% assign slider_min = product_price_min | money_without_currency | replace: ',', '' | default: '0' %} - {% assign slider_max = filter.range_max + {% assign slider_max = product_price_max | money_without_currency | replace: ',', '' | default: '100' From 176601ab419257de556c08a9a5add33e0e42abcd Mon Sep 17 00:00:00 2001 From: MisterDetour Date: Mon, 28 Sep 2026 12:41:54 -0700 Subject: [PATCH 051/110] Filters are functioning --- assets/icon-filter.svg | 1 + snippets/cart-drawer.liquid | 2 +- snippets/filters.liquid | 729 ++++++++++++++++++++++-------------- 3 files changed, 449 insertions(+), 283 deletions(-) create mode 100644 assets/icon-filter.svg diff --git a/assets/icon-filter.svg b/assets/icon-filter.svg new file mode 100644 index 000000000..69a73781d --- /dev/null +++ b/assets/icon-filter.svg @@ -0,0 +1 @@ + diff --git a/snippets/cart-drawer.liquid b/snippets/cart-drawer.liquid index 9b2fac595..4606065d2 100644 --- a/snippets/cart-drawer.liquid +++ b/snippets/cart-drawer.liquid @@ -489,7 +489,7 @@ productForm.addEventListener('submit', async (e) => { e.preventDefault(); - const submitButton = productForm.querySelector('.submit'); + const submitButton = productForm.querySelector('.product-buy-button'); const originalButtonText = submitButton.value; if (submitButton) { diff --git a/snippets/filters.liquid b/snippets/filters.liquid index 02f4ba399..f87da0b8e 100644 --- a/snippets/filters.liquid +++ b/snippets/filters.liquid @@ -8,236 +8,237 @@ {% assign active_filter_count = active_filter_count | plus: filter.active_values.size %} {% endfor %} -
-
- {% if collection.filters != empty %} -
-

Filter:

- - {% comment %} - {% if active_filter_count > 0 %} - Clear all - {% endif %} - {% endcomment %} -
+
+ +
{{ 'icon-filter.svg' | inline_asset_content }} Filter & Sort:
+
+
+ + +
+ {% if collection.filters != empty %} +
+

Filter:

+
+ + {% for filter in collection.filters %} +
+ + {{ filter.label }} + {{ 'icon-dropdown.svg' | inline_asset_content }} + + +
+ {% case filter.type %} + {% when 'list', 'boolean' %} +
    + {% for filter_value in filter.values %} +
  • + +
  • + {% endfor %} +
+ + {% when 'price_range' %} + {% assign product_price_min = collection.price_range.min | default: filter.range_min %} + {% assign product_price_max = collection.price_range.max | default: filter.range_max %} + + {% assign min_value = filter.min_value.value | money_without_currency | replace: ',', '' %} + {% assign max_value = filter.max_value.value | money_without_currency | replace: ',', '' %} + {% assign slider_min = product_price_min + | money_without_currency + | replace: ',', '' + | default: '0' + %} + {% assign slider_max = product_price_max + | money_without_currency + | replace: ',', '' + | default: '100' + %} + +
+
+ + + - + + +
+ +
+
+
+
- {% for filter in collection.filters %} -
- - {{ filter.label }} - {{ 'icon-dropdown.svg' | inline_asset_content }} - - -
- {% case filter.type %} - {% when 'list', 'boolean' %} -
    - {% for filter_value in filter.values %} -
  • - -
  • - {% endfor %} -
- - {% when 'price_range' %} - {% assign product_price_min = collection.price_range.min | default: filter.range_min %} - {% assign product_price_max = collection.price_range.max | default: filter.range_max %} - - {% assign min_value = filter.min_value.value - | money_without_currency - | replace: ',', '' - | default: product_price_min - | money_without_currency - | replace: ',', '' - %} - {% assign max_value = filter.max_value.value - | money_without_currency - | replace: ',', '' - | default: product_price_max - | money_without_currency - | replace: ',', '' - %} - {% assign slider_min = product_price_min - | money_without_currency - | replace: ',', '' - | default: '0' - %} - {% assign slider_max = product_price_max - | money_without_currency - | replace: ',', '' - | default: '100' - %} - -
-
- - - - - - -
- -
-
-
+ +
+ {% endcase %} +
+
+ {% endfor %} + {% endif %} +
+ {% assign has_active_filters = false %} + {% for filter in collection.filters %} + {% if filter.type == 'price_range' %} + {% if filter.min_value.value != blank %} + {% assign has_active_filters = true %} + {% elsif filter.max_value.value != blank %} + {% assign has_active_filters = true %} + {% endif %} + {% elsif filter.active_values.size > 0 %} + {% assign has_active_filters = true %} + {% endif %} + {% endfor %} - - -
-
- {% endcase %} +
+ {% if collection.sort_options.size > 0 %} + + {% endif %} + {% if visible_product_count > 0 %} +
+ {{ visible_product_count }} of {{ total_product_count }} products
-
- {% endfor %} - {% endif %} -
- {% assign has_active_filters = false %} - {% for filter in collection.filters %} - {% if filter.type == 'price_range' %} - {% if filter.min_value.value != blank %} - {% assign has_active_filters = true %} - {% elsif filter.max_value.value != blank %} - {% assign has_active_filters = true %} - {% endif %} - {% elsif filter.active_values.size > 0 %} - {% assign has_active_filters = true %} - {% endif %} - {% endfor %} - -
- {% if collection.sort_options.size > 0 %} - - {% endif %} - {% if visible_product_count > 0 %} -
{{ visible_product_count }} of {{ total_product_count }} products
- {% endif %} -
- {% if has_active_filters %} -
-
- {% for filter in collection.filters %} - {% if filter.type == 'price_range' %} - {% if filter.min_value.value != blank %} - - - {% if filter.max_value.value != blank %} - {{ filter.min_value.value | money }} – {{ filter.max_value.value | money }} - {% else %} - {{ filter.min_value.value | money }}+ - {% endif %} - - - - {% elsif filter.max_value.value != blank %} - - Up to {{ filter.max_value.value | money }} - - - {% endif %} - {% else %} - {% for filter_value in filter.active_values %} - - {{ filter_value.label }} - - {% endfor %} - {% endif %} - {% endfor %} -
+
- Remove all -
- {% endif %} - + Remove all +
+ {%- endif %} + + + {% endif %} +{% if visible_product_count > 0 %} +
{{ visible_product_count }} of {{ total_product_count }} products
+{% endif %} + {% stylesheet %} .collection-toolbar { margin-block: 1.5rem; @@ -245,20 +246,92 @@ .collection-filters { display: grid; - align-items: center; - grid-template-columns: 1fr 1fr; - justify-content: space-between; + } + + @media (max-width: 767px) { + .collection-filters { + height: 100%; + width: 40rem; + max-width: calc(100vw - 3rem); + padding: 0 1.5rem; + border-right: 0; + overflow: hidden; + display: flex; + flex-direction: column; + background: var(--color-background); + transform: translate(100%); + transition: transform 0.3s ease; + } + + .collection-toolbar-details[open] .collection-filters { + transform: translate(0); + } + } + + @media (min-width: 768px) { + .collection-filters { + grid-template-columns: 1fr 1fr; + align-items: center; + justify-content: space-between; + } + + .collection-toolbar-details:not([open])::details-content { + content-visibility: visible; + } + } + + .collection-toolbar-details summary { + list-style: none; + } + + @media (max-width: 767px) { + .collection-toolbar-content { + visibility: hidden; + position: fixed; + z-index: 1000; + inset: 0; + display: flex; + justify-content: flex-end; + background-color: rgba(0, 0, 0, 0.5); + transition: visibility 0.3s ease; + } + + .collection-toolbar-details[open] .collection-toolbar-content { + visibility: visible; + } + } + + .collection-toolbar-close { + position: absolute; + right: 0; + top: 0; + z-index: 3; + border: none; + background: transparent; + padding: 0.5rem 0.9rem; + cursor: pointer; + } + + .collection-toolbar-close .icon-close { + width: 1.6rem; } .collection-filters-wrap { display: flex; - align-items: center; flex-wrap: wrap; + flex-direction: column; gap: 2rem; position: relative; z-index: 2; } + @media (min-width: 768px) { + .collection-filters-wrap { + flex-direction: row; + align-items: center; + } + } + .collection-active-filters-wrapper { display: flex; align-items: center; @@ -330,9 +403,21 @@ gap: 1rem; } - .collection-filters-heading { + .collection-filters-heading, + .collection-toolbar-toggle { margin: 0; font-size: 1.4rem; + display: flex; + gap: 1rem; + } + + .collection-toolbar-toggle { + cursor: pointer; + } + + .collection-filters-heading .icon-filter, + .collection-toolbar-toggle .icon-filter { + width: 2rem; } .collection-filters-clear { @@ -362,14 +447,16 @@ top: 0.1rem; } - .collection-filter-content { - padding: 1rem; - position: absolute; - left: 0; - top: 100%; - width: 35rem; - background: var(--color-background); - border: 1px solid var(--color-border); + @media (min-width: 768px) { + .collection-filter-content { + position: absolute; + left: 0; + top: 100%; + width: 35rem; + padding: 1rem; + background: var(--color-background); + border: 1px solid var(--color-border); + } } .collection-filter-values { @@ -585,6 +672,12 @@ {% javascript %} const setupCollectionFilterInteractions = () => { + const collectionProducts = document.querySelector('.collection-products'); + + if (!collectionProducts) { + return; + } + const filterGroups = document.querySelectorAll('.collection-filter'); filterGroups.forEach((group) => { @@ -604,35 +697,6 @@ }); }); - const collectionFiltersForm = document.querySelector('.collection-filters'); - const collectionProducts = document.querySelector('.collection-products'); - const collectionClearAllLink = document.querySelector('.collection-active-filters-clear'); - - if (!collectionFiltersForm || !collectionProducts) { - return; - } - - if (collectionFiltersForm.dataset.collectionBound === 'true') { - return; - } - - collectionFiltersForm.dataset.collectionBound = 'true'; - - const getFilteredCollectionUrl = (form) => { - const params = new URLSearchParams(); - const formData = new FormData(form); - - formData.forEach((value, key) => { - if (String(value).trim() !== '') { - params.append(key, String(value)); - } - }); - - const url = new URL(form.action || window.location.href, window.location.origin); - url.search = params.toString(); - return url.toString(); - }; - const syncPriceRangeSlider = (group) => { const minInput = group.querySelector('.collection-price-input-min'); const maxInput = group.querySelector('.collection-price-input-max'); @@ -646,10 +710,16 @@ const min = Number(group.dataset.rangeMin || 0); const max = Number(group.dataset.rangeMax || 100); - - if (!minInput.value && !maxInput.value) { - minInput.value = min; - maxInput.value = max; + const hasSelection = String(minInput.value).trim() !== '' || String(maxInput.value).trim() !== ''; + + if (!hasSelection) { + minSlider.value = min; + maxSlider.value = max; + minInput.value = ''; + maxInput.value = ''; + rangeLine.style.left = '0%'; + rangeLine.style.width = '100%'; + return; } const minValue = Math.min(Number(minInput.value || min), Number(maxInput.value || max)); @@ -666,12 +736,22 @@ rangeLine.style.width = `${Math.max(rightPercent - leftPercent, 0)}%`; }; - document.querySelectorAll('.collection-filter-price-range').forEach((group) => { + const bindPriceRangeGroup = (group) => { + if (!group || group.dataset.priceRangeBound === 'true') { + return; + } + + group.dataset.priceRangeBound = 'true'; + const minInput = group.querySelector('.collection-price-input-min'); const maxInput = group.querySelector('.collection-price-input-max'); const minSlider = group.querySelector('.collection-price-range-slider-min'); const maxSlider = group.querySelector('.collection-price-range-slider-max'); + if (!minInput || !maxInput || !minSlider || !maxSlider) { + return; + } + [minInput, maxInput].forEach((input) => { input.addEventListener('input', () => { const minValue = Number(minInput.value || 0); @@ -709,8 +789,27 @@ }); syncPriceRangeSlider(group); + }; + + document.querySelectorAll('.collection-filter-price-range').forEach((group) => { + bindPriceRangeGroup(group); }); + const getFilteredCollectionUrl = (form) => { + const params = new URLSearchParams(); + const formData = new FormData(form); + + formData.forEach((value, key) => { + if (String(value).trim() !== '') { + params.append(key, String(value)); + } + }); + + const url = new URL(form.action || window.location.href, window.location.origin); + url.search = params.toString(); + return url.toString(); + }; + let activeCollectionRequest = null; const updateCollectionProducts = async (url) => { @@ -734,20 +833,51 @@ const html = await response.text(); const doc = new DOMParser().parseFromString(html, 'text/html'); + const nextCollectionFilters = doc.querySelector('.collection-filters'); const nextCollectionProducts = doc.querySelector('.collection-products'); const nextActiveFiltersWrapper = doc.querySelector('.collection-active-filters-wrapper'); + const nextProductCounts = doc.querySelectorAll('.collection-product-count'); + const currentCollectionFilters = document.querySelector('.collection-filters'); + const currentProductCounts = document.querySelectorAll('.collection-product-count'); + + if (nextCollectionFilters && currentCollectionFilters) { + currentCollectionFilters.replaceWith(nextCollectionFilters); + } if (nextCollectionProducts) { collectionProducts.innerHTML = nextCollectionProducts.innerHTML; } + if (nextProductCounts.length) { + currentProductCounts.forEach((countEl, index) => { + const nextCount = nextProductCounts[index] || nextProductCounts[0]; + + if (nextCount) { + countEl.innerHTML = nextCount.innerHTML; + } + }); + + if (currentProductCounts.length < nextProductCounts.length) { + const container = + document.querySelector('.collection-filters') || document.querySelector('.collection-toolbar'); + + if (container) { + nextProductCounts.forEach((nextCount, index) => { + if (index >= currentProductCounts.length) { + container.insertAdjacentHTML('beforeend', nextCount.outerHTML); + } + }); + } + } + } + if (nextActiveFiltersWrapper) { const currentActiveFiltersWrapper = document.querySelector('.collection-active-filters-wrapper'); if (currentActiveFiltersWrapper) { currentActiveFiltersWrapper.replaceWith(nextActiveFiltersWrapper); } else { - collectionFiltersForm.append(nextActiveFiltersWrapper); + document.querySelector('.collection-filters')?.append(nextActiveFiltersWrapper); } } else { const currentActiveFiltersWrapper = document.querySelector('.collection-active-filters-wrapper'); @@ -757,6 +887,7 @@ } } + setupCollectionFilterInteractions(); window.history.pushState({ path: url }, '', url); } catch (error) { if (error.name === 'AbortError') { @@ -764,7 +895,7 @@ } console.error(error); - window.location.assign(collectionFiltersForm.action); + window.location.assign(document.querySelector('.collection-filters')?.action || window.location.href); } finally { if (activeCollectionRequest && activeCollectionRequest.signal.aborted) { activeCollectionRequest = null; @@ -774,35 +905,69 @@ } }; - collectionFiltersForm.addEventListener('submit', (event) => { - event.preventDefault(); + if (document.body.dataset.collectionFilterHandlersBound === 'true') { + return; + } - const url = getFilteredCollectionUrl(event.currentTarget); + document.body.dataset.collectionFilterHandlersBound = 'true'; - updateCollectionProducts(url); - }); + document.addEventListener('submit', (event) => { + const form = event.target; - collectionFiltersForm.addEventListener('change', (event) => { - if (!(event.target instanceof HTMLElement)) { + if (!(form instanceof HTMLFormElement) || !form.matches('.collection-filters')) { return; } - if (!event.target.matches('input, select')) { + event.preventDefault(); + updateCollectionProducts(getFilteredCollectionUrl(form)); + }); + + document.addEventListener('change', (event) => { + const target = event.target; + + if (!(target instanceof HTMLElement) || !target.matches('input, select')) { return; } - const url = getFilteredCollectionUrl(event.currentTarget); + const form = target.closest('.collection-filters'); + + if (!form) { + return; + } - updateCollectionProducts(url); + updateCollectionProducts(getFilteredCollectionUrl(form)); }); - if (collectionClearAllLink) { - collectionClearAllLink.addEventListener('click', (event) => { + document.addEventListener('click', (event) => { + const closeButton = event.target.closest('.collection-toolbar-close'); + + if (closeButton) { + const toolbarDetails = closeButton.closest('.collection-toolbar-details'); + + if (toolbarDetails) { + toolbarDetails.removeAttribute('open'); + } + + return; + } + + const activeFilterLink = event.target.closest('.collection-active-filter'); + + if (activeFilterLink) { event.preventDefault(); + updateCollectionProducts(activeFilterLink.href); + return; + } - updateCollectionProducts(collectionClearAllLink.href); - }); - } + const clearLink = event.target.closest('.collection-active-filters-clear'); + + if (!clearLink) { + return; + } + + event.preventDefault(); + updateCollectionProducts(clearLink.href); + }); }; setupCollectionFilterInteractions(); From 7a646789f4c384f9fd88b73e6fd37d68b54c44b6 Mon Sep 17 00:00:00 2001 From: MisterDetour Date: Tue, 29 Sep 2026 11:59:32 -0700 Subject: [PATCH 052/110] Image sizes attributes --- TODO.md | 8 +++---- config/settings_schema.json | 6 ++--- sections/article.liquid | 2 +- sections/blog.liquid | 10 +++++++-- sections/cart.liquid | 9 +++++++- sections/collection.liquid | 25 +++++++++++++-------- sections/collections.liquid | 21 +++++++++++------- sections/product.liquid | 38 ++++++++++++++++++++------------ sections/search.liquid | 16 ++++++++++++-- snippets/cart-drawer.liquid | 9 +++++++- snippets/image-article.liquid | 14 ++++++++++++ snippets/image-blog.liquid | 29 ++++++++++++++++++++++++ snippets/image-collection.liquid | 23 +++++++++++++++++++ snippets/image-pdp.liquid | 29 ++++++++++++++++++++++++ snippets/image.liquid | 3 ++- 15 files changed, 195 insertions(+), 47 deletions(-) create mode 100644 snippets/image-article.liquid create mode 100644 snippets/image-blog.liquid create mode 100644 snippets/image-collection.liquid create mode 100644 snippets/image-pdp.liquid diff --git a/TODO.md b/TODO.md index dbb74299a..a059b6952 100644 --- a/TODO.md +++ b/TODO.md @@ -1,10 +1,8 @@ # MVP -- Add filters to collection page -- Compare at prices -- Responsive images (article, blog) - need to take site width into account - preload pdp images +- Responsive images - need to take site width into account - preload pdp images (currently at Cart) - Make sure all section previews work -- If you quickly update the quantity twice it fails +- If you quickly update the quantity twice it fails - also, should wait to open cart until item added - Basic contact page - Go through all templates again - Responsive cart drawer styles & mobile nave and better styles overall @@ -33,11 +31,11 @@ - Cart drawer (should css/js go in global?) - Mobile menu - Announcement bar +- Category filtering # Complex Functionality - create skills for - Color swatches -- Category filtering - Predictive search - Multiple currencies/markets - Subscription app support diff --git a/config/settings_schema.json b/config/settings_schema.json index 80400bac3..eb2055022 100644 --- a/config/settings_schema.json +++ b/config/settings_schema.json @@ -31,15 +31,15 @@ "label": "t:labels.page_width", "options": [ { - "value": "120rem", + "value": "1200px", "label": "t:options.page_width.narrow" }, { - "value": "150rem", + "value": "1500px", "label": "t:options.page_width.wide" } ], - "default": "120rem" + "default": "1200px" }, { "type": "range", diff --git a/sections/article.liquid b/sections/article.liquid index b94467a2f..26ec14971 100644 --- a/sections/article.liquid +++ b/sections/article.liquid @@ -2,7 +2,7 @@
{% if article.image %}
- {{ article.image | image_url: width: 1000, height: 500 | image_tag }} + {% render 'image-article', image: article.image %}
{% endif %} diff --git a/sections/blog.liquid b/sections/blog.liquid index 140b9a1b7..0b46fa246 100644 --- a/sections/blog.liquid +++ b/sections/blog.liquid @@ -6,7 +6,7 @@ {% for article in blog.articles %}
{% if article.image %} - {{ article.image | image_url: width: 1000 | image_tag | link_to: article.url }} + {% render 'image-blog', image: article.image, url: article.url %} {% endif %}

{{ article.title | link_to: article.url }} @@ -31,10 +31,16 @@ {% stylesheet %} .blog-grid { display: grid; - grid-template-columns: repeat(2, 1fr); + gap: 1rem; margin-top: 1rem; } + @media (min-width: 576px) { + .blog-grid { + grid-template-columns: repeat(2, 1fr); + } + } + .blog-grid-article { display: grid; gap: 1rem; diff --git a/sections/cart.liquid b/sections/cart.liquid index f2ff16724..788260900 100644 --- a/sections/cart.liquid +++ b/sections/cart.liquid @@ -15,7 +15,14 @@ {% for item in cart.items %} - {% render 'image', image: item.image, url: item.url %} + {% render 'image', + image: item.image, + url: item.url, + width: 400, + height: 400, + crop: 'center', + sizes: '90px' + %}

{{ item.product.title }}

diff --git a/sections/collection.liquid b/sections/collection.liquid index fd416b497..354b18081 100644 --- a/sections/collection.liquid +++ b/sections/collection.liquid @@ -9,19 +9,15 @@ {% for product in collection.products %}
{% if product.featured_image %} - {% render 'image', - image: product.featured_image, - url: product.url, - width: 1000, - height: 1000, - crop: 'center' - %} + {% render 'image-collection', image: product.featured_image, url: product.url %} {% endif %}

{{ product.title | escape }}

-
{{ product.price | money }}
+
+ {% render 'price', product: product %} +
{% endfor %} @@ -35,10 +31,21 @@ {% stylesheet %} .collection-products-grid { display: grid; - grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 1rem; } + @media (min-width: 576px) { + .collection-products-grid { + grid-template-columns: repeat(2, 1fr); + } + } + + @media (min-width: 768px) { + .collection-products-grid { + grid-template-columns: repeat(4, 1fr); + } + } + .collection-product { display: grid; gap: 1rem; diff --git a/sections/collections.liquid b/sections/collections.liquid index 76b94fa4f..f34749571 100644 --- a/sections/collections.liquid +++ b/sections/collections.liquid @@ -7,13 +7,7 @@ {% for collection in collections %} {% if collection.featured_image %} - {% render 'image', - class: 'collection-card-image', - image: collection.featured_image, - width: 600, - height: 600, - crop: 'center' - %} + {% render 'image-collection', image: collection.featured_image, class: 'collection-card-image' %} {% endif %}
@@ -31,10 +25,21 @@ {% stylesheet %} .collections { display: grid; - grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 1rem; } + @media (min-width: 576px) { + .collections { + grid-template-columns: repeat(2, 1fr); + } + } + + @media (min-width: 768px) { + .collections { + grid-template-columns: repeat(4, 1fr); + } + } + .collection-card { display: flex; flex-direction: column; diff --git a/sections/product.liquid b/sections/product.liquid index b076f56c8..d20a61509 100644 --- a/sections/product.liquid +++ b/sections/product.liquid @@ -10,12 +10,12 @@
{% for image in variant_images %}
- {% render 'image', class: 'product-image', image: image %} + {% render 'image-pdp', image: image %}
{% endfor %} {% for image in other_images %}
- {% render 'image', class: 'product-image', image: image %} + {% render 'image-pdp', image: image %}
{% endfor %}
@@ -28,12 +28,26 @@ data-index="{{ forloop.index0 }}" data-variant-ids="{{ image.variants | map: 'id' | join: ',' }}" > - {% render 'image', class: 'product-image', image: image, width: 400, height: 400, crop: 'center' %} + {% render 'image', + class: 'product-image', + image: image, + width: 400, + height: 400, + crop: 'center', + sizes: '100px' + %}
{% endfor %} {% for image in other_images %}
- {% render 'image', class: 'product-image', image: image, width: 400, height: 400, crop: 'center' %} + {% render 'image', + class: 'product-image', + image: image, + width: 400, + height: 400, + crop: 'center', + sizes: '100px' + %}
{% endfor %} @@ -96,10 +110,12 @@ {% stylesheet %} - .product-main { - display: grid; - grid-template-columns: repeat(2, 50%); - gap: 2rem; + @media (min-width: 768px) { + .product-main { + display: grid; + gap: 2rem; + grid-template-columns: repeat(2, 50%); + } } .shopify-product-form { @@ -118,12 +134,6 @@ opacity: 0.6; } - .product-gallery .product-image > img { - width: auto; - max-height: 60rem; - margin: 0 auto; - } - .product-gallery-nav { margin-top: 1rem; } diff --git a/sections/search.liquid b/sections/search.liquid index e37412bd5..c7fc4cd34 100644 --- a/sections/search.liquid +++ b/sections/search.liquid @@ -31,7 +31,7 @@
{% assign featured_image = result.featured_image | default: result.image %} {% if featured_image %} - {% render 'image', class: 'search-result-image', image: featured_image, url: result.url, width: 400 %} + {% render 'image-collection', image: featured_image, url: result.url, class: 'search-result-image' %} {% else %}
{% endif %} @@ -79,10 +79,21 @@ .search-results { display: grid; - grid-template-columns: repeat(auto-fill, minmax(250px, 1fr)); gap: 1rem; } + @media (min-width: 576px) { + .search-results { + grid-template-columns: repeat(2, 1fr); + } + } + + @media (min-width: 768px) { + .search-results { + grid-template-columns: repeat(4, 1fr); + } + } + .search-result { display: grid; gap: 1rem; @@ -90,6 +101,7 @@ .search-result-image-holder { background: var(--color-border); + padding-top: 100%; } .search-result-content { diff --git a/snippets/cart-drawer.liquid b/snippets/cart-drawer.liquid index 4606065d2..3eb40bdec 100644 --- a/snippets/cart-drawer.liquid +++ b/snippets/cart-drawer.liquid @@ -13,7 +13,14 @@ {% for item in cart.items %}
  • {% if item.image %} - {% render 'image', image: item.image, url: item.url, width: 240, height: 240, crop: 'center' %} + {% render 'image', + image: item.image, + url: item.url, + width: 400, + height: 400, + crop: 'center', + sizes: '102px' + %} {% endif %}
    diff --git a/snippets/image-article.liquid b/snippets/image-article.liquid new file mode 100644 index 000000000..2d3b0be22 --- /dev/null +++ b/snippets/image-article.liquid @@ -0,0 +1,14 @@ +{% assign margins = settings.min_page_margin | times: 2 %} +{% assign full_width = settings.max_page_width | minus: margins %} + +{% assign sizes = '(min-width: ' + | append: settings.max_page_width + | append: ') ' + | append: full_width + | append: 'px, ' + | append: 'calc(100vw - ' + | append: margins + | append: 'px)' +%} + +{% render 'image', image: image, class: 'article-image', width: 2500, height: 1250, crop: 'center', sizes: sizes %} diff --git a/snippets/image-blog.liquid b/snippets/image-blog.liquid new file mode 100644 index 000000000..62166305a --- /dev/null +++ b/snippets/image-blog.liquid @@ -0,0 +1,29 @@ +{% liquid + assign margins = settings.min_page_margin | times: 2 + assign grid_space = 5 | plus: margins +%} + +{% assign sizes = '(min-width: ' + | append: settings.max_page_width + | append: ') calc((' + | append: settings.max_page_width + | append: ' - ' + | append: grid_space + | append: 'px) / 2), ' + | append: '(min-width: 576px) calc((100vw - ' + | append: grid_space + | append: 'px) / 2), ' + | append: 'calc(100vw - ' + | append: margins + | append: 'px)' +%} + +{% render 'image', + image: image, + url: product.url, + class: 'blog-article-image', + width: 1500, + height: 1500, + crop: 'center', + sizes: sizes +%} diff --git a/snippets/image-collection.liquid b/snippets/image-collection.liquid new file mode 100644 index 000000000..583bdbf3d --- /dev/null +++ b/snippets/image-collection.liquid @@ -0,0 +1,23 @@ +{% liquid + assign margins = settings.min_page_margin | times: 2 + assign grid_space = 30 | plus: margins +%} + +{% assign sizes = '(min-width: ' + | append: settings.max_page_width + | append: ') calc((' + | append: settings.max_page_width + | append: ' - ' + | append: grid_space + | append: 'px) / 4), ' + | append: '(min-width: 768px) calc((100vw - ' + | append: grid_space + | append: 'px) / 4), ' + | append: '(min-width: 576px) calc(50vw - ' + | append: settings.min_page_margin + | append: 'px), calc(100vw - ' + | append: margins + | append: 'px)' +%} + +{% render 'image', image: image, url: url, width: 1000, height: 1000, crop: 'center', sizes: sizes, class: class %} diff --git a/snippets/image-pdp.liquid b/snippets/image-pdp.liquid new file mode 100644 index 000000000..425132b77 --- /dev/null +++ b/snippets/image-pdp.liquid @@ -0,0 +1,29 @@ +{% liquid + assign margins = settings.min_page_margin | times: 2 + assign grid_space = 5 | plus: margins +%} + +{% assign sizes = '(min-width: ' + | append: settings.max_page_width + | append: ') calc((' + | append: settings.max_page_width + | append: ' - ' + | append: grid_space + | append: 'px) / 2), ' + | append: '(min-width: 768px) calc((100vw - ' + | append: grid_space + | append: 'px) / 2), ' + | append: 'calc(100vw - ' + | append: margins + | append: 'px)' +%} + +{% render 'image', + image: image, + url: product.url, + class: 'product-image', + width: 1800, + height: 1800, + crop: 'center', + sizes: sizes +%} diff --git a/snippets/image.liquid b/snippets/image.liquid index 9ea580a80..92cd8d6be 100644 --- a/snippets/image.liquid +++ b/snippets/image.liquid @@ -13,6 +13,7 @@ @param {number} [width] - The highest resolution width of the image to be rendered @param {number} [height] - The highest resolution height of the image to be rendered @param {string} [crop] - The crop position of the image + @param {string} [sizes] - The sizes attribute for the responsive image @example {% render 'image', image: product.featured_image %} @@ -45,7 +46,7 @@ href="{{ url }}" {% endif %} > - {{ image | image_url: width: width, height: height, crop: crop | image_tag }} + {{ image | image_url: width: width, height: height, crop: crop | image_tag: sizes: sizes }} {% stylesheet %} From bf4f4749a53e1789f6a982e40a7ae2069fec82f9 Mon Sep 17 00:00:00 2001 From: MisterDetour Date: Tue, 29 Sep 2026 14:19:08 -0700 Subject: [PATCH 053/110] Working on pdp preload --- TODO.md | 5 +++-- sections/product.liquid | 16 ++++++++++++++-- snippets/cart-drawer.liquid | 4 +++- snippets/image-pdp.liquid | 4 +++- snippets/image.liquid | 8 +++++++- 5 files changed, 30 insertions(+), 7 deletions(-) diff --git a/TODO.md b/TODO.md index a059b6952..c9faebd2d 100644 --- a/TODO.md +++ b/TODO.md @@ -1,11 +1,12 @@ # MVP -- Responsive images - need to take site width into account - preload pdp images (currently at Cart) +- Preload pdp images - Make sure all section previews work - If you quickly update the quantity twice it fails - also, should wait to open cart until item added - Basic contact page +- Scrap the image snippet? - Go through all templates again -- Responsive cart drawer styles & mobile nave and better styles overall +- Responsive cart drawer styles & mobile nav and better styles overall - Clean up all code - html validation - accessability diff --git a/sections/product.liquid b/sections/product.liquid index d20a61509..0771d9aee 100644 --- a/sections/product.liquid +++ b/sections/product.liquid @@ -3,6 +3,7 @@ {% assign variant_images = product.images | where: 'attached_to_variant?', true %} {% assign other_images = product.images | reject: 'attached_to_variant?', true %} +{% assign first_shown = false %}
    @@ -10,12 +11,23 @@
    {% for image in variant_images %}
    - {% render 'image-pdp', image: image %} + {% if first_shown -%} + {% render 'image-pdp', image: image %} + {% else -%} + gfds + {% render 'image-pdp', image: image, preload: true, loading: 'eager' %} + {% assign first_shown = true %} + {% endif %}
    {% endfor %} {% for image in other_images %}
    - {% render 'image-pdp', image: image %} + {% if first_shown -%} + {% render 'image-pdp', image: image %} + {% else -%} + {% render 'image-pdp', image: image, preload: true, loading: 'eager' %} + {% assign first_shown = true %} + {% endif %}
    {% endfor %}
    diff --git a/snippets/cart-drawer.liquid b/snippets/cart-drawer.liquid index 3eb40bdec..b5d2e0ade 100644 --- a/snippets/cart-drawer.liquid +++ b/snippets/cart-drawer.liquid @@ -24,7 +24,9 @@ {% endif %}
    -

    {{ item.product.title }}

    +

    + {{ item.product.title }} +

    diff --git a/snippets/image-pdp.liquid b/snippets/image-pdp.liquid index 425132b77..2b739d447 100644 --- a/snippets/image-pdp.liquid +++ b/snippets/image-pdp.liquid @@ -17,7 +17,7 @@ | append: margins | append: 'px)' %} - +loading: {{ loading }} {% render 'image', image: image, url: product.url, @@ -25,5 +25,7 @@ width: 1800, height: 1800, crop: 'center', + preload: preload, + loading: loading, sizes: sizes %} diff --git a/snippets/image.liquid b/snippets/image.liquid index 92cd8d6be..6727873eb 100644 --- a/snippets/image.liquid +++ b/snippets/image.liquid @@ -14,6 +14,8 @@ @param {number} [height] - The highest resolution height of the image to be rendered @param {string} [crop] - The crop position of the image @param {string} [sizes] - The sizes attribute for the responsive image + @param {string} [preload] - Whether the image should be preloaded + @param {string} [loading] - Loading strategy for the image @example {% render 'image', image: product.featured_image %} @@ -46,7 +48,11 @@ href="{{ url }}" {% endif %} > - {{ image | image_url: width: width, height: height, crop: crop | image_tag: sizes: sizes }} + {{ + image + | image_url: width: width, height: height, crop: crop + | image_tag: sizes: sizes, preload: preload, loading: loading + }} {% stylesheet %} From 345a2b403181a299913b87bf58da683ef926442e Mon Sep 17 00:00:00 2001 From: MisterDetour Date: Tue, 29 Sep 2026 14:47:07 -0700 Subject: [PATCH 054/110] Debounce quantity changes --- TODO.md | 4 +- sections/product.liquid | 15 ++++--- snippets/cart-drawer.liquid | 88 ++++++++++++++++++++++++------------- snippets/image-pdp.liquid | 2 +- 4 files changed, 69 insertions(+), 40 deletions(-) diff --git a/TODO.md b/TODO.md index c9faebd2d..c7bd31fff 100644 --- a/TODO.md +++ b/TODO.md @@ -1,7 +1,5 @@ # MVP -- Preload pdp images -- Make sure all section previews work - If you quickly update the quantity twice it fails - also, should wait to open cart until item added - Basic contact page - Scrap the image snippet? @@ -12,6 +10,7 @@ - accessability - performance - translation files or not - make consistent throughout theme +- Preload pdp images # Templates https://shopify.dev/docs/storefronts/themes/architecture/templates @@ -44,6 +43,7 @@ # Various +- Make sure all section previews work - white - Add to cart errors should be show on page rather than in js alert - Potential global js - header.liquid diff --git a/sections/product.liquid b/sections/product.liquid index 0771d9aee..c64f3547a 100644 --- a/sections/product.liquid +++ b/sections/product.liquid @@ -10,15 +10,16 @@
  • {% if item.image %} - {% render 'image', - image: item.image, - url: item.url, - width: 400, - height: 400, - crop: 'center', - sizes: '102px' - %} + + {{ + item.image + | image_url: width: 400, height: 400, crop: 'center' + | image_tag: sizes: '102px', loading: 'lazy' + }} + {% endif %}
    diff --git a/snippets/image-article.liquid b/snippets/image-article.liquid index 2d3b0be22..bd4dcc2a1 100644 --- a/snippets/image-article.liquid +++ b/snippets/image-article.liquid @@ -11,4 +11,6 @@ | append: 'px)' %} -{% render 'image', image: image, class: 'article-image', width: 2500, height: 1250, crop: 'center', sizes: sizes %} +
    + {{ image | image_url: width: 2500, height: 1250, crop: 'center' | image_tag: sizes: sizes, preload: true }} +
    diff --git a/snippets/image-blog.liquid b/snippets/image-blog.liquid index 62166305a..299074a09 100644 --- a/snippets/image-blog.liquid +++ b/snippets/image-blog.liquid @@ -18,12 +18,6 @@ | append: 'px)' %} -{% render 'image', - image: image, - url: product.url, - class: 'blog-article-image', - width: 1500, - height: 1500, - crop: 'center', - sizes: sizes -%} + + {{ image | image_url: width: 1500, height: 1500, crop: 'center' | image_tag: sizes: sizes }} + diff --git a/snippets/image-collection.liquid b/snippets/image-collection.liquid index 583bdbf3d..02e91e646 100644 --- a/snippets/image-collection.liquid +++ b/snippets/image-collection.liquid @@ -20,4 +20,6 @@ | append: 'px)' %} -{% render 'image', image: image, url: url, width: 1000, height: 1000, crop: 'center', sizes: sizes, class: class %} + + {{ image | image_url: width: 1000, height: 1000, crop: 'center' | image_tag: sizes: sizes }} + From 3f3913260f79a294dbf028ca5681a9a16e9eb57c Mon Sep 17 00:00:00 2001 From: MisterDetour Date: Wed, 30 Sep 2026 10:01:21 -0700 Subject: [PATCH 058/110] Reset pdp images --- sections/product.liquid | 21 ++++----------------- 1 file changed, 4 insertions(+), 17 deletions(-) diff --git a/sections/product.liquid b/sections/product.liquid index c64f3547a..d20a61509 100644 --- a/sections/product.liquid +++ b/sections/product.liquid @@ -3,32 +3,19 @@ {% assign variant_images = product.images | where: 'attached_to_variant?', true %} {% assign other_images = product.images | reject: 'attached_to_variant?', true %} -{% assign first_shown = false %}
    {% endfor %} {% endfor %} diff --git a/TODO.md b/TODO.md index 971e908a1..477f641e0 100644 --- a/TODO.md +++ b/TODO.md @@ -1,9 +1,9 @@ # MVP -- Preload pdp images and scrap image snippet - Responsive cart drawer styles & mobile nav and better styles overall - translation files or not - make consistent throughout theme - accessability +- videos in pdp gallery - Go through all templates again - Clean up all code diff --git a/sections/404.liquid b/sections/main-404.liquid similarity index 100% rename from sections/404.liquid rename to sections/main-404.liquid diff --git a/sections/article.liquid b/sections/main-article.liquid similarity index 100% rename from sections/article.liquid rename to sections/main-article.liquid diff --git a/sections/blog.liquid b/sections/main-blog.liquid similarity index 100% rename from sections/blog.liquid rename to sections/main-blog.liquid diff --git a/sections/cart.liquid b/sections/main-cart.liquid similarity index 100% rename from sections/cart.liquid rename to sections/main-cart.liquid diff --git a/sections/collection.liquid b/sections/main-collection.liquid similarity index 100% rename from sections/collection.liquid rename to sections/main-collection.liquid diff --git a/sections/collections.liquid b/sections/main-collections.liquid similarity index 100% rename from sections/collections.liquid rename to sections/main-collections.liquid diff --git a/sections/page.liquid b/sections/main-page.liquid similarity index 100% rename from sections/page.liquid rename to sections/main-page.liquid diff --git a/sections/password.liquid b/sections/main-password.liquid similarity index 100% rename from sections/password.liquid rename to sections/main-password.liquid diff --git a/sections/product.liquid b/sections/main-product.liquid similarity index 76% rename from sections/product.liquid rename to sections/main-product.liquid index d20a61509..a05462f69 100644 --- a/sections/product.liquid +++ b/sections/main-product.liquid @@ -1,53 +1,102 @@ {{ 'swiper-bundle.min.css' | asset_url | stylesheet_tag }} -{% assign variant_images = product.images | where: 'attached_to_variant?', true %} +{% assign selected_variant = product.selected_or_first_available_variant %} +{% assign selected_variant_image_id = null %} +{% for image in product.images %} + {% if image.attached_to_variant? %} + {% assign is_selected_variant_image = false %} + {% for variant in image.variants %} + {% if variant.id == selected_variant.id %} + {% assign is_selected_variant_image = true %} + {% endif %} + {% endfor %} + + {% if is_selected_variant_image %} + {% assign selected_variant_image_id = image.id %} + {% break %} + {% endif %} + {% endif %} +{% endfor %} + {% assign other_images = product.images | reject: 'attached_to_variant?', true %} +{% assign first_shown = false %}
    @@ -341,6 +342,10 @@ justify-content: space-between; } + .cart-drawer-footer-note { + font-size: 1.4rem; + } + .cart-drawer-button { width: 100%; text-align: center; From 573db16bee2baf31680c1df1c0f3597dc5d9dbb7 Mon Sep 17 00:00:00 2001 From: MisterDetour Date: Wed, 30 Sep 2026 11:30:44 -0700 Subject: [PATCH 061/110] Cart drawer scrollbar styles --- TODO.md | 4 +++- snippets/cart-drawer.liquid | 22 ++++++++++++++++++++-- 2 files changed, 23 insertions(+), 3 deletions(-) diff --git a/TODO.md b/TODO.md index 477f641e0..b2e58edfa 100644 --- a/TODO.md +++ b/TODO.md @@ -1,6 +1,8 @@ # MVP -- Responsive cart drawer styles & mobile nav and better styles overall +- Mobile nav +- Mobile test +- better styles overall - translation files or not - make consistent throughout theme - accessability - videos in pdp gallery diff --git a/snippets/cart-drawer.liquid b/snippets/cart-drawer.liquid index 1d5967f3b..7f2cfc8f3 100644 --- a/snippets/cart-drawer.liquid +++ b/snippets/cart-drawer.liquid @@ -186,13 +186,32 @@ justify-content: space-between; align-items: center; padding: 1rem 0; - margin-bottom: 1rem; border-bottom: 0.1rem solid rgba(0, 0, 0, 0.2); } .cart-drawer-contents { flex: 1; overflow-y: auto; + scrollbar-width: thin; + scrollbar-color: rgba(0, 0, 0, 0.35) transparent; + padding: 1rem 0.25rem 1rem 0; + } + + .cart-drawer-contents::-webkit-scrollbar { + width: 0.5rem; + } + + .cart-drawer-contents::-webkit-scrollbar-track { + background: transparent; + } + + .cart-drawer-contents::-webkit-scrollbar-thumb { + background: rgba(0, 0, 0, 0.35); + border-radius: 999px; + } + + .cart-drawer-contents::-webkit-scrollbar-thumb:hover { + background: rgba(0, 0, 0, 0.5); } .cart-drawer-items { @@ -330,7 +349,6 @@ .cart-drawer-footer { padding: 1rem 0; - margin-top: 1rem; border-top: 0.1rem solid rgba(0, 0, 0, 0.2); display: flex; flex-direction: column; From b502cb478f1238b0a3aebf154392a0269c7950f1 Mon Sep 17 00:00:00 2001 From: MisterDetour Date: Wed, 30 Sep 2026 11:55:58 -0700 Subject: [PATCH 062/110] Mobile nav styles --- assets/global.css | 105 +++++++++++++++++++++++++++++++---------- sections/header.liquid | 20 +++++++- 2 files changed, 98 insertions(+), 27 deletions(-) diff --git a/assets/global.css b/assets/global.css index 58ee09289..9b9548073 100644 --- a/assets/global.css +++ b/assets/global.css @@ -125,7 +125,7 @@ h6 { z-index: 2; } -.site-header h1 { +.site-header-title { margin: 0; } @@ -133,30 +133,6 @@ h6 { position: relative; text-decoration: none; color: var(--color-foreground); - display: flex; - align-items: center; - justify-content: center; -} - -.site-header a sup { - position: absolute; - right: 0; - bottom: 0; - overflow: hidden; - max-width: var(--page-margin); - font-size: 1rem; - background: var(--color-foreground); - color: white; - border-radius: 100%; - display: flex; - align-items: center; - justify-content: center; - width: 2rem; - height: 2rem; -} - -.site-header svg { - width: 3rem; } @media (max-width: 767px) { @@ -170,6 +146,8 @@ h6 { visibility: hidden; transform: translate(-100%); padding: 0 var(--page-margin); + font-size: 2rem; + overflow-y: auto; } .site-nav.anim-ready { @@ -194,6 +172,36 @@ h6 { display: none; } +.site-header-icons a { + position: relative; + text-decoration: none; + color: var(--color-foreground); + display: flex; + align-items: center; + justify-content: center; +} + +.site-header-icons a sup { + position: absolute; + right: 0; + bottom: 0; + overflow: hidden; + max-width: var(--page-margin); + font-size: 1rem; + background: var(--color-foreground); + color: white; + border-radius: 100%; + display: flex; + align-items: center; + justify-content: center; + width: 2rem; + height: 2rem; +} + +.site-header-icons svg { + width: 3rem; +} + .site-nav-menu { flex-direction: column; align-items: flex-start; @@ -205,11 +213,58 @@ h6 { } } +.site-nav-details { + position: relative; +} + +.site-nav-summary { + list-style: none; + cursor: pointer; + display: flex; + align-items: center; + gap: 0.5rem; +} + +.site-nav-summary::-webkit-details-marker { + display: none; +} + +.site-nav-summary .icon-dropdown { + width: 1rem; + position: relative; + top: 0.1rem; +} + +.site-nav-details[open] .icon-dropdown { + transform: rotate(180deg); +} + +.site-nav-submenu { + display: grid; + gap: 0.75rem; + margin-top: 0.75rem; + padding-left: 1rem; + list-style: none; +} + +@media (min-width: 768px) { + .site-nav-submenu { + position: absolute; + top: calc(100% + 0.75rem); + left: 0; + min-width: 16rem; + padding: 0.75rem 1rem; + background: var(--color-background); + border: 0.1rem solid var(--color-border); + } +} + .site-nav-toggle { cursor: pointer; display: flex; align-items: center; justify-content: center; + color: var(--color-foreground); } @media (min-width: 768px) { diff --git a/sections/header.liquid b/sections/header.liquid index ae806a1e6..9bf382607 100644 --- a/sections/header.liquid +++ b/sections/header.liquid @@ -7,14 +7,30 @@ -%}
  • + {%- when 'description' -%} +
    {{ product.description }}
    + {%- when 'price' -%} + {% render 'price' %} + {%- when 'buy_button' -%} +
    + -
    - {%- when 'quantity' -%} - - {%- when 'variant_selector' -%} - {% render 'variant-selector', product: product %} - {%- endcase -%} - {%- endfor -%} - - - {% endform %} - + + {{ 'icon-loader.svg' | inline_asset_content }} + + + {%- when 'quantity' -%} + + {%- when 'variant_selector' -%} + {% render 'variant-selector', product: product %} + {%- endcase -%} + {%- endfor -%} + + + {% endform %} @@ -165,7 +163,7 @@ } } - .shopify-product-form { + .product-form { display: grid; gap: 1rem; } diff --git a/snippets/cart-drawer.liquid b/snippets/cart-drawer.liquid index 677c65ba6..a45a6ffff 100644 --- a/snippets/cart-drawer.liquid +++ b/snippets/cart-drawer.liquid @@ -654,7 +654,7 @@ }); // Ajax add to cart - const productForm = document.querySelector('.product-form form'); + const productForm = document.querySelector('.product-form'); if (productForm) { productForm.addEventListener('submit', async (e) => { From 546d302548789ac8b08ac8755e5be01fa72f2377 Mon Sep 17 00:00:00 2001 From: MisterDetour Date: Tue, 6 Oct 2026 12:39:28 -0700 Subject: [PATCH 088/110] Update todos --- TODO.md | 1 - 1 file changed, 1 deletion(-) diff --git a/TODO.md b/TODO.md index e3d39f810..92be49504 100644 --- a/TODO.md +++ b/TODO.md @@ -1,6 +1,5 @@ # MVP -- featured collection section - better styles overall - accessability From 5e60cefb0baab93adeec36386c365335d6907e06 Mon Sep 17 00:00:00 2001 From: MisterDetour Date: Tue, 6 Oct 2026 12:59:43 -0700 Subject: [PATCH 089/110] Add vissually-hidden class --- assets/global.css | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/assets/global.css b/assets/global.css index f09fa5e75..507319402 100644 --- a/assets/global.css +++ b/assets/global.css @@ -414,6 +414,18 @@ h6 { display: none !important; } +.visually-hidden { + position: absolute; + width: 1px; + height: 1px; + margin: -1px; + padding: 0; + overflow: hidden; + clip: rect(0, 0, 0, 0); + white-space: nowrap; + border: 0; +} + @media (max-width: 767px) { .hide-sm { display: none; From 5f037a61618b0ff8cff4db64d34aabf2a88631d8 Mon Sep 17 00:00:00 2001 From: MisterDetour Date: Tue, 6 Oct 2026 14:30:17 -0700 Subject: [PATCH 090/110] Add focus visible styles --- TODO.md | 17 +++++++++++++++++ assets/global.css | 5 +++++ snippets/variant-selector.liquid | 5 +++++ 3 files changed, 27 insertions(+) diff --git a/TODO.md b/TODO.md index 92be49504..74f349bc4 100644 --- a/TODO.md +++ b/TODO.md @@ -1,5 +1,22 @@ # MVP + +Product variant swatches are not keyboard-friendly + +In snippets/variant-selector.liquid, the radio inputs are visually hidden using opacity: 0 and pointer-events: none. + +The visible label has no focus-visible styling, so keyboard users can tab to the hidden input but won’t get a clear visual cue. + +This should be updated to keep the control focus visible while preserving the custom appearance. + +Mobile navigation needs a bit more keyboard polish + +sections/header.liquid toggles aria-expanded and aria-hidden, which is good. + +But when the menu is open, there is no visible focus treatment on the summary trigger and no obvious keyboard close behavior like Escape handling. + + + - better styles overall - accessability diff --git a/assets/global.css b/assets/global.css index 507319402..e103574b8 100644 --- a/assets/global.css +++ b/assets/global.css @@ -83,6 +83,11 @@ body { color: var(--color-foreground); } +:where(a, button, summary, input, select, textarea, [tabindex]):focus-visible { + outline: 0.2rem solid var(--color-foreground); + outline-offset: 0.2rem; +} + /* --------------- Full Width Page Grid --------------- */ .shopify-section { diff --git a/snippets/variant-selector.liquid b/snippets/variant-selector.liquid index 470f55e58..a55be6e9f 100644 --- a/snippets/variant-selector.liquid +++ b/snippets/variant-selector.liquid @@ -73,6 +73,11 @@ color: var(--color-background); } + .product-option-input:focus-visible + .product-option-button { + outline: 0.2rem solid var(--color-foreground); + outline-offset: 0.2rem; + } + .product-option-input.disabled + .product-option-button { opacity: 0.45; text-decoration: line-through; From 543f8f039b0218c6a1bfd7b7896ac733f996ca3b Mon Sep 17 00:00:00 2001 From: MisterDetour Date: Tue, 6 Oct 2026 14:34:00 -0700 Subject: [PATCH 091/110] Add focus visible styles --- snippets/variant-selector.liquid | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/snippets/variant-selector.liquid b/snippets/variant-selector.liquid index a55be6e9f..e75f537cb 100644 --- a/snippets/variant-selector.liquid +++ b/snippets/variant-selector.liquid @@ -74,8 +74,9 @@ } .product-option-input:focus-visible + .product-option-button { - outline: 0.2rem solid var(--color-foreground); - outline-offset: 0.2rem; + border-color: var(--color-foreground); + box-shadow: 0 0 0 0.2rem var(--color-background), 0 0 0 0.4rem var(--color-foreground); + outline: none; } .product-option-input.disabled + .product-option-button { From 919b5cf78bdf3083e214472dffdb7b69dd0aae21 Mon Sep 17 00:00:00 2001 From: MisterDetour Date: Tue, 6 Oct 2026 14:38:15 -0700 Subject: [PATCH 092/110] Add focus visible styles --- TODO.md | 8 -------- assets/global.css | 7 +++++++ sections/header.liquid | 17 +++++++++++++++-- 3 files changed, 22 insertions(+), 10 deletions(-) diff --git a/TODO.md b/TODO.md index 74f349bc4..b1e821f53 100644 --- a/TODO.md +++ b/TODO.md @@ -1,13 +1,6 @@ # MVP -Product variant swatches are not keyboard-friendly - -In snippets/variant-selector.liquid, the radio inputs are visually hidden using opacity: 0 and pointer-events: none. - -The visible label has no focus-visible styling, so keyboard users can tab to the hidden input but won’t get a clear visual cue. - -This should be updated to keep the control focus visible while preserving the custom appearance. Mobile navigation needs a bit more keyboard polish @@ -18,7 +11,6 @@ But when the menu is open, there is no visible focus treatment on the summary tr - better styles overall -- accessability - Go through all templates again - Clean up all code - particularly cart and pdp js diff --git a/assets/global.css b/assets/global.css index e103574b8..415d1ef08 100644 --- a/assets/global.css +++ b/assets/global.css @@ -261,6 +261,13 @@ h6 { display: flex; align-items: center; gap: 0.5rem; + border-radius: 0.4rem; +} + +.site-nav-summary:focus-visible, +.site-nav-toggle:focus-visible { + outline: 0.2rem solid var(--color-foreground); + outline-offset: 0.2rem; } .site-nav-summary::-webkit-details-marker { diff --git a/sections/header.liquid b/sections/header.liquid index a3e6d3528..15e2eae04 100644 --- a/sections/header.liquid +++ b/sections/header.liquid @@ -86,6 +86,13 @@ navMenu.classList.add('anim-ready'); } + function closeMenu() { + if (!navMenu.classList.contains('open')) return; + + navMenu.classList.remove('open'); + updateMenuAccessibility(); + } + if (navToggle && navMenu) { updateMenuAccessibility(); @@ -95,10 +102,16 @@ document.body.classList.toggle('overflow-hidden', isOpen && mobileQuery.matches); }); + document.addEventListener('keydown', function (event) { + if (event.key === 'Escape' && mobileQuery.matches && navMenu.classList.contains('open')) { + closeMenu(); + navToggle.focus(); + } + }); + siteHeader?.addEventListener('click', function (event) { if (event.target === siteHeader && navMenu.classList.contains('open')) { - navMenu.classList.remove('open'); - updateMenuAccessibility(); + closeMenu(); } }); From 83438e53dca361554026aa3a9114344079f0bcd8 Mon Sep 17 00:00:00 2001 From: MisterDetour Date: Tue, 6 Oct 2026 14:43:20 -0700 Subject: [PATCH 093/110] Fix a few accessability errors --- sections/header.liquid | 4 ++-- snippets/pagination.liquid | 3 ++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/sections/header.liquid b/sections/header.liquid index 15e2eae04..0f11c19a9 100644 --- a/sections/header.liquid +++ b/sections/header.liquid @@ -11,7 +11,7 @@ {{ shop.name | link_to: routes.root_url }} -