1<!DOCTYPE html><!-- This site was created in Webflow. https://webflow.com --><!-- Last Published: Wed May 13 2026 06:48:54 GMT+0000 (Coordinated Universal Time) --><html data-wf-domain="napa-canvas-website.webflow.io" data-wf-page="69ba6291152cc9f0778e4cb8" data-wf-site="69ab2b3b80f48170ba7e0b8f" lang="en"><head><meta charset="utf-8"/><link href="https://cdn.prod.website-files.com" rel="preconnect" crossorigin="anonymous"/><title>Solutions</title><meta content="Solutions" property="og:title"/><meta content="Solutions" name="twitter:title"/><meta content="width=device-width, initial-scale=1" name="viewport"/><meta content="Webflow" name="generator"/><link href="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/css/napa-canvas-website.webflow.shared.7f37e9e79.css" rel="stylesheet" type="text/css" integrity="sha384-fzfp555nmJrY98IbqElK1Y5kLZFhRa+Ft58zih4i49bN5nr/ZpfYxTuplJjJ8Eyi" crossorigin="anonymous"/><style>html.w-mod-js:not(.w-mod-ix3) :is(.div-block-3) {visibility: hidden !important;}</style><link href="https://fonts.googleapis.com" rel="preconnect"/><link href="https://fonts.gstatic.com" rel="preconnect" crossorigin="anonymous"/>
1<script src="https://ajax.googleapis.com/ajax/libs/webfont/1.6.26/webfont.js" type="text/javascript"></script>
1<script type="text/javascript">WebFont.load({ google: { families: ["Montserrat:100,100italic,200,200italic,300,300italic,400,400italic,500,500italic,600,600italic,700,700italic,800,800italic,900,900italic","Inter:300,400,500,600,700","Tenor Sans:300,400,500,600,700"] }});</script>
1<script type="text/javascript">!function(o,c){var n=c.documentElement,t=" w-mod-";n.className+=t+"js",("ontouchstart"in o||o.DocumentTouch&&c instanceof DocumentTouch)&&(n.className+=t+"touch")}(window,document);</script>
1<link href="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69afb9f6a01fa3d13b429153_favicon-32x32.png" rel="shortcut icon" type="image/x-icon"/><link href="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69afba07c8531cd9d5f8aa0f_favicon-256x256.png" rel="apple-touch-icon"/><!-- 1. Paste this code as high in the <head> of the page as possible: --> 2<!-- Google Tag Manager -->
3<script>
vendor: 327 bytes, lines 3-7
3(function(w,d,s,l,i){w[l]=w[l]||[];w[l].push({'gtm.start': 4new Date().getTime(),event:'gtm.js'});var f=d.getElementsByTagName(s)[0], 5j=d.createElement(s),dl=l!='dataLayer'?'&l='+l:'';j.async=true;j.src= 6'https://www.googletagmanager.com/gtm.js?id='+i+dl;f.parentNode.insertBefore(j,f); 7})(window,document,'script','dataLayer','
7GTM-NTQ5SPMC
vendor: 3 bytes, line 7
7');
7</script>
7 8<!-- End Google Tag Manager --> 9 10 11 12<style> 13 .product-total-price:before { 14 content: '$'; 15 } 16 .product-total-price:after { 17 content: '.00'; 18 } 19 /*a.footer-menu-link.w--current .footer-menu-text, 20 a.footer-menu-link.w--current .footer-menu-text { 21 color: #dfa961 !important; 22 }*/ 23 .footer-menu-icon-link:hover .footer-menu-icon-text { 24 color: #dfa961 !important; 25 text-decoration-color: #dfa961 !important; 26 } 27 /* Solution Page Css Start */ 28 .solution-collection-list::before, 29 .solution-collection-list::after { 30 content: none !important; 31 display: none !important; 32 } 33 .solution-collection-list { 34 margin-left: auto !important; 35 margin-right: auto !important; 36 } 37 .marquee-wrapper { 38 overflow: hidden; 39 width: 100%; 40 } 41 .marquee-track { 42 display: flex; 43 width: max-content; 44 will-change: transform; 45 } 46 47 .clients-wrapper-three { 48 display: flex; 49 gap: 32px; 50 flex-shrink: 0; 51 52 /* ð¥ THIS IS THE FIX */ 53 padding-left: 32px; 54 } 55 /* FAQ section css start */ 56 57 .faq-answer { 58 height: 0; 59 overflow: hidden; 60 transition: height 0.35s ease; 61 } 62 63 .faq-item.active .faq-answer { 64 height: auto; 65 } 66 .faq-icon { 67 transition: transform 0.4s ease; 68 } 69 .faq-answer a { 70 color: #1e1e1e; 71 transition: color 0.2s ease; 72 } 73 74 .faq-answer a:hover { 75 color: #dfa961; 76 } 77 /* Solution Page Css End */ 78 /* Privacy Policy css start */ 79 .p-tag a { 80 color: #1e1e1e; 81 transition: color 0.2s ease; 82 } 83 84 .p-tag a:hover { 85 color: #dfa961; 86 } 87 /* Privacy policy css end */ 88 89 .home-body { 90 overflow-x: hidden; 91 } 92 93 .w-layout-blockcontainer.horizantal-container.w-container { 94 margin-left: auto !important; 95 margin-right: 0; 96 max-width: calc((100% - 1320px) / 2 + 1320px) !important; 97 } 98 .collection-item img.image-40 { 99 transition: transform 0.5s; 100 } 101 .collection-item:hover img.image-40 { 102 transform: scale(1.1) !important; 103 } 104 /* TEXT COLOR CHANGE */ 105 .collection-item .related-projects { 106 transition: color 0.3s ease; 107 } 108 109 .collection-item:hover .related-projects { 110 /* color: #dfa961; */ 111 } 112 a.nav-link.w-nav-link.w--current, 113 a.nav-link:hover { 114 color: var(--_napa-canvas-colors---muted-gold) !important; 115 } 116 .horizontal-section { 117 position: relative; 118 } 119 .horizantal-scroll-track { 120 display: flex; 121 position: sticky; 122 top: 0; 123 will-change: transform; 124 } 125 @media screen and (max-width: 991px) { 126 .nav-link.white-text:active, 127 .nav-link.white-text:visited { 128 color: initial; 129 } 130 } 131 132 /* filter radio styling start*/ 133 /* Base radio wrapper â button */ 134 .w-radio { 135 display: inline-flex; 136 align-items: center; 137 justify-content: center; 138 padding: 14px 18px; 139 background-color: rgba(42, 79, 139, 0.08); 140 cursor: pointer; 141 /* transition: all 0.25s ease; */ 142 } 143 144 /* Hide native radio */ 145 .w-radio-input { 146 display: none; 147 } 148 149 /* Label text */ 150 .w-radio .w-form-label { 151 margin: 0; 152 font-size: 14px; 153 font-weight: 400; 154 color: #1e1e1e; 155 cursor: pointer; 156 font-family: Satoshi, Arial, sans-serif; 157 line-height: initial; 158 letter-spacing: 0; 159 } 160 /* Hover state */ 161 .w-radio:hover { 162 border-color: #000; 163 } 164 /* Active text */ 165 .w-radio-input:checked + .w-form-label, 166 .w-radio:hover .w-form-label { 167 color: #fff; 168 } 169 /* Active button (modern browsers) */ 170 .w-radio:has(.w-radio-input:checked), 171 .w-radio:hover { 172 background-color: #dfa961; 173 border-color: #fff; 174 } 175 /* Optional press effect */ 176 .w-radio:active { 177 transform: scale(0.97); 178 } 179 /* filter radio styling end*/ 180 181 a.page-number.w--current, 182 a.pagination-previous.w--current { 183 background-color: #dfa961; 184 color: #fff; 185 } 186 a.pagination-previous.is-list-pagination-disabled path, 187 a.pagination-next.is-list-pagination-disabled path { 188 color: rgba(30, 30, 30, 0.3); 189 } 190 191 .filter-tab-row.w-dyn-items, 192 .w-form-label { 193 white-space: nowrap; 194 } 195 196 /* .pagination-row { 197 display: none; 198} */ 199 200/* detail page gallery */ 201.detail-gallery-list { 202 display: flex; 203 gap: 24px; 204 flex-wrap: wrap; 205} 206
207.detail-gallery-list .w-dyn-item { 208 box-sizing: border-box; 209} 210.detail-gallery-list img { 211 width: 100%; 212 height: 100%; 213 object-fit: cover !important; 214} 215/* 1st */ 216.detail-gallery-list .w-dyn-item:nth-child(5n + 1) { 217 width: calc(61.67% - 12px); 218 height: 678px; 219} 220 221/* 2nd */ 222.detail-gallery-list .w-dyn-item:nth-child(5n + 2) { 223 width: calc(38.33% - 12px); 224 height: 678px; 225} 226 227/* 3rd */ 228.detail-gallery-list .w-dyn-item:nth-child(5n + 3) { 229 width: calc(41.52% - 12px); 230 height: 545px; 231} 232 233/* 4th */ 234.detail-gallery-list .w-dyn-item:nth-child(5n + 4) { 235 width: calc(58.48% - 12px); 236 height: 545px; 237} 238 239/* 5th (FULL WIDTH) */ 240.detail-gallery-list .w-dyn-item:nth-child(5n + 5) { 241 width: 100%; 242 height: 793px; 243} 244.collection-list.solution-collection-list.portfolio-listing-sec { 245 grid-template-rows: initial !important; 246} 247/* tab horizantal scroll bar styling */ 248/* .collection-item:hover .detail-page-category{ 249 color: #1e1e1e !important; 250} 251.detail-page-category { 252 transition: initial !important; 253} */ 254@media screen and (max-width: 930px) { 255 .filter-tabs-scroll { 256 overflow-x: scroll; 257 } 258 .filter-tabs-scroll::-webkit-scrollbar {
259 height: 2px; 260 } 261 .filter-tabs-scroll::-webkit-scrollbar-track { 262 background: #2a4f8b50; 263 } 264 .filter-tabs-scroll::-webkit-scrollbar-thumb { 265 background-color: #dfa961; 266 border-radius: 10px; 267 } 268 .filter-tabs-scroll::-webkit-scrollbar-thumb:hover { 269 background-color: #dfa961; 270 } 271} 272 @media (max-width: 767px) { 273 .detail-gallery-list { 274 flex-direction: column; 275 gap: 16px; /* optional */ 276 } 277 278 .detail-gallery-list .w-dyn-item { 279 width: 100% !important; 280 height: 400px !important; 281 } 282} 283</style></head><body class="body"><div class="div-block-2"><section class="header-sec black-header"><div class="w-layout-blockcontainer container w-container"><div data-animation="default" data-collapse="medium" data-duration="400" data-easing="ease-in-quart" data-easing2="ease" role="banner" class="navbar w-nav"><div class="navbar-row"><div><a href="https://napa-canvas-website.webflow.io" class="w-inline-block"><img loading="lazy" src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69afbcf7e3d2db41ba5c2b0b_Logo.svg" alt="" class="logo-img"/></a></div><div class="header-menu-column"><div class="header-menu-inline"><nav role="navigation" class="nav-menu-3 w-nav-menu"><a href="/" class="nav-link w-nav-link">Home</a><a href="/solutions" aria-current="page" class="nav-link w-nav-link w--current">Solutions</a><a href="/portfolio" class="nav-link w-nav-link">Portfolio</a><a href="/about" class="nav-link w-nav-link">About</a><a href="/faq" class="nav-link w-nav-link">FAQ</a><a href="/pricing" class="nav-link w-nav-link">Pricing</a><a href="/contact" class="blue-button-icon hide-for-desktop w-inline-block"><div class="btn-text-white">Get in Touch</div><img src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c24a519c2a0d913bc4c45d_orange-right-icon.svg" loading="lazy" alt="" class="btn-icon"/></a></nav><a href="/contact" class="blue-button-icon hide-for-mobile w-inline-block"><div class="btn-text-white">Get in Touch</div><img src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c24a519c2a0d913bc4c45d_orange-right-icon.svg" loading="lazy" alt="" class="btn-icon"/></a></div><div class="menu-button-3 mobile-menu-bar w-nav-button"><div class="icon w-icon-nav-menu"></div></div></div></div></div></div></section></div><section class="section-8 banner-overlay"><div class="w-layout-blockcontainer container w-container"><div class="div-block-3"><div class="w-layout-blockcontainer container-36 w-container"><div class="text-satoshi-white-18">Solutions</div><h1 class="heading-tenor-white-42">Custom Shade Solutions, Designed Around You</h1></div><div class="w-layout-blockcontainer container-37 w-container"><div class="text-satoshi-white-18 solution-banner-content">Every Napa Canvas project begins with your space, your vision, and
283your needs. We bring your design to life with clarity, intention and precision.</div><div class="w-layout-hflex flex-block"><div class="w-layout-vflex"><a href="#explore-our-solution" class="link-block-9 w-inline-block"><div class="text-block-11 explore-product">Explore Our Solutions</div><img loading="lazy" src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c24df7f83d4d963305f414_Explore%20Solution%20Icon.svg" alt="" class="image-32"/></a></div></div></div></div></div></section><section class="section-9"><div class="w-layout-blockcontainer container w-container"><div class="div-block-4"><div class="white-heading-tenor-52">Enhance your outdoor living space and create functional shade, we guide you every step of the way.</div></div><div id="explore-our-solution" class="div-block-16"><div class="collection-list-wrapper w-dyn-list"><div role="list" class="collection-list solution-collection-list w-dyn-items"><div role="listitem" class="collection-item w-dyn-item"><a href="/portfolio?sun-shades" class="link-block-12 w-inline-block"><div class="collection-img-box"><img src="https://cdn.prod.website-files.com/69c27323fcf6b36fe968f58a/69cfa80c6ed7508bf44ce86e_sunshades%20img.jpg" loading="lazy" alt="" sizes="(max-width: 767px) 100vw, (max-width: 991px) 95vw, 848px" srcset="https://cdn.prod.website-files.com/69c27323fcf6b36fe968f58a/69cfa80c6ed7508bf44ce86e_sunshades%20img-p-500.jpg 500w, https://cdn.prod.website-files.com/69c27323fcf6b36fe968f58a/69cfa80c6ed7508bf44ce86e_sunshades%20img-p-800.jpg 800w, https://cdn.prod.website-files.com/69c27323fcf6b36fe968f58a/69cfa80c6ed7508bf44ce86e_sunshades%20img.jpg 848w" class="image-40"/></div><div class="div-block-12"><h3 class="heading-18">Sun Shades</h3><div class="div-block-6"><div class="text-block-7 related-projects">See Related Projects</div><img loading="lazy" src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69b1148ce4791c8169422443_Vector%201.svg" alt="" class="image-34"/></div></div></a></div><div role="listitem" class="collection-item w-dyn-item"><a href="/portfolio?cat=traditional-awnings-commercial" class="link-block-12 w-inline-block"><div class="collection-img-box"><img src="https://cdn.prod.website-files.com/69c27323fcf6b36fe968f58a/6a02e1a5894cc41cd1547bfb_Traditional%20Awnings%20(Commercial)%20cat%20img.jpg" loading="lazy" alt="" sizes="(max-width: 767px) 100vw, (max-width: 991px) 95vw, 848px" srcset="https://cdn.prod.website-files.com/69c27323fcf6b36fe968f58a/6a02e1a5894cc41cd1547bfb_Traditional%20Awnings%20(Commercial)%20cat%20img-p-500.jpg 500w, https://cdn.prod.website-files.com/69c27323fcf6b36fe968f58a/6a02e1a5894cc41cd1547bfb_Traditional%20Awnings%20(Commercial)%20cat%20img-p-800.jpg 800w, https://cdn.prod.website-files.com/69c27323fcf6b36fe968f58a/6a02e1a5894cc41cd1547bfb_Traditional%20Awnings%20(Commercial)%20cat%20img.jpg 848w" class="image-40"/></div><div class="div-block-12"><h3 class="heading-18">Traditional Awnings (Commercial)</h3><div class="div-block-6"><div class="text-block-7 related-projects">See Related Projects</div><img loading="lazy" src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69b1148ce4791c8169422443_Vector%201.svg" alt="" class="image-34"/></div></div></a></div><div role="listitem" class="collection-item w-dyn-item"><a href="/portfolio?cat=sail-shades" class="link-block-12 w-inline-block"><div class="collection-img-box"><img src="https://cdn.prod.website-files.com/69c27323fcf6b36fe968f58a/69e7203f8ea4cd4374d0c7a0_Sails%20Shades.jpg" loading="lazy" alt="" sizes="(max-width: 767px) 100vw, (max-width: 991px) 95vw, 848px" srcset="https://cdn.prod.website-files.com/69c27323fcf6b36fe968f58a/69e7203f8ea4cd4374d0c7a0_Sails%20Shades-p-500.jpg 500w, https://cdn.prod.website-files.com/69c27323fcf6b36fe968f58a/69e7203f8ea4cd4374d0c7a0_Sails%20Shades-p-800.jpg 800w, https://cdn.prod.website-files.com/69c27323fcf6b36fe968f58a/69e7203f8ea4cd4374d0c7a0_Sails%20Shades.jpg 848w" class="image-40"/></div><div class="div-block-12"><h3 class="heading-18">Sail Shades</h3><div class="div-block-6"><div class="text-block-7 related-projects">See Related Projects</div><img loading="lazy" src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69b1148ce4791c8169422443_Vector%201.svg" alt="" class="image-34"/></div></div></a></div><div role="listitem" class="collection-item w-dyn-item"><a href="/portfolio?traditional-awnings" class="link-block-12 w-inline-block"><div class="collection-img-box"><img src="https://cdn.prod.website-files.com/69c27323fcf6b36fe968f58a/69cfac8ced3be7bbd72cf1d4_Traditional%20awing%20img.jpg" loading="lazy" alt="" sizes="(max-width: 767px) 100v
283w, (max-width: 991px) 95vw, 848px" srcset="https://cdn.prod.website-files.com/69c27323fcf6b36fe968f58a/69cfac8ced3be7bbd72cf1d4_Traditional%20awing%20img-p-500.jpg 500w, https://cdn.prod.website-files.com/69c27323fcf6b36fe968f58a/69cfac8ced3be7bbd72cf1d4_Traditional%20awing%20img-p-800.jpg 800w, https://cdn.prod.website-files.com/69c27323fcf6b36fe968f58a/69cfac8ced3be7bbd72cf1d4_Traditional%20awing%20img.jpg 848w" class="image-40"/></div><div class="div-block-12"><h3 class="heading-18">Traditional Awnings</h3><div class="div-block-6"><div class="text-block-7 related-projects">See Related Projects</div><img loading="lazy" src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69b1148ce4791c8169422443_Vector%201.svg" alt="" class="image-34"/></div></div></a></div><div role="listitem" class="collection-item w-dyn-item"><a href="/portfolio?retractable-awnings" class="link-block-12 w-inline-block"><div class="collection-img-box"><img src="https://cdn.prod.website-files.com/69c27323fcf6b36fe968f58a/69cface6025a5122e756e1a5_Retractable%20img.jpg" loading="lazy" alt="" sizes="(max-width: 767px) 100vw, (max-width: 991px) 95vw, 848px" srcset="https://cdn.prod.website-files.com/69c27323fcf6b36fe968f58a/69cface6025a5122e756e1a5_Retractable%20img-p-500.jpg 500w, https://cdn.prod.website-files.com/69c27323fcf6b36fe968f58a/69cface6025a5122e756e1a5_Retractable%20img-p-800.jpg 800w, https://cdn.prod.website-files.com/69c27323fcf6b36fe968f58a/69cface6025a5122e756e1a5_Retractable%20img.jpg 848w" class="image-40"/></div><div class="div-block-12"><h3 class="heading-18">Retractable Awnings</h3><div class="div-block-6"><div class="text-block-7 related-projects">See Related Projects</div><img loading="lazy" src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69b1148ce4791c8169422443_Vector%201.svg" alt="" class="image-34"/></div></div></a></div></div></div></div><div class="w-layout-vflex flex-block-2 portfolio-cta"><a href="/portfolio" class="link-block-9 w-inline-block"><div class="text-block-11 explore-product">See All Portfolio</div><img loading="lazy" src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c272ec836112f98196d1fe_See%20all%20Portfolio.svg" alt="" class="image-32"/></a></div></div></section><section class="logos-title-small blue-bg blue-marquee"><div class="marquee-wrapper"><div class="marquee-track"><div class="clients-wrapper-three"><img src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c38e06540daf399a681713_Napa%20Canvas%20logo.svg" loading="lazy" alt="AriseHealth logo" class="clients-image-three"/><img src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c38e611bf0e61113fc3e79_Marquee%20Icon.svg" loading="lazy" alt="OE logo" class="clients-image-three"/><img src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c38e06540daf399a681713_Napa%20Canvas%20logo.svg" loading="lazy" alt="AriseHealth logo" class="clients-image-three"/><img src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c38e611bf0e61113fc3e79_Marquee%20Icon.svg" loading="lazy" alt="OE logo" class="clients-image-three"/><img src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c38e06540daf399a681713_Napa%20Canvas%20logo.svg" loading="lazy" alt="AriseHealth logo" class="clients-image-three"/><img src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c38e611bf0e61113fc3e79_Marquee%20Icon.svg" loading="lazy" alt="OE logo" class="clients-image-three"/><img src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c38e06540daf399a681713_Napa%20Canvas%20logo.svg" loading="lazy" alt="AriseHealth logo" class="clients-image-three"/><img src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c38e611bf0e61113fc3e79_Marquee%20Icon.svg" loading="lazy" alt="OE logo" class="clients-image-three"/><img src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c38e06540daf399a681713_Napa%20Canvas%20logo.svg" loading="lazy" alt="AriseHealth logo" class="clients-image-three"/><img src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c38e611bf0e61113fc3e79_Marquee%20Icon.svg" loading="lazy" alt="OE logo" class="clients-image-three"/></div></div></div></section><section class="section-10"><div class="w-layout-blockcontainer container w-container"><div class="div-block-9"><div id="w-node-_2805fdaf-764c-fec1-f4b0-4e0fff2e147d-778e4cb8" class="div-block-10"><h1 class="heading-tenor-white-42">Frequently Asked Questions</h1><div class="text-block-13 text-satoshi-white-18">Clear answers to common questions about implementation, integrations, and how our solution fits your business. Still have questions? Feel free to contact our support.</div><div class="w-layout-hflex flex-block"><div class="w-layout-vflex"><a href="/contact" class="link-block-9 get-in-touch-cta w-inline-block"><div class="text-block-11 get-in-touch">Get in Touch</div><img loading="lazy" src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c24a519c2a0d913bc4c45d_orange-right-icon.svg" alt="" class="get-touch-icon"/></a></div></div></div><div class="div-block-11 faq-wrapper"><div class="faq-list"><div class="faq-item"><div class="faq-question solution-faq-question"><div class="text-block-14">What types of projects do you work on?</div><img src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c3ddf88aeec579eab6e9d9_Chevron.svg" loading="lazy" alt="" class="faq-icon"/></div><div class="faq-answer"><div class="rich-text-block paragraph w-richtext"><p>We design and fabricate custom awnings, shade structures, and fabric solutions for residential homes, wineries, hospital
283ity spaces, and commercial properties. Every project is made to order to suit the specific space and requirements.</p></div></div></div><div class="faq-item"><div class="faq-question solution-faq-question"><div class="text-block-14">Do you work on both residential and commercial projects?</div><img src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c3ddf88aeec579eab6e9d9_Chevron.svg" loading="lazy" alt="" class="faq-icon"/></div><div class="faq-answer"><div class="rich-text-block paragraph w-richtext"><p>Yes, We work with homeowners as well as commercial clients, including wineries, restaurants, retail spaces, and other businesses that need durable, professionally installed shade solutions.</p></div></div></div><div class="faq-item"><div class="faq-question solution-faq-question"><div class="text-block-14">How much does a custom awning cost?</div><img src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c3ddf88aeec579eab6e9d9_Chevron.svg" loading="lazy" alt="" class="faq-icon"/></div><div class="faq-answer"><div class="rich-text-block paragraph w-richtext"><p>
283Pricing varies depending on size, materials, structural requirements, and installation complexity. Once we review your space and project goals, we provide a detailed proposal tailored to your project.</p></div></div></div><div class="faq-item"><div class="faq-question solution-faq-question"><div class="text-block-14">How long does a project take?</div><img src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c3ddf88aeec579eab6e9d9_Chevron.svg" loading="lazy" alt="" class="faq-icon"/></div><div class="faq-answer"><div class="rich-text-block paragraph w-richtext"><p>Most projects have an average lead time of about <strong>6 weeks</strong>.<br/>The process typically includes:</p><ul role="list"><li>Consultation and measurements</li><li>Design and material selection</li><li>Fabrication</li><li>Installation</li></ul><p>We provide a more specific timeline during the consultation phase.</p></div></div></div><div class="faq-item"><div class="faq-question solution-faq-question"><div class="text-block-14">What materials do you use?</div><img src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c3ddf88aeec579eab6e9d9_Chevron.svg" loading="lazy" alt="" class="faq-icon"/></div><div class="faq-answer"><div class="rich-text-block paragraph w-richtext"><p>We use high-quality performance fabrics and materials designed for outdoor environments, including:<br/></p><ul role="list"><li>Sunbrella fabrics</li><li>Commercial-grade vinyl materials</li><li>Screen fabrics</li></ul><p><strong>â</strong>These materials are selected for durability, weather resistance, and long-term outdoor performance.</p></div></div></div><div class="faq-item"><div class="faq-question solution-faq-question"><div class="text-block-14">How long do your awnings last?</div><img src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c3ddf88aeec579eab6e9d9_Chevron.svg" loading="lazy" alt="" class="faq-icon"/></div><div class="faq-answer"><div class="rich-text-block paragraph w-richtext"><p>Lifespan depends on the material, environmental exposure, and maintenance. With proper care, many installations last for many years. We select materials specifically for durability and long-term performance.</p></div></div></div><div class="faq-item"><div class="faq-question solution-faq-question"><div class="text-block-14">What warranties do you offer?</div><img src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c3ddf88aeec579eab6e9d9_Chevron.svg" loading="lazy" alt="" class="faq-icon"/></div><div class="faq-answer"><div class="rich-text-block paragraph w-richtext"><p>
283Warranty coverage varies depending on the materials used.</p><ul role="list"><li>Sunbrella fabrics include a 10-year manufacturer warranty</li><li>All stitching completed in-house carries a 10-year workmanship warranty</li></ul><p>Additional components may carry manufacturer warranties depending on the product.</p></div></div></div><div class="faq-item"><div class="faq-question solution-faq-question"><div class="text-block-14">Do you offer custom logos or branding for commercial projects?</div><img src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c3ddf88aeec579eab6e9d9_Chevron.svg" loading="lazy" alt="" class="faq-icon"/></div><div class="faq-answer"><div class="rich-text-block paragraph w-richtext"><p>Yes, We can incorporate logos, lettering, and branding elements into commercial awnings and fabric installations when required.</p></div></div></div><div class="faq-item"><div class="faq-question solution-faq-question"><div class="text-block-14">Do you handle installation?</div><img src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c3ddf88aeec579eab6e9d9_Chevron.svg" loading="lazy" alt="" class="faq-icon"/></div><div class="faq-answer"><div class="rich-text-block paragraph w-richtext"><p>Yes, Our team manages installation to ensure proper fit, safety, and long-term performance.</p></div></div></div><div class="faq-item"><div class="faq-question solution-faq-question"><div class="text-block-14">How do I maintain or clean my awning?</div><img src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c3ddf88aeec579eab6e9d9_Chevron.svg" loading="lazy" alt="" class="faq-icon"/></div><div class="faq-answer"><div class="rich-text-block paragraph w-richtext"><p>
283Maintenance varies depending on the fabric type. In general, periodic cleaning and basic care will extend the life of your awning.</p><p>For manufacturer-recommended cleaning methods, see: <a href="/cleaning-guide"><strong>Cleaning Guide</strong></a></p></div></div></div><div class="faq-item"><div class="faq-question solution-faq-question"><div class="text-block-14">Can existing awnings be repaired or re-covered?</div><img src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c3ddf88aeec579eab6e9d9_Chevron.svg" loading="lazy" alt="" class="faq-icon"/></div><div class="faq-answer"><div class="rich-text-block paragraph w-richtext"><p>In many cases, existing structures can be repaired or re-covered with new fabric. We can evaluate the condition of the frame and recommend the best approach.</p></div></div></div><div class="faq-item"><div class="faq-question solution-faq-question"><div class="text-block-14">What is the first step to start a project?</div><img src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c3ddf88aeec579eab6e9d9_Chevron.svg" loading="lazy" alt="" class="faq-icon"/></div><div class="faq-answer"><div class="rich-text-block paragraph w-richtext"><p>The first step is to request an estimate and share details about your space.</p><p><a href="/contact">â<strong>Start Here</strong></a></p><p><strong>â</strong>We will review your project information, discuss your goals, and guide you through the next steps.</p></div></div></div></div></div></div></div></section><section><div class="w-layout-blockcontainer w-container"><div class="div-block-13"><div class="div-block-14"></div><div class="div-block-15"></div></div></div></section><section class="section-12 carfted-in-house-copy"><div class="w-layout-blockcontainer container w-container"><div class="div-block-30 craft-block"><div class="w-layout-blockcontainer container-43 col1 w-container"><img src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c279543cf3cde35525babd_Line%201.png" loading="lazy" alt="" class="image-49"/><h1 class="heading-22">Crafted In-House in Napa Valley</h1></div><div class="w-layout-blockcontainer container-42 col2 w-container"><div class="text-block-19 craft-text">Unlike mass-produced alternatives, our products are made to order by skilled fabricators using proven materials and construction methods.</div></div></div></div></section><section class="footer-sec"><section class="logos-title-small"><div class="marquee-wrapper"><div class="marquee-track"><div class="clients-wrapper-three"><img src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c3982967a280eb9dd40115_Napa%20blue%20logo.svg" loading="lazy" alt="AriseHealth logo" class="clients-image-three"/><img src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c38e611bf0e61113fc3e79_Marquee%20Icon.svg" loading="lazy" alt="OE logo" class="clients-image-three"/><img src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c3982967a280eb9dd40115_Napa%20blue%20logo.svg" loading="lazy" alt="AriseHealth logo" class="clients-image-three"/><img src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c38e611bf0e61113fc3e79_Marquee%20Icon.svg" loading="lazy" alt="OE logo" class="clients-image-three"/><img src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c3982967a280eb9dd40115_Napa%20blue%20logo.svg" loading="lazy" alt="AriseHealth logo" class="clients-image-three"/><img src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c38e611bf0e61113fc3e79_Marquee%20Icon.svg" loading="lazy" alt="OE logo" class="clients-image-three"/><img src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c3982967a280eb9dd40115_Napa%20blue%20logo.svg" loading="lazy" alt="AriseHealth logo" class="clients-image-three"/><img src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c38e611bf0e61113fc3e79_Marquee%20Icon.svg" loading="lazy" alt="OE logo" class="clients-image-three"/><img src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c3982967a280eb9dd40115_Napa%20blue%20logo.svg" loading="lazy" alt="AriseHealth logo" class="clients-image-three"/><img src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c38e611bf0e61113fc3e79_Marquee%20Icon.svg" loading="lazy" alt="OE logo" class="clients-image-three"/></div></div></div></section><div class="w-layout-blockcontainer container-1298 w-container"><div class="footer-row"><div class="footer-logo-col"><div class="footer-logo-col-box-1"><a href="https://napa-canvas-website.webflow.io" class="link-block-14 w-inline-block"><img loading="lazy" src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c2845d01fe0eadab771757_logo%20whtie%20footer.svg" alt=""/></a><div class="footer-logo-col-box-2"><div class="text-white-14">Bespoke shade solutions designed for residential estates and commercial properties, combining craftsmanship, premium materials, and lasting performance.</div><div class="footer-logo-col-box-3"><div class="social-icon-row"><a href="https://www.facebook.com/profile.php?id=61588848756028" target="_blank" class="social-icon-box w-inline-block"><img loading="lazy" src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c2880793e696dea039c7c3_facebook%20icon.svg" alt="" class="social-icon"/></a><a href="https://www.instagram.com/napacanvas/" target="_blank" class="social-icon-box w-inline-block"><img loading="lazy" src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c2888d9c267c37f24d5d63_insagram%20icon.svg" alt="" class="social-icon"/></a><a href="https://www.linkedin.com/company/napa-canvas" target="_blank" class="social-icon-box w-inline-block"><img loading="lazy" src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c288078b35dd5c81d144dd_linkedin%20icon.svg" alt="" class="social-icon"/></a></div><div class="text-gray-14">Copyright © 2026 Napa Canvas. All right reserved.</div></div></div></div></div><div class="footer-menus-col"><div class="footer-menus-row"><div class="footer-menus-column"><h3 class="orange-heading-24-tenor">Portfolio</h3><div class="footer-menu-box"><a href="/portfolio" class="footer-menu-link w-inline-block"><div class="footer-menu-text">All Projects</div></a><a href="/portfolio?sun-shades" class="footer-menu-link w-inline-block"><div class="footer-menu-text">Sun Shades</div></a><a href="/portfolio?traditional-awnings-commercial" class="footer-menu-link w-inline-block"><div class="footer-menu-text">Traditional Awnings (Commercial)</div></a><a href="/portfolio?cat=sail-shades" class="footer-menu-link w-inline-block"><div class="footer-menu-text">Sail Shades</div></a><a href="/portfolio?traditional-awnings" class="footer-menu-link w-inline-block"><div class="footer-menu-text">Traditional Awnings</div></a><a href="/portfolio?retractable-awnings" class="footer-menu-link w-inline-block"><div class="footer-menu-text">Retractable Awnings</div></a></div></div><div class="footer-menus-column"><h3 class="orange-heading-24-tenor">Explore</h3><div class="footer-menu-box"><a href="/" class="footer-menu-link w-inline-block"><div class="footer-menu-text">Home</div></a><a href="/solutions" aria-current="page" class="footer-menu-link w-inline-block w--current"><div class="footer-menu-text">Solutions</div></a><a href="/portfolio" class="footer-menu-link w-inline-block"><div class="footer-menu-text">Portfolio</div></a><a href="/about" class="footer-menu-link w-inline-block"><div class="footer-menu-text">About</div></a><a href="/faq" class="footer-menu-link w-inline-block"><div class="footer-menu-text">FAQ</div></a><a href="/pricing" class="footer-menu-link w-inline-block"><div class="footer-menu-text">
283Pricing</div></a><a href="/contact" class="footer-menu-link w-inline-block"><div class="footer-menu-text">Contact</div></a></div></div><div class="footer-menus-column"><h3 class="orange-heading-24-tenor">Contact</h3><div class="footer-menu-icon-box"><div class="text-white-14">Request a Consultation</div><a href="tel:7072582903" class="footer-menu-icon-link w-inline-block"><img loading="lazy" src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c382dc3efc8e2a01e4ba86_phone%20icon.svg" alt="" class="image-41"/><div class="footer-menu-icon-text">707-258-2903</div></a><a href="mailto:[email protected]" target="_blank" class="footer-menu-icon-link w-inline-block"><img loading="lazy" src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c382dc0c92a90bdb9e7717_email%20icon.svg" alt="" class="image-42"/><div class="footer-menu-icon-text">[email protected]</div></a></div></div></div><div><div class="footer-horizantal-menu-box"><a href="/terms-of-service" class="footer-menu-link w-inline-block"><div class="footer-horizantal-menu-text">Terms of Service</div></a><div class="divider-vertical"><div class="divider-vertical-inner"></div></div><a href="/privacy-policy" class="footer-horizantal-menu-text w-inline-block"><div class="footer-horizantal-menu-text">Privacy Policy</div></a></div></div></div></div></div><div class="divider"></div><div class="w-layout-blockcontainer container w-container"><img data-w-id="6137622c-4b15-8b4e-f10c-710ed8b40619" loading="lazy" alt="" src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/69c26287d2bd5067873373a2_logo%20large.svg" class="footer-logo-large"/></div></section>
283<script src="https://d3e54v103j8qbb.cloudfront.net/js/jquery-3.5.1.min.dc5e7f18c8.js?site=69ab2b3b80f48170ba7e0b8f" type="text/javascript" integrity="sha256-9/aliU8dGd2tb6OSsuzixeV4y/faTqgFtohetphbbj0=" crossorigin="anonymous"></script>
283<script src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/js/webflow.schunk.36b8fb49256177c8.js" type="text/javascript" integrity="sha384-4abIlA5/v7XaW1HMXKBgnUuhnjBYJ/Z9C1OSg4OhmVw9O3QeHJ/qJqFBERCDPv7G" crossorigin="anonymous"></script>
283<script src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/js/webflow.schunk.2c12a0068e137466.js" type="text/javascript" integrity="sha384-De7Y0vcCatHoz1HskSkcKlTN/SUvf32Nf/NTi+cASpDCqjsQNOXViYXX4h7RDIm+" crossorigin="anonymous"></script>
283<script src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/js/webflow.schunk.ba232ca75e256be3.js" type="text/javascript" integrity="sha384-t+4fkOWj47jTfCjx5I9dTEE2IaxF+elImimFk66qJvV8OEZKPaMAPXqpeyKRbCvt" crossorigin="anonymous"></script>
283<script src="https://cdn.prod.website-files.com/69ab2b3b80f48170ba7e0b8f/js/webflow.e32610a6.b5d2e649526db97f.js" type="text/javascript" integrity="sha384-pUeFEwf8c37xXyTQ39Ct8Dy4iuxCMswCOCfoCGw5BEmbIWrWhywlMokadFLZtLnG" crossorigin="anonymous"></script>
283<script src="https://cdn.prod.website-files.com/gsap/3.15.0/gsap.min.js" type="text/javascript"></script>
283<!-- 2. Paste this code immediately after the opening <body> tag: --> 284<!-- Google Tag Manager (noscript) -->
vendor: 68 bytes, lines 284-285
284 285<noscript><iframe src="https://www.googletagmanager.com/ns.html?id=
285GTM-NTQ5SPMC
vendor: 83 bytes, lines 285-286
285" 286height="0" width="0" style="display:none;visibility:hidden"></iframe></noscript>
287<!-- End Google Tag Manager (noscript) --> 288
289<script> 290document.addEventListener("DOMContentLoaded", function () { 291 292 // CENTRAL STATE 293 const state = { 294 productType: { price: 0, label: "" }, 295 productMaterial: { price: 0, label: "" }, 296 productSize: { price: 0, label: "" } 297 }; 298 299 // TOTAL + UI UPDATE 300 function updateTotal() { 301 const total = 302 state.productType.price + 303 state.productMaterial.price + 304 state.productSize.price; 305 306 console.log("Total:", total); 307 308 // TOTAL UI 309 const totalEl = document.querySelector(".product-total-price"); 310 if (totalEl) { 311 totalEl.textContent = total; 312 } 313 314 // TYPE UI (only update if selected) 315 const typeEl = document.querySelector(".product-result-type"); 316 if (typeEl && state.productType.label) { 317 typeEl.textContent = state.productType.label; 318 } 319 320 // MATERIAL UI 321 const materialEl = document.querySelector(".product-result-material"); 322 if (materialEl && state.productMaterial.label) { 323 materialEl.textContent = state.productMaterial.label; 324 } 325 326 // SIZE UI 327 const sizeEl = document.querySelector(".product-result-size"); 328 if (sizeEl && state.productSize.label) { 329 sizeEl.textContent = state.productSize.label; 330 } 331 } 332 333 // GET CORRECT LABEL (Webflow-safe) 334 function getLabel(input) { 335 return ( 336 input.closest("label") || 337 document.querySelector(`label[for="${input.id}"]`) 338 ); 339 } 340 341 // GENERIC HANDLER 342 function handleRadioChange(selector, stateKey) { 343 344 const inputs = document.querySelectorAll(selector + " input[type='radio']"); 345 346 inputs.forEach(input => { 347 348 input.addEventListener("change", function () { 349 350 const label = getLabel(input); 351 if (!label) return; 352 353 // PRICE 354 const priceEl = label.querySelector(".canvas-price"); 355 const price = priceEl 356 ? parseFloat(priceEl.textContent.replace(/[^\d.]/g, "")) || 0 357 : 0; 358 359 // LABEL TEXT 360 const labelEl = label.querySelector(".radio-lablel-backend"); 361 const labelValue = labelEl 362 ? labelEl.textContent.trim() 363 : ""; 364 365 // UPDATE STATE 366 state[stateKey].price = price; 367 state[stateKey].label = labelValue; 368 369 console.log(stateKey + ":", state[stateKey]); 370 console.log("Full State:", state); 371 372 // UPDATE TOTAL + UI 373 updateTotal(); 374 375 }); 376 377 }); 378 } 379 380 // INITIALIZE ALL GROUPS 381 handleRadioChange( 382 ".calculator-form .form-radio-product-row label.product-type", 383 "productType" 384 ); 385 386 handleRadioChange( 387 ".calculator-form .form-radio-product-row label.product-material", 388 "productMaterial" 389 ); 390 391 handleRadioChange( 392 ".calculator-form .form-radio-product-row label.product-size", 393 "productSize" 394 ); 395 396 // INITIAL LOAD (only total, labels won't overwrite default HTML) 397 updateTotal(); 398 399}); 400</script>
vendor: 1 bytes, line 400
400
401<script> 402window.addEventListener("load", () => { 403 404 const tracks = document.querySelectorAll('.marquee-track'); 405 406 tracks.forEach(track => { 407 408 const content = track.innerHTML; 409 410 // duplicate until enough width 411 while (track.scrollWidth < window.innerWidth * 2) { 412 track.innerHTML += content; 413 } 414 415 const singleWidth = track.scrollWidth / 2; // ð¥ important fix 416 417 let position = 0; 418 419 function animate() { 420 position -= 0.5; 421 422 track.style.transform = `translateX(${position}px)`; 423 424 // ð¥ FIXED RESET (no gap / no mismatch) 425 if (Math.abs(position) >= singleWidth) { 426 position = 0; 427 } 428 429 requestAnimationFrame(animate); 430 } 431 432 animate(); 433 434 }); 435 436}); 437</script>
vendor: 1 bytes, line 437
437
438<script> 439document.querySelectorAll('.faq-question').forEach((question) => { 440 question.addEventListener('click', () => { 441 const item = question.closest('.faq-item'); 442 const answer = item.querySelector('.faq-answer'); 443 const icon = item.querySelector('.faq-icon'); 444 445 // close others 446 document.querySelectorAll('.faq-item').forEach((i) => { 447 if (i !== item) { 448 i.classList.remove('active'); 449 i.querySelector('.faq-answer').style.height = "0px"; 450 const otherIcon = i.querySelector('.faq-icon'); 451 if (otherIcon) otherIcon.style.transform = "rotate(0deg)"; 452 } 453 }); 454 455 // toggle current 456 if (item.classList.contains('active')) { 457 item.classList.remove('active'); 458 answer.style.height = "0px"; 459 icon.style.transform = "rotate(0deg)"; 460 } else { 461 item.classList.add('active'); 462 answer.style.height = answer.scrollHeight + "px"; 463 icon.style.transform = "rotate(180deg)"; 464 } 465 }); 466}); 467 468 469// â OPEN FIRST FAQ ON LOAD 470window.addEventListener('load', () => { 471 const firstItem = document.querySelector('.faq-item'); 472 if (firstItem) { 473 const answer = firstItem.querySelector('.faq-answer'); 474 const icon = firstItem.querySelector('.faq-icon'); 475 476 firstItem.classList.add('active'); 477 answer.style.height = answer.scrollHeight + "px"; 478 if (icon) icon.style.transform = "rotate(180deg)"; 479 } 480}); 481 482 483 484 485// horizantal scrolling section fixation start 486document.addEventListener("DOMContentLoaded", () => { 487 const track = document.querySelector(".horizantal-scroll-track"); 488 const section = track.closest("section"); 489 490 let maxTranslate = 0; 491 let sectionTop = 0; 492 let scrollDistance = 0; 493 let isActive = false; 494 495 const BREAKPOINT = 990; 496 497 function calculate() { 498 const viewportWidth = window.innerWidth; 499 const viewportHeight = window.innerHeight; 500 501 // base horizontal distance 502 maxTranslate = track.scrollWidth - viewportWidth; 503 504 // gap 505 const styles = window.getComputedStyle(track); 506 const gap = parseFloat(styles.columnGap) || 0; 507 508 // container offset 509 const trackRect = track.getBoundingClientRect(); 510 const offsetLeft = trackRect.left; 511 512 maxTranslate = maxTranslate + gap + offsetLeft; 513 514 // section position 515 const rect = section.getBoundingClientRect(); 516 sectionTop = window.scrollY + rect.top; 517 518 // same speed logic 519 scrollDistance = maxTranslate * 1.2; 520 521 section.style.height = `${scrollDistance + viewportHeight}px`; 522 } 523 524 function update() { 525 const scrollY = window.scrollY; 526 527 let progress = (scrollY - sectionTop) / scrollDistance; 528 progress = Math.max(0, Math.min(1, progress)); 529 530 const translateX = -maxTranslate * progress; 531 track.style.transform = `translate3d(${translateX}px, 0, 0)`; 532 } 533 534 function enable() { 535 if (isActive) return; 536 isActive = true; 537 538 calculate(); 539 update(); 540 541 window.addEventListener("scroll", onScroll); 542 } 543 544 function disable() { 545 if (!isActive) return; 546 isActive = false; 547 548 window.removeEventListener("scroll", onScroll); 549 550 // â reset styles (VERY IMPORTANT) 551 track.style.transform = "none"; 552 section.style.height = "auto"; 553 } 554 555 let ticking = false; 556 557 function onScroll() { 558 if (!ticking) { 559 requestAnimationFrame(() => { 560 update(); 561 ticking = false; 562 }); 563 ticking = true; 564 } 565 } 566 567 function handleResize() { 568 if (window.innerWidth >= BREAKPOINT) { 569 enable(); 570 calculate(); 571 update(); 572 } else { 573 disable(); 574 } 575 } 576 577 // init 578 handleResize(); 579 580 window.addEventListener("resize", handleResize); 581 582 // â FIX: Force re-calculation after fonts/images load to ensure sticky works first time 583 window.addEventListener("load", function() { 584 if (window.innerWidth >= BREAKPOINT) { 585 calculate(); 586 update(); 587 } 588 }); 589}); 590// horizantal scrolling section fixation end 591 592 593 594// Portfolio Tab Filter --------------------------------------------------------------------------------------------------------- 595// Portfolio Tab Filter active on page load and on click 596let categoryMatched = false;
597let activeCategory = null; 598let currentPageNumber = 1; 599let pageRestoreAttempts = 0; 600let categoryRestored = false; 601 602window.addEventListener("load", function () { 603 initCategoryFilter(); 604 initRadioUI(); 605 restoreFromURL(); 606 607 // Auto double-click on All Project if it has is-list-active class 608 setTimeout(function() { 609 autoTriggerAllProjectIfActive(); 610 }, 500); 611}); 612 613function autoTriggerAllProjectIfActive() { 614 const clearAllButton = document.querySelector(".clear-all-filter.w-radio.is-list-active"); 615 616 if (clearAllButton) { 617 const label = clearAllButton.querySelector(".w-form-label"); 618 if (label && label.textContent.trim() === "All Project") { 619 console.log("Auto-triggering All Project filter"); 620 621 // First click 622 clearAllButton.click(); 623 624 // Double click with small delay 625 setTimeout(() => { 626 clearAllButton.click(); 627 console.log("Double click triggered on All Project"); 628 629 // Force Finsweet filter refresh after double click 630 setTimeout(() => { 631 triggerFinsweetFilterRefresh(); 632 }, 100); 633 }, 150); 634 } 635 } 636} 637 638function initCategoryFilter() { 639 // Check URL query parameters first 640 const urlParams = new URLSearchParams(window.location.search); 641 let categoryFromURL = null; 642 643 // Check for 'cat' parameter first 644 if (urlParams.has('cat')) { 645 categoryFromURL = urlParams.get('cat'); 646 } else { 647 // Check for old format (direct parameter like ?sun-shades) 648 for (let [key, value] of urlParams.entries()) { 649 if (key !== 'page' && !value) { 650 categoryFromURL = key; 651 break; 652 } 653 } 654 } 655 656 if (categoryFromURL) { 657 // URL parameter takes precedence 658 const categoryText = paramToCategoryText(categoryFromURL); 659 if (categoryText) { 660 categoryRestored = true; 661 setTimeout(() => { 662 activateCategoryByText(categoryText); 663 }, 200); 664 return; 665 } 666 } 667 668 // No category in URL, activate All Project after a short delay 669 setTimeout(() => { 670 const clearAll = document.querySelector(".clear-all-filter.w-radio"); 671 if (clearAll && !clearAll.classList.contains('is-list-active')) { 672 clearAll.click(); 673 saveActiveCategory('All Project'); 674 } 675 }, 300); 676} 677 678function initRadioUI() { 679 const radios = document.querySelectorAll(".w-radio"); 680 const clearAll = document.querySelector(".clear-all-filter.w-radio"); 681 682 // UI active state sync 683 radios.forEach((radio) => { 684 const input = radio.querySelector("input[type='radio']"); 685 if (!input) return; 686 687 input.addEventListener("change", () => { 688 radios.forEach(r => r.classList.remove("is-list-active")); 689 690 if (input.checked) { 691 radio.classList.add("is-list-active"); 692 const categoryText = getCategoryText(radio); 693 saveActiveCategory(categoryText); 694 activeCategory = categoryText; 695 696 // Reset to page 1 when changing category 697 currentPageNumber = 1; 698 699 // Update URL based on selected category (except for All Project) 700 updateFullURL(categoryText, 1); 701 } 702 }); 703 }); 704 705 // Clear-all click fix (single-click UI sync) 706 if (clearAll) { 707 clearAll.addEventListener("click", () => { 708 const input = clearAll.querySelector("input[type='radio']"); 709 710 radios.forEach(r => r.classList.remove("is-list-active")); 711 712 clearAll.classList.add("is-list-active"); 713 714 // Save Clear All as active category 715 saveActiveCategory('All Project'); 716 activeCategory = 'All Project'; 717 718 // Reset to page 1 719 currentPageNumber = 1; 720 721 // Update URL to base portfolio page (remove all query params) 722 updateFullURL(null, 1); 723 724 if (input && !input.checked) { 725 input.checked = true; 726 input.dispatchEvent(new Event("change", { bubbles: true })); 727 } 728 729 // Force trigger Finsweet filter refresh for Clear All 730 setTimeout(() => { 731 triggerFinsweetFilterRefresh(); 732 }, 100); 733 }); 734 } 735 736 // Setup pagination click handlers after a delay (wait for Finsweet) 737 setTimeout(() => { 738 setupPaginationHandlers(); 739 }, 1000); 740} 741 742// Setup pagination click handlers 743function setupPaginationHandlers() { 744 // Function to handle pagination clicks 745 function handlePaginationClick(e) { 746 // Get the current active page after click 747 setTimeout(() => { 748 const activePage = document.querySelector('.page-number.w--current'); 749 if (activePage) { 750 currentPageNumber = parseInt(activePage.textContent); 751 const currentCategory = getCurrentCategory(); 752 updateFullURL(currentCategory, currentPageNumber); 753 console.log('Page updated to:', currentPageNumber); 754 } 755 }, 200); 756 } 757 758 // Watch for clicks on page buttons 759 const pageButtons = document.querySelectorAll('[fs-list-element="page-button"]'); 760 pageButtons.forEach(button => { 761 button.removeEventListener('click', handlePaginationClick); 762 button.addEventListener('click', handlePaginationClick); 763 }); 764
765 // Watch for clicks on next/prev buttons 766 const nextButton = document.querySelector('.pagination-next'); 767 const prevButton = document.querySelector('.pagination-previous'); 768 769 if (nextButton) { 770 nextButton.removeEventListener('click', handlePaginationClick); 771 nextButton.addEventListener('click', handlePaginationClick); 772 } 773 774 if (prevButton) { 775 prevButton.removeEventListener('click', handlePaginationClick); 776 prevButton.addEventListener('click', handlePaginationClick); 777 } 778} 779 780// Get current active category 781function getCurrentCategory() { 782 const activeRadio = document.querySelector('.w-radio.is-list-active'); 783 if (activeRadio) { 784 const label = activeRadio.querySelector('.w-form-label'); 785 if (label && label.textContent.trim() !== 'All Project') { 786 return label.textContent.trim(); 787 } 788 } 789 return null; // All Project 790} 791 792// Update full URL with both category and page - IMPROVED VERSION 793function updateFullURL(categoryText, pageNumber) { 794 const baseUrl = window.location.origin + window.location.pathname; 795 const params = new URLSearchParams(); 796 797 // Add category parameter if not All Project 798 if (categoryText && categoryText !== 'All Project') { 799 const param = categoryToParam(categoryText); 800 params.set('cat', param); 801 } 802 803 // Add page parameter if greater than 1 804 if (pageNumber && pageNumber > 1) { 805 params.set('page', pageNumber); 806 } 807 808 // Build new URL 809 let newUrl = baseUrl; 810 const paramString = params.toString(); 811 if (paramString) { 812 newUrl = baseUrl + '?' + paramString; 813 } 814 815 // Only update if URL has changed 816 if (window.location.href !== newUrl) { 817 // Check if we're just normalizing the URL format 818 const currentParams = new URLSearchParams(window.location.search); 819 const hasOldFormat = Array.from(currentParams.keys()).some(key => key !== 'page' && key !== 'cat' && currentParams.get(key) === ''); 820 821 if (hasOldFormat && params.has('cat')) { 822 // If we're converting from old format to new format, use replaceState 823 window.history.replaceState({ category: categoryText, page: pageNumber }, "", newUrl); 824 } else { 825 // Normal navigation - use pushState 826 window.history.pushState({ category: categoryText, page: pageNumber }, "", newUrl); 827 } 828 console.log('URL updated to:', newUrl); 829 } 830} 831 832// Restore from URL on page load - IMPROVED VERSION 833function restoreFromURL() { 834 const urlParams = new URLSearchParams(window.location.search); 835 836 // Check and normalize URL format 837 let needsNormalization = false; 838 let oldFormatCategory = null; 839 840 // Check for old format (direct parameter without 'cat') 841 for (let [key, value] of urlParams.entries()) { 842 if (key !== 'page' && key !== 'cat' && !value) { 843 oldFormatCategory = key; 844 needsNormalization = true; 845 break; 846 } 847 } 848 849 // If old format detected, update to new format without page reload 850 if (needsNormalization && oldFormatCategory) { 851 const normalizedCategory = oldFormatCategory.replace(/-/g, ' '); 852 const param = categoryToParam(normalizedCategory); 853 const newUrl = window.location.origin + window.location.pathname + '?cat=' + param; 854 // Use replaceState to fix the URL without reload 855 window.history.replaceState({ category: normalizedCategory, page: 1 }, "", newUrl); 856 console.log('Normalized URL from old format to:', newUrl); 857 } 858 859 // Get category from URL 860 let categoryFromURL = null; 861 if (urlParams.has('cat')) { 862 categoryFromURL = urlParams.get('cat'); 863 } else { 864 // Check for old format 865 for (let [key, value] of urlParams.entries()) { 866 if (key !== 'page' && !value) { 867 categoryFromURL = key; 868 break; 869 } 870 } 871 } 872 873 // Get page from URL 874 const pageFromURL = urlParams.get('page'); 875 let targetPage = 1; 876 if (pageFromURL && !isNaN(parseInt(pageFromURL))) { 877 targetPage = parseInt(pageFromURL); 878 currentPageNumber = targetPage; 879 console.log('Target page from URL:', targetPage); 880 } 881 882 // Restore category first, then page 883 if (categoryFromURL) { 884 const categoryText = paramToCategoryText(categoryFromURL); 885 if (categoryText) { 886 setTimeout(() => { 887 activateCategoryByText(categoryText); 888 // Wait for category filter to apply, then restore page 889 waitForPaginationAndRestorePage(targetPage); 890 }, 300); 891 return; 892 } 893 } 894 895 // No category, restore page for All Project 896 if (targetPage > 1) { 897 waitForPaginationAndRestorePage(targetPage); 898 } 899} 900 901// Wait for pagination to be ready before restoring page 902function waitForPaginationAndRestorePage(targetPage, attempt = 0) { 903 if (attempt > 20) { 904 console.log('Max attempts reached, could not restore page'); 905 return; 906 } 907 908 const paginationWrapper = document.querySelector('[fs-list-element="pagination-wrapper"]'); 909 const pageButtons = document.querySelectorAll('[fs-list-element="page-button"]'); 910 911 if (paginationWrapper && pageButtons.length > 0) { 912 console.log('Pagination found, restoring page:', targetPage); 913 setTimeout(() => { 914 restorePageNumber(targetPage); 915 }, 500); 916 } else { 917 console.log('Waiting for pagination... attempt:', attempt + 1); 918 setTimeout(() => { 919 waitForPaginationAndRestorePage(targetPage, attempt + 1); 920 }, 300); 921 } 922} 923 924// Restore page number 925function restorePageNumber(targetPage) { 926 if (!targetPage || targetPage === 1) { 927 console.log('No need to restore, page 1 is default'); 928 return; 929 } 930 931 console.log('Attempting to restore to page:', targetPage); 932 933 // Try multiple times with increasing delays 934 let restoreAttempt = 0; 935 936 function attemptRestore() { 937 restoreAttempt++; 938 939 // Find the target page button 940 const pageButtons = document.querySelectorAll('[fs-list-element="page-button"]'); 941 let targetButton = null; 942 943 for (let button of pageButtons) { 944 const pageNum = parseInt(button.textContent); 945 if (pageNum === targetPage) { 946 targetButton = button; 947 break; 948 } 949 } 950 951 if (targetButton && !targetButton.classList.contains('w--current')) { 952 targetButton.click(); 953 console.log('Restored to page:', targetPage); 954 return true; 955 } else if (targetButton && targetButton.classList.contains('w--current')) { 956 console.log('Already on correct page:', targetPage); 957 return true; 958 } else if (restoreAttempt < 5) { 959 // Try again with longer delay 960 console.log('Retry restore attempt:', restoreAttempt); 961 setTimeout(attemptRestore, 500); 962 return false;
963 } else { 964 console.log('Could not restore page after multiple attempts'); 965 return false; 966 } 967 } 968 969 // Start restore attempts with initial delay 970 setTimeout(attemptRestore, 300); 971} 972 973// Helper function to get category text from radio element 974function getCategoryText(radio) { 975 const label = radio.querySelector(".w-form-label"); 976 if (label) { 977 return label.textContent.trim(); 978 } 979 return null; 980} 981 982// Save active category to sessionStorage (backup) 983function saveActiveCategory(categoryText) { 984 if (categoryText) { 985 sessionStorage.setItem('activePortfolioCategory', categoryText); 986 sessionStorage.setItem('activePortfolioCategoryTimestamp', Date.now()); 987 } 988} 989 990// Convert category text to URL parameter 991function categoryToParam(categoryText) { 992 return categoryText 993 .toLowerCase() 994 .replace(/[()]/g, "") // remove brackets 995 .replace(/&/g, "and") // replace & with 'and' 996 .replace(/\s+/g, "-") // replace spaces with hyphens 997 .replace(/[^a-z0-9-]/g, ""); // remove special characters 998} 999 1000// Convert URL parameter back to category text 1001function paramToCategoryText(param) { 1002 const radios = document.querySelectorAll(".w-radio"); 1003 1004 // Decode the parameter (replace hyphens with spaces) 1005 const decodedParam = param.replace(/-/g, " "); 1006 1007 // First try exact match (case insensitive) 1008 for (let radio of radios) { 1009 const label = radio.querySelector(".w-form-label"); 1010 if (label) { 1011 const labelText = label.textContent.trim(); 1012 if (labelText.toLowerCase() === decodedParam.toLowerCase()) { 1013 return labelText; 1014 } 1015 } 1016 } 1017 1018 // Then try slug match 1019 for (let radio of radios) { 1020 const label = radio.querySelector(".w-form-label"); 1021 if (label) { 1022 const labelText = label.textContent.trim(); 1023 const labelParam = categoryToParam(labelText); 1024 if (labelParam === param) { 1025 return labelText; 1026 } 1027 } 1028 } 1029 1030 return null; 1031} 1032 1033// Handle browser back/forward buttons - FIXED VERSION 1034window.addEventListener("popstate", function(event) { 1035 // Don't reload the page - let the browser handle navigation naturally 1036 1037 if (event.state) { 1038 // If we have state data, use it to restore category and page 1039 const category = event.state.category; 1040 const page = event.state.page || 1; 1041 1042 if (category && category !== 'All Project') { 1043 setTimeout(() => { 1044 activateCategoryByText(category); 1045 if (page > 1) { 1046 waitForPaginationAndRestorePage(page); 1047 } else { 1048 currentPageNumber = 1; 1049 } 1050 }, 100); 1051 } else { 1052 // Activate All Project 1053 const clearAll = document.querySelector(".clear-all-filter.w-radio"); 1054 if (clearAll && !clearAll.classList.contains('is-list-active')) { 1055 clearAll.click(); 1056 if (page > 1) { 1057 waitForPaginationAndRestorePage(page); 1058 } 1059 } 1060 } 1061 } else { 1062 // No state available, check URL parameters 1063 const urlParams = new URLSearchParams(window.location.search); 1064 let categoryFromURL = null; 1065 1066 if (urlParams.has('cat')) { 1067 categoryFromURL = urlParams.get('cat'); 1068 } else { 1069 for (let [key, value] of urlParams.entries()) { 1070 if (key !== 'page' && !value) { 1071 categoryFromURL = key; 1072 break; 1073 } 1074 } 1075 } 1076 1077 if (categoryFromURL) { 1078 const categoryText = paramToCategoryText(categoryFromURL); 1079 if (categoryText) { 1080 setTimeout(() => { 1081 activateCategoryByText(categoryText); 1082 }, 100); 1083 } 1084 } else { 1085 // No category in URL, activate All Project 1086 setTimeout(() => { 1087 const clearAll = document.querySelector(".clear-all-filter.w-radio"); 1088 if (clearAll && !clearAll.classList.contains('is-list-active')) { 1089 clearAll.click(); 1090 } 1091 }, 100); 1092 } 1093 } 1094}); 1095 1096// Force trigger Finsweet filter refresh 1097function triggerFinsweetFilterRefresh() { 1098 const filterInputs = document.querySelectorAll('[fs-cmsfilter-element="filter"] input, [fs-cmsfilter-element="filter"] select'); 1099 1100 if (filterInputs.length > 0) { 1101 filterInputs.forEach(input => { 1102 input.dispatchEvent(new Event('change', { bubbles: true })); 1103 input.dispatchEvent(new Event('input', { bubbles: true })); 1104 }); 1105 } 1106 1107 const filterForm = document.querySelector('[fs-cmsfilter-element="filters"]'); 1108 if (filterForm) { 1109 filterForm.dispatchEvent(new Event('change', { bubbles: true })); 1110 } 1111} 1112 1113// Activate category by its text label 1114function activateCategoryByText(categoryText) { 1115 const radios = document.querySelectorAll(".w-radio"); 1116 let found = false;
1117 1118 for (let radio of radios) { 1119 const label = radio.querySelector(".w-form-label"); 1120 if (label && label.textContent.trim() === categoryText) { 1121 const input = radio.querySelector("input[type='radio']"); 1122 if (input && !input.checked) { 1123 found = true; 1124 setTimeout(() => { 1125 radio.click(); 1126 }, 100); 1127 break; 1128 } else if (input && input.checked) { 1129 found = true; 1130 radios.forEach(r => r.classList.remove("is-list-active")); 1131 radio.classList.add("is-list-active"); 1132 break; 1133 } 1134 } 1135 } 1136 1137 return found; 1138} 1139// Category tabs and pagination state back from detail page should be active end --------------------------------------------------------------- 1140</script>
1140 1141 1142
1143<script> 1144const fixImages = () => { 1145 document.querySelectorAll( 1146 ".detail-images-sec img, .portfolio-listing-sec img" 1147 ).forEach(img => { 1148 if (img.srcset) { 1149 img.removeAttribute("srcset"); 1150 img.removeAttribute("sizes"); 1151 } 1152 }); 1153}; 1154 1155window.addEventListener("load", fixImages); 1156 1157// CMS / dynamic content ke liye 1158const observer = new MutationObserver(fixImages); 1159observer.observe(document.body, { childList: true, subtree: true }); 1160 1161 1162</script>
1162 1163
1164<script> 1165// PDF file download on pricing page - Multiple PDFs support start 1166// document.addEventListener("DOMContentLoaded", function() { 1167// const links = document.querySelectorAll('.see-pricing'); // Sabhi elements select honge 1168 1169// links.forEach(link => { 1170// link.addEventListener('click', function(e) { 1171// e.preventDefault(); // Default behavior rokein 1172// const pdfUrl = this.getAttribute('href'); 1173 1174// // Extract filename from URL 1175// const fileName = pdfUrl.split('/').pop(); 1176 1177// // 1. PDF download karein 1178// fetch(pdfUrl) 1179// .then(response => response.blob()) 1180// .then(blob => { 1181// const blobUrl = window.URL.createObjectURL(blob); 1182// const downloadLink = document.createElement('a'); 1183// downloadLink.href = blobUrl; 1184// downloadLink.download = fileName; // Dynamic filename 1185// document.body.appendChild(downloadLink); 1186// downloadLink.click(); 1187// document.body.removeChild(downloadLink); 1188// window.URL.revokeObjectURL(blobUrl); 1189// }) 1190// .catch(error => console.error('Download failed:', error)); 1191 1192// // 2. New tab mein bhi open karein 1193// // window.open(pdfUrl, '_blank'); 1194// window.open(pdfUrl, '_blank'); 1195// }); 1196// }); 1197// }); 1198 1199// document.addEventListener("DOMContentLoaded", function() { 1200// const links = document.querySelectorAll('.see-pricing'); 1201 1202// links.forEach(link => { 1203// link.addEventListener('click', function(e) { 1204// e.preventDefault(); 1205// const pdfUrl = this.getAttribute('href'); 1206 1207// // Download (optional) 1208// fetch(pdfUrl) 1209// .then(response => response.blob()) 1210// .then(blob => { 1211// const blobUrl = window.URL.createObjectURL(blob); 1212// const downloadLink = document.createElement('a'); 1213// downloadLink.href = blobUrl; 1214// downloadLink.download = pdfUrl.split('/').pop(); 1215// document.body.appendChild(downloadLink); 1216// downloadLink.click(); 1217// document.body.removeChild(downloadLink); 1218// window.URL.revokeObjectURL(blobUrl); 1219// }) 1220// .catch(error => console.error('Download failed:', error)); 1221 1222// // Open in SAME TAB 1223// window.location.href = pdfUrl; 1224// }); 1225// }); 1226// }); 1227 1228document.addEventListener("DOMContentLoaded", function () { 1229 const links = document.querySelectorAll('.see-pricing'); 1230 const modal = document.getElementById('pdf-modal'); 1231 const iframe = document.getElementById('pdf-frame'); 1232 const closeBtn = document.querySelector('.pdf-close'); 1233 const overlay = document.querySelector('.pdf-modal-overlay'); 1234 1235 function isMobile() { 1236 return /iPhone|iPad|iPod|Android/i.test(navigator.userAgent); 1237 } 1238 1239 links.forEach(link => { 1240 link.addEventListener('click', function (e) { 1241 e.preventDefault(); 1242 1243 const pdfUrl = this.getAttribute('href'); 1244 1245 // For mobile/tablet - open directly in new tab/window 1246 if (isMobile()) { 1247 window.open(pdfUrl, '_blank'); 1248 } else { 1249 // Desktop - show in modal 1250 iframe.src = pdfUrl; 1251 modal.classList.add('active'); 1252 } 1253 }); 1254 }); 1255 1256 function closeModal() { 1257 modal.classList.remove('active'); 1258 iframe.src = ""; // stop loading 1259 } 1260 1261 if (closeBtn) closeBtn.addEventListener('click', closeModal); 1262 if (overlay) overlay.addEventListener('click', closeModal); 1263}); 1264// PDF file download on pricing page - Multiple PDFs support end 1265</script>
1265 1266 1267<!-- clicking on pagination redirect move to tabs section start-->
1268<script> 1269document.addEventListener("DOMContentLoaded", function () { 1270 const paginationWrapper = document.querySelector('.w-pagination-wrapper.pagination-row'); 1271 const targetSection = document.querySelector('#tab-filters'); 1272 1273 if (paginationWrapper && targetSection) { 1274 paginationWrapper.addEventListener('click', function () { 1275 targetSection.scrollIntoView({ 1276 behavior: 'smooth', 1277 block: 'start' 1278 }); 1279 }); 1280 } 1281}); 1282</script>
1282 1283<!-- clicking on pagination redirect move to tabs section end--> 1284 1285<!-- make portiflio active header menu once category page open start -->
1286<script> 1287document.addEventListener("DOMContentLoaded", function () { 1288 if (window.location.pathname.startsWith('/category')) { 1289 1290 setTimeout(() => { 1291 const portfolioLink = document.querySelector( 1292 '.header-menu-inline a.nav-link[href="/portfolio"]' 1293 ); 1294 1295 if (portfolioLink) { 1296 portfolioLink.classList.add('w--current'); 1297 } 1298 }, 300); // adjust 200â500ms if needed 1299 } 1300}); 1301</script>
1301 1302<!-- make portiflio active header menu once category page open end --> 1303 1304
1305<script> 1306(function() { 1307 function hideMatches() { 1308 console.log("Hiding matches..."); // Optional: for debugging 1309 document.querySelectorAll(".collection-item").forEach(function(item) { 1310 var project = item.querySelector(".listing-project-name"); 1311 var category = item.querySelector(".related-projects"); 1312 if (project && category) { 1313 if (project.innerText.trim().toLowerCase() === category.innerText.trim().toLowerCase()) { 1314 category.style.display = "none"; 1315 } else { 1316 // Ensure items that don't match remain visible (important for when DOM changes) 1317 category.style.display = ""; 1318 } 1319 } 1320 }); 1321 } 1322 1323 // Run immediately 1324 hideMatches(); 1325 1326 // Wait for full page load 1327 window.addEventListener("load", hideMatches); 1328 1329 // Handle clicks (existing functionality) 1330 document.addEventListener("click", function() { 1331 setTimeout(hideMatches, 50); 1332 }); 1333 1334 // CRITICAL: Handle back/forward navigation (pageshow event) 1335 window.addEventListener("pageshow", function(event) { 1336 // pageshot fires on back/forward navigation 1337 // persisted is true when page is loaded from cache 1338 if (event.persisted) { 1339 hideMatches(); 1340 } 1341 setTimeout(hideMatches, 100); // Extra delay for safety 1342 }); 1343 1344 // For Webflow's native navigation 1345 if (typeof Webflow !== "undefined") { 1346 Webflow.push(hideMatches); 1347 1348 // Also listen for Webflow page transitions 1349 $(document).on('wf-page-ready', function() { 1350 setTimeout(hideMatches, 100); 1351 }); 1352 } 1353 1354 // Use MutationObserver as a backup (detects DOM changes) 1355 if (typeof MutationObserver !== "undefined") { 1356 var observer = new MutationObserver(function(mutations) { 1357 // Only run if we see significant DOM changes 1358 var shouldRun = mutations.some(function(mutation) { 1359 return mutation.target.matches && 1360 (mutation.target.matches('.collection-item, .collection-list') || 1361 mutation.target.querySelector && mutation.target.querySelector('.collection-item')); 1362 }); 1363 1364 if (shouldRun) { 1365 setTimeout(hideMatches, 50); 1366 } 1367 }); 1368 1369 // Start observing when DOM is ready 1370 document.addEventListener("DOMContentLoaded", function() { 1371 observer.observe(document.body, { 1372 childList: true, 1373 subtree: true 1374 }); 1375 }); 1376 } 1377})(); 1378</script>
1378</body></html>
Line numbers count LF bytes from the start of the resource, as the search results do. Vendor segments are library code the classifier recognised; they are stored but not indexed. Bytes are shown as Latin1 characters, one per byte.