goodnocodetools 1.0.0 → 1.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -1,25 +1,155 @@
1
- # Finsweet Developer Starter
1
+ # goodnocodetools
2
2
 
3
- A starter template for both Client & Power projects.
3
+ Custom TypeScript scripts for Webflow projects, built with the Finsweet Developer Starter.
4
4
 
5
- Before starting to work with this template, please take some time to read through the documentation.
5
+ ## 🚀 Quick Start
6
6
 
7
- ## Reference
7
+ ### Development
8
+
9
+ ```bash
10
+ pnpm install # Install dependencies
11
+ pnpm dev # Start dev server at localhost:3000
12
+ ```
13
+
14
+ Visit your Webflow site with `?dev=true` to load local scripts:
15
+ ```
16
+ https://yoursite.webflow.io?dev=true&debug=true
17
+ ```
18
+
19
+ ### Production
20
+
21
+ Scripts are published to npm and served via jsDelivr CDN:
22
+ ```
23
+ https://cdn.jsdelivr.net/npm/goodnocodetools@1/dist/global/index.js
24
+ ```
25
+
26
+ ## 📦 Available Scripts
27
+
28
+ - **`global/index.js`** (1.2KB) - Site-wide utilities and debug logging
29
+ - **`tools/index.js`** (3.8KB) - /tools page (filters, counter, click trigger)
30
+ - **`vendor/dashboard/index.js`** (1.3KB) - /vendor/dashboard (checkbox sync, add listing)
31
+ - **`vendor/claim-listing/index.js`** (460B) - /vendor/claim-listing (slug capture)
32
+ - **`vendor/thank-you/index.js`** (1.2KB) - /vendor/thank-you (Memberstack save)
33
+
34
+ ## 🛠️ Development Workflow
35
+
36
+ See **[WORKFLOW.md](./WORKFLOW.md)** for complete development guide.
37
+
38
+ **Quick reference:**
39
+ ```bash
40
+ pnpm dev # Start dev server
41
+ pnpm build # Build production bundles
42
+ pnpm check # TypeScript type checking
43
+ pnpm lint # Check code style
44
+ ```
45
+
46
+ ## 🔄 Publishing
47
+
48
+ ### Manual Publishing
49
+ ```bash
50
+ npm version patch # Bump version (patch/minor/major)
51
+ npm publish # Publish to npm
52
+ git push --tags # Push tags to GitHub
53
+ ```
54
+
55
+ ### Automated Publishing (GitHub Actions)
56
+
57
+ See **[GITHUB_ACTIONS_SETUP.md](./GITHUB_ACTIONS_SETUP.md)** for setup instructions.
58
+
59
+ Once configured:
60
+ ```bash
61
+ npx changeset # Describe changes
62
+ git add .
63
+ git commit -m "Add new feature"
64
+ git push # Creates "Version Packages" PR
65
+ # Merge PR → auto-publishes to npm
66
+ ```
67
+
68
+ ## 📝 Webflow Integration
8
69
 
9
- - [Included tools](#included-tools)
10
- - [Requirements](#requirements)
11
- - [Getting started](#getting-started)
12
- - [Installing](#installing)
13
- - [Building](#building)
14
- - [Serving files on development mode](#serving-files-on-development-mode)
15
- - [Building multiple files](#building-multiple-files)
16
- - [Setting up a path alias](#setting-up-a-path-alias)
17
- - [Contributing guide](#contributing-guide)
18
- - [Pre-defined scripts](#pre-defined-scripts)
19
- - [CI/CD](#cicd)
20
- - [Continuous Integration](#continuous-integration)
21
- - [Continuous Deployment](#continuous-deployment)
22
- - [How to automatically deploy updates to npm](#how-to-automatically-deploy-updates-to-npm)
70
+ Add to **Project Settings > Custom Code > Head**:
71
+
72
+ ```html
73
+ <script>
74
+ (function() {
75
+ const isDev = new URLSearchParams(window.location.search).get('dev') === 'true';
76
+ const baseUrl = isDev
77
+ ? 'http://localhost:3000'
78
+ : 'https://cdn.jsdelivr.net/npm/goodnocodetools@1/dist';
79
+
80
+ window.loadScript = function(path) {
81
+ const script = document.createElement('script');
82
+ script.src = baseUrl + '/' + path;
83
+ script.defer = true;
84
+ document.head.appendChild(script);
85
+ };
86
+
87
+ window.loadScript('global/index.js');
88
+ })();
89
+ </script>
90
+ ```
91
+
92
+ Add to **Page Settings > Custom Code > Before `</body>`**:
93
+
94
+ ```html
95
+ <!-- /tools page -->
96
+ <script>window.loadScript('tools/index.js');</script>
97
+
98
+ <!-- /vendor/dashboard page -->
99
+ <script>window.loadScript('vendor/dashboard/index.js');</script>
100
+
101
+ <!-- /vendor/claim-listing page -->
102
+ <script>window.loadScript('vendor/claim-listing/index.js');</script>
103
+
104
+ <!-- /vendor/thank-you page -->
105
+ <script>window.loadScript('vendor/thank-you/index.js');</script>
106
+ ```
107
+
108
+ ## 🔍 Debug Mode
109
+
110
+ Add `?debug=true` to any URL to enable console logging:
111
+ ```
112
+ https://yoursite.webflow.io/tools?debug=true
113
+ ```
114
+
115
+ All scripts use `window.debug()` for logging, which only outputs when debug mode is enabled.
116
+
117
+ ## 📚 Documentation
118
+
119
+ - **[WORKFLOW.md](./WORKFLOW.md)** - Development workflow and daily tasks
120
+ - **[GITHUB_ACTIONS_SETUP.md](./GITHUB_ACTIONS_SETUP.md)** - Automated publishing setup
121
+
122
+ ## 🏗️ Project Structure
123
+
124
+ ```
125
+ src/
126
+ ├── global/ # Site-wide scripts (all pages)
127
+ ├── tools/ # /tools page scripts
128
+ ├── vendor/ # /vendor/* page scripts
129
+ │ ├── dashboard/
130
+ │ ├── claim-listing/
131
+ │ └── thank-you/
132
+ ├── utils/ # Shared utilities
133
+ └── types/ # TypeScript definitions
134
+ ```
135
+
136
+ ## 🛡️ Scripts Overview
137
+
138
+ ### Global Scripts
139
+ - Debug utilities (`window.debug`, `window.debugError`, `window.debugWarn`)
140
+ - Pricing switch with GSAP animations
141
+
142
+ ### Tools Page
143
+ - Filter clear button visibility
144
+ - Click trigger mirroring
145
+ - Results counter with GSAP animation
146
+
147
+ ### Vendor Pages
148
+ - **Dashboard:** Checkbox label sync, add listing button with Memberstack
149
+ - **Claim Listing:** Tool slug capture from URL
150
+ - **Thank You:** Save claimed tool to Memberstack member JSON
151
+
152
+ ## Reference
23
153
 
24
154
  ## Included tools
25
155
 
@@ -1,158 +1 @@
1
- "use strict";
2
- (() => {
3
- // bin/live-reload.js
4
- new EventSource(`${"http://localhost:3000"}/esbuild`).addEventListener("change", () => location.reload());
5
-
6
- // src/utils/pricing-switch.ts
7
- function initPricingSwitch(options = {}) {
8
- if (typeof gsap === "undefined") {
9
- window.debugWarn("[pricing-switch] GSAP not loaded yet, retrying in 100ms");
10
- setTimeout(() => initPricingSwitch(options), 100);
11
- return;
12
- }
13
- const DEFAULT_STATE = (options.defaultState || "annual").toLowerCase();
14
- const defaultIsMonthly = DEFAULT_STATE === "monthly";
15
- const SECTION_SELECTOR = options.sectionSelector || "[data-pricing-section]";
16
- const CLIP_MONTHLY = "inset(0% 50% 0% 0% round 100rem)";
17
- const CLIP_ANNUAL = "inset(0% 0% 0% 50% round 100rem)";
18
- window.debug("[pricing-switch] Initializing with options:", options);
19
- function showEl(el) {
20
- if (!el) return;
21
- el.style.display = "";
22
- }
23
- function hideEl(el) {
24
- if (!el) return;
25
- el.style.display = "none";
26
- }
27
- function blurIn(el, delay = 0) {
28
- if (!el) return;
29
- gsap.killTweensOf(el);
30
- gsap.fromTo(
31
- el,
32
- { autoAlpha: 0, filter: "blur(4px)" },
33
- { autoAlpha: 1, filter: "blur(0px)", duration: 0.28, delay, ease: "power1.out" }
34
- );
35
- }
36
- function swapCard(card, isMonthly) {
37
- const monthlyWrap = card.querySelector('[data-price="monthly"]');
38
- const annualWrap = card.querySelector('[data-price="annual"]');
39
- const monthlyActions = card.querySelector(
40
- '[data-price-element="actions"][data-price="monthly"]'
41
- );
42
- const annualActions = card.querySelector(
43
- '[data-price-element="actions"][data-price="annual"]'
44
- );
45
- if (monthlyWrap && annualWrap) {
46
- const showWrap = isMonthly ? monthlyWrap : annualWrap;
47
- const hideWrap = isMonthly ? annualWrap : monthlyWrap;
48
- hideEl(hideWrap);
49
- showEl(showWrap);
50
- const price = showWrap.querySelector('[data-price-element="price"]');
51
- const term = showWrap.querySelector('[data-price-element="term"]');
52
- blurIn(price, 0);
53
- blurIn(term, 0.06);
54
- }
55
- if (monthlyActions && annualActions) {
56
- const showActions = isMonthly ? monthlyActions : annualActions;
57
- const hideActions = isMonthly ? annualActions : monthlyActions;
58
- hideEl(hideActions);
59
- showEl(showActions);
60
- }
61
- }
62
- function applyState(sectionEl, isMonthly, animateMask) {
63
- const darkLabelContainer = sectionEl.querySelector(
64
- ".pricing-switch_label-container.is-dark"
65
- );
66
- if (darkLabelContainer) {
67
- gsap.to(darkLabelContainer, {
68
- clipPath: isMonthly ? CLIP_MONTHLY : CLIP_ANNUAL,
69
- duration: animateMask ? 0.4 : 0,
70
- ease: "power2.inOut"
71
- });
72
- }
73
- sectionEl.querySelectorAll("[data-price-card]").forEach((card) => {
74
- swapCard(card, isMonthly);
75
- });
76
- }
77
- function initSection(sectionEl) {
78
- const switchButton = sectionEl.querySelector("#payment-switch") || sectionEl.querySelector("[data-payment-switch]");
79
- const darkLabelContainer = sectionEl.querySelector(
80
- ".pricing-switch_label-container.is-dark"
81
- );
82
- if (!switchButton || !darkLabelContainer) {
83
- window.debugWarn("[pricing-switch] Missing switch button or label container in section");
84
- return;
85
- }
86
- const initialIsMonthly = defaultIsMonthly;
87
- switchButton.setAttribute("aria-checked", initialIsMonthly ? "true" : "false");
88
- gsap.set(darkLabelContainer, {
89
- clipPath: initialIsMonthly ? CLIP_MONTHLY : CLIP_ANNUAL
90
- });
91
- sectionEl.querySelectorAll("[data-price-card]").forEach((card) => {
92
- const monthlyWrap = card.querySelector('[data-price="monthly"]');
93
- const annualWrap = card.querySelector('[data-price="annual"]');
94
- const monthlyActions = card.querySelector(
95
- '[data-price-element="actions"][data-price="monthly"]'
96
- );
97
- const annualActions = card.querySelector(
98
- '[data-price-element="actions"][data-price="annual"]'
99
- );
100
- if (monthlyWrap && annualWrap) {
101
- if (initialIsMonthly) {
102
- showEl(monthlyWrap);
103
- hideEl(annualWrap);
104
- } else {
105
- showEl(annualWrap);
106
- hideEl(monthlyWrap);
107
- }
108
- }
109
- if (monthlyActions && annualActions) {
110
- if (initialIsMonthly) {
111
- showEl(monthlyActions);
112
- hideEl(annualActions);
113
- } else {
114
- showEl(annualActions);
115
- hideEl(monthlyActions);
116
- }
117
- }
118
- });
119
- if (switchButton.dataset.pricingSwitchBound === "true") {
120
- window.debug("[pricing-switch] Section already initialized, skipping");
121
- return;
122
- }
123
- switchButton.dataset.pricingSwitchBound = "true";
124
- switchButton.addEventListener("click", function() {
125
- const wasMonthly = this.getAttribute("aria-checked") === "true";
126
- this.setAttribute("aria-checked", wasMonthly ? "false" : "true");
127
- window.debug("[pricing-switch] Toggled to:", wasMonthly ? "annual" : "monthly");
128
- applyState(sectionEl, !wasMonthly, true);
129
- });
130
- window.debug("[pricing-switch] Section initialized");
131
- }
132
- const sections = document.querySelectorAll(SECTION_SELECTOR);
133
- window.debug("[pricing-switch] Found sections:", sections.length);
134
- sections.forEach(initSection);
135
- }
136
-
137
- // src/global/index.ts
138
- window.debug = (...args) => {
139
- new URLSearchParams(location.search).get("debug") === "true" && console.log(...args);
140
- };
141
- window.debugError = (...args) => {
142
- new URLSearchParams(location.search).get("debug") === "true" && console.error(...args);
143
- };
144
- window.debugWarn = (...args) => {
145
- new URLSearchParams(location.search).get("debug") === "true" && console.warn(...args);
146
- };
147
- window.Webflow ||= [];
148
- window.Webflow.push(() => {
149
- window.debug("Webflow initialized - debug mode active");
150
- if (document.querySelector("[data-pricing-section]")) {
151
- initPricingSwitch({
152
- defaultState: "monthly",
153
- sectionSelector: "[data-pricing-section]"
154
- });
155
- }
156
- });
157
- })();
158
- //# sourceMappingURL=index.js.map
1
+ "use strict";(()=>{function p(o={}){if(typeof gsap>"u"){window.debugWarn("[pricing-switch] GSAP not loaded yet, retrying in 100ms"),setTimeout(()=>p(o),100);return}let f=(o.defaultState||"annual").toLowerCase()==="monthly",S=o.sectionSelector||"[data-pricing-section]",w="inset(0% 50% 0% 0% round 100rem)",h="inset(0% 0% 0% 50% round 100rem)";window.debug("[pricing-switch] Initializing with options:",o);function l(e){e&&(e.style.display="")}function s(e){e&&(e.style.display="none")}function m(e,t=0){e&&(gsap.killTweensOf(e),gsap.fromTo(e,{autoAlpha:0,filter:"blur(4px)"},{autoAlpha:1,filter:"blur(0px)",duration:.28,delay:t,ease:"power1.out"}))}function y(e,t){let a=e.querySelector('[data-price="monthly"]'),i=e.querySelector('[data-price="annual"]'),n=e.querySelector('[data-price-element="actions"][data-price="monthly"]'),c=e.querySelector('[data-price-element="actions"][data-price="annual"]');if(a&&i){let r=t?a:i;s(t?i:a),l(r);let d=r.querySelector('[data-price-element="price"]'),T=r.querySelector('[data-price-element="term"]');m(d,0),m(T,.06)}if(n&&c){let r=t?n:c;s(t?c:n),l(r)}}function b(e,t,a){let i=e.querySelector(".pricing-switch_label-container.is-dark");i&&gsap.to(i,{clipPath:t?w:h,duration:a?.4:0,ease:"power2.inOut"}),e.querySelectorAll("[data-price-card]").forEach(n=>{y(n,t)})}function L(e){let t=e.querySelector("#payment-switch")||e.querySelector("[data-payment-switch]"),a=e.querySelector(".pricing-switch_label-container.is-dark");if(!t||!a){window.debugWarn("[pricing-switch] Missing switch button or label container in section");return}let i=f;if(t.setAttribute("aria-checked",i?"true":"false"),gsap.set(a,{clipPath:i?w:h}),e.querySelectorAll("[data-price-card]").forEach(n=>{let c=n.querySelector('[data-price="monthly"]'),r=n.querySelector('[data-price="annual"]'),u=n.querySelector('[data-price-element="actions"][data-price="monthly"]'),d=n.querySelector('[data-price-element="actions"][data-price="annual"]');c&&r&&(i?(l(c),s(r)):(l(r),s(c))),u&&d&&(i?(l(u),s(d)):(l(d),s(u)))}),t.dataset.pricingSwitchBound==="true"){window.debug("[pricing-switch] Section already initialized, skipping");return}t.dataset.pricingSwitchBound="true",t.addEventListener("click",function(){let n=this.getAttribute("aria-checked")==="true";this.setAttribute("aria-checked",n?"false":"true"),window.debug("[pricing-switch] Toggled to:",n?"annual":"monthly"),b(e,!n,!0)}),window.debug("[pricing-switch] Section initialized")}let g=document.querySelectorAll(S);window.debug("[pricing-switch] Found sections:",g.length),g.forEach(L)}window.debug=(...o)=>{new URLSearchParams(location.search).get("debug")==="true"&&console.log(...o)};window.debugError=(...o)=>{new URLSearchParams(location.search).get("debug")==="true"&&console.error(...o)};window.debugWarn=(...o)=>{new URLSearchParams(location.search).get("debug")==="true"&&console.warn(...o)};window.Webflow||(window.Webflow=[]);window.Webflow.push(()=>{window.debug("Webflow initialized - debug mode active"),document.querySelector("[data-pricing-section]")&&p({defaultState:"monthly",sectionSelector:"[data-pricing-section]"})});})();
package/package.json CHANGED
@@ -1,8 +1,8 @@
1
1
  {
2
2
  "name": "goodnocodetools",
3
- "version": "1.0.0",
3
+ "version": "1.0.1",
4
4
  "description": "Custom scripts for Webflow projects.",
5
- "homepage": "https://github.com/liammews/goodnocodetools#readme",
5
+ "homepage": "https://github.com/liammews/goodnocodetoolsv2#readme",
6
6
  "license": "ISC",
7
7
  "keywords": [],
8
8
  "author": {
@@ -10,27 +10,15 @@
10
10
  },
11
11
  "repository": {
12
12
  "type": "git",
13
- "url": "git+https://github.com/liammews/goodnocodetools.git"
13
+ "url": "git+https://github.com/liammews/goodnocodetoolsv2.git"
14
14
  },
15
15
  "bugs": {
16
- "url": "https://github.com/liammews/goodnocodetools/issues"
16
+ "url": "https://github.com/liammews/goodnocodetoolsv2/issues"
17
17
  },
18
18
  "type": "module",
19
19
  "files": [
20
20
  "dist"
21
21
  ],
22
- "scripts": {
23
- "dev": "cross-env NODE_ENV=development node ./bin/build.js",
24
- "build": "cross-env NODE_ENV=production node ./bin/build.js",
25
- "lint": "eslint ./src && prettier --check ./src",
26
- "lint:fix": "eslint ./src --fix",
27
- "check": "tsc --noEmit",
28
- "format": "prettier --write ./src",
29
- "test": "playwright test",
30
- "test:ui": "playwright test --ui",
31
- "release": "changeset publish",
32
- "update": "pnpm update -i -L -r"
33
- },
34
22
  "devDependencies": {
35
23
  "@changesets/changelog-git": "^0.2.0",
36
24
  "@changesets/cli": "^2.27.12",
@@ -53,5 +41,17 @@
53
41
  },
54
42
  "engines": {
55
43
  "pnpm": ">=10"
44
+ },
45
+ "scripts": {
46
+ "dev": "cross-env NODE_ENV=development node ./bin/build.js",
47
+ "build": "cross-env NODE_ENV=production node ./bin/build.js",
48
+ "lint": "eslint ./src && prettier --check ./src",
49
+ "lint:fix": "eslint ./src --fix",
50
+ "check": "tsc --noEmit",
51
+ "format": "prettier --write ./src",
52
+ "test": "playwright test",
53
+ "test:ui": "playwright test --ui",
54
+ "release": "changeset publish",
55
+ "update": "pnpm update -i -L -r"
56
56
  }
57
- }
57
+ }
@@ -1,7 +0,0 @@
1
- {
2
- "version": 3,
3
- "sources": ["../../bin/live-reload.js", "../../src/utils/pricing-switch.ts", "../../src/global/index.ts"],
4
- "sourcesContent": ["new EventSource(`${SERVE_ORIGIN}/esbuild`).addEventListener('change', () => location.reload());\n", "/**\n * Pricing switch configuration options\n */\ninterface PricingSwitchOptions {\n defaultState?: 'monthly' | 'annual';\n sectionSelector?: string;\n}\n\n/**\n * Initializes pricing toggle switches across all pricing sections on the page.\n * Supports multiple independent sections with monthly/annual toggle animations.\n *\n * @param options - Configuration options for the pricing switch\n */\nexport function initPricingSwitch(options: PricingSwitchOptions = {}) {\n // Check for GSAP, retry if not loaded yet\n if (typeof gsap === 'undefined') {\n window.debugWarn('[pricing-switch] GSAP not loaded yet, retrying in 100ms');\n setTimeout(() => initPricingSwitch(options), 100);\n return;\n }\n\n const DEFAULT_STATE = (options.defaultState || 'annual').toLowerCase();\n const defaultIsMonthly = DEFAULT_STATE === 'monthly';\n const SECTION_SELECTOR = options.sectionSelector || '[data-pricing-section]';\n const CLIP_MONTHLY = 'inset(0% 50% 0% 0% round 100rem)';\n const CLIP_ANNUAL = 'inset(0% 0% 0% 50% round 100rem)';\n\n window.debug('[pricing-switch] Initializing with options:', options);\n\n function showEl(el: HTMLElement | null) {\n if (!el) return;\n el.style.display = '';\n }\n\n function hideEl(el: HTMLElement | null) {\n if (!el) return;\n el.style.display = 'none';\n }\n\n function blurIn(el: HTMLElement | null, delay = 0) {\n if (!el) return;\n gsap.killTweensOf(el);\n gsap.fromTo(\n el,\n { autoAlpha: 0, filter: 'blur(4px)' },\n { autoAlpha: 1, filter: 'blur(0px)', duration: 0.28, delay, ease: 'power1.out' }\n );\n }\n\n function swapCard(card: Element, isMonthly: boolean) {\n const monthlyWrap = card.querySelector<HTMLElement>('[data-price=\"monthly\"]');\n const annualWrap = card.querySelector<HTMLElement>('[data-price=\"annual\"]');\n const monthlyActions = card.querySelector<HTMLElement>(\n '[data-price-element=\"actions\"][data-price=\"monthly\"]'\n );\n const annualActions = card.querySelector<HTMLElement>(\n '[data-price-element=\"actions\"][data-price=\"annual\"]'\n );\n\n if (monthlyWrap && annualWrap) {\n const showWrap = isMonthly ? monthlyWrap : annualWrap;\n const hideWrap = isMonthly ? annualWrap : monthlyWrap;\n hideEl(hideWrap);\n showEl(showWrap);\n\n const price = showWrap.querySelector<HTMLElement>('[data-price-element=\"price\"]');\n const term = showWrap.querySelector<HTMLElement>('[data-price-element=\"term\"]');\n blurIn(price, 0);\n blurIn(term, 0.06);\n }\n\n if (monthlyActions && annualActions) {\n const showActions = isMonthly ? monthlyActions : annualActions;\n const hideActions = isMonthly ? annualActions : monthlyActions;\n hideEl(hideActions);\n showEl(showActions);\n }\n }\n\n function applyState(sectionEl: Element, isMonthly: boolean, animateMask: boolean) {\n const darkLabelContainer = sectionEl.querySelector<HTMLElement>(\n '.pricing-switch_label-container.is-dark'\n );\n if (darkLabelContainer) {\n gsap.to(darkLabelContainer, {\n clipPath: isMonthly ? CLIP_MONTHLY : CLIP_ANNUAL,\n duration: animateMask ? 0.4 : 0,\n ease: 'power2.inOut',\n });\n }\n\n sectionEl.querySelectorAll('[data-price-card]').forEach((card) => {\n swapCard(card, isMonthly);\n });\n }\n\n function initSection(sectionEl: Element) {\n const switchButton =\n sectionEl.querySelector<HTMLElement>('#payment-switch') ||\n sectionEl.querySelector<HTMLElement>('[data-payment-switch]');\n const darkLabelContainer = sectionEl.querySelector<HTMLElement>(\n '.pricing-switch_label-container.is-dark'\n );\n\n if (!switchButton || !darkLabelContainer) {\n window.debugWarn('[pricing-switch] Missing switch button or label container in section');\n return;\n }\n\n const initialIsMonthly = defaultIsMonthly;\n\n // Ensure this section's switch starts in the desired state\n switchButton.setAttribute('aria-checked', initialIsMonthly ? 'true' : 'false');\n\n // Initial mask for this section\n gsap.set(darkLabelContainer, {\n clipPath: initialIsMonthly ? CLIP_MONTHLY : CLIP_ANNUAL,\n });\n\n // Initial visibility (no animation on load) for this section only\n sectionEl.querySelectorAll('[data-price-card]').forEach((card) => {\n const monthlyWrap = card.querySelector<HTMLElement>('[data-price=\"monthly\"]');\n const annualWrap = card.querySelector<HTMLElement>('[data-price=\"annual\"]');\n const monthlyActions = card.querySelector<HTMLElement>(\n '[data-price-element=\"actions\"][data-price=\"monthly\"]'\n );\n const annualActions = card.querySelector<HTMLElement>(\n '[data-price-element=\"actions\"][data-price=\"annual\"]'\n );\n\n if (monthlyWrap && annualWrap) {\n if (initialIsMonthly) {\n showEl(monthlyWrap);\n hideEl(annualWrap);\n } else {\n showEl(annualWrap);\n hideEl(monthlyWrap);\n }\n }\n\n if (monthlyActions && annualActions) {\n if (initialIsMonthly) {\n showEl(monthlyActions);\n hideEl(annualActions);\n } else {\n showEl(annualActions);\n hideEl(monthlyActions);\n }\n }\n });\n\n // Prevent double-binding if initPricingSwitch runs more than once\n if (switchButton.dataset.pricingSwitchBound === 'true') {\n window.debug('[pricing-switch] Section already initialized, skipping');\n return;\n }\n switchButton.dataset.pricingSwitchBound = 'true';\n\n // Click handler scoped to this section\n switchButton.addEventListener('click', function (this: HTMLElement) {\n const wasMonthly = this.getAttribute('aria-checked') === 'true';\n this.setAttribute('aria-checked', wasMonthly ? 'false' : 'true');\n window.debug('[pricing-switch] Toggled to:', wasMonthly ? 'annual' : 'monthly');\n applyState(sectionEl, !wasMonthly, true);\n });\n\n window.debug('[pricing-switch] Section initialized');\n }\n\n // Init all independent sections\n const sections = document.querySelectorAll(SECTION_SELECTOR);\n window.debug('[pricing-switch] Found sections:', sections.length);\n sections.forEach(initSection);\n}\n", "// Global debug logging functions\n// Usage: Add ?debug=true to URL to enable debug logging\nwindow.debug = (...args: unknown[]) => {\n new URLSearchParams(location.search).get('debug') === 'true' && console.log(...args);\n};\nwindow.debugError = (...args: unknown[]) => {\n new URLSearchParams(location.search).get('debug') === 'true' && console.error(...args);\n};\nwindow.debugWarn = (...args: unknown[]) => {\n new URLSearchParams(location.search).get('debug') === 'true' && console.warn(...args);\n};\n\nimport { initPricingSwitch } from '$utils/pricing-switch';\n\nwindow.Webflow ||= [];\nwindow.Webflow.push(() => {\n window.debug('Webflow initialized - debug mode active');\n\n // Initialize pricing switches if any exist on the page\n if (document.querySelector('[data-pricing-section]')) {\n initPricingSwitch({\n defaultState: 'monthly',\n sectionSelector: '[data-pricing-section]',\n });\n }\n});\n"],
5
- "mappings": "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",
6
- "names": []
7
- }