1<!doctype html> 2<html 3 4 lang="en" 5> 6 <head><link rel="preconnect" href="https://media.bostonproper.com"> 7 <link rel="dns-prefetch" href="https://media.bostonproper.com"><link 8 rel="icon" 9 type="image/png" 10 href="//www.bostonproper.com/cdn/shop/files/bp-favicon_2026.jpg?crop=center&height=32&v=1785357425&width=32" 11 > 12 13 14 15<meta charset="utf-8"> 16<meta 17 http-equiv="X-UA-Compatible" 18 content="IE=edge" 19> 20<meta 21 name="viewport" 22 content="width=device-width,initial-scale=1" 23> 24<meta 25 name="view-transition" 26 content="same-origin" 27> 28<meta 29 name="theme-color" 30 content="" 31> 32 33<meta 34 property="og:site_name" 35 content="Boston Proper" 36> 37<meta 38 property="og:url" 39 content="https://www.bostonproper.com/products/boston-proper-printed-scarf-with-love-bp" 40> 41<meta 42 property="og:title" 43 content="Boston Proper Printed Scarf With Love BP" 44> 45<meta 46 property="og:type" 47 content="product" 48> 49<meta 50 property="og:description" 51 content="Shop women's dresses, statement tops, polished pants, shoes, accessories, and travel-ready styles designed to fit beautifully and own every room. " 52><meta 53 property="og:image" 54 content="http://www.bostonproper.com/cdn/shop/files/2CT8071_SET_e6f3e3ce-5a5f-451b-bc3b-ea1f8f71d236.jpg?v=1791009198" 55 > 56 <meta 57 property="og:image:secure_url" 58 content="https://www.bostonproper.com/cdn/shop/files/2CT8071_SET_e6f3e3ce-5a5f-451b-bc3b-ea1f8f71d236.jpg?v=1791009198" 59 > 60 <meta 61 property="og:image:width" 62 content="1639" 63 > 64 <meta 65 property="og:image:height" 66 content="2048" 67 ><meta 68 property="og:price:amount" 69 content="50.00" 70 > 71 <meta 72 property="og:price:currency" 73 content="USD" 74 ><meta 75 name="twitter:card" 76 content="summary_large_image" 77> 78<meta 79 name="twitter:title" 80 content="Boston Proper Printed Scarf With Love BP" 81> 82<meta 83 name="twitter:description" 84 content="Shop women's dresses, statement tops, polished pants, shoes, accessories, and travel-ready styles designed to fit beautifully and own every room. " 85> 86 87<title> 88 Boston Proper Printed Scarf With Love BP 89</title> 90 91<link 92 rel="canonical" 93 href="https://www.bostonproper.com/products/boston-proper-printed-scarf-with-love-bp" 94> 95 96 97<link href="//www.bostonproper.com/cdn/shop/t/1521/assets/overflow-list.css?v=16727044177065489451790713714" as="style" rel="preload"> 98<link href="//www.bostonproper.com/cdn/shop/t/1521/assets/base.css?v=80415010869825589501790713714" rel="stylesheet" type="text/css" media="all" /> 99 100<link 101 rel="preload" 102 as="font" 103 href="//www.bostonproper.com/cdn/fonts/inter/inter_n4.b2a3f24c19b4de56e8871f609e73ca7f6d2e2bb9.woff2" 104 type="font/woff2" 105 crossorigin 106 fetchpriority="low" 107 ><link 108 rel="preload" 109 as="font" 110 href="//www.bostonproper.com/cdn/fonts/inter/inter_n5.d7101d5e168594dd06f56f290dd759fba5431d97.woff2" 111 type="font/woff2" 112 crossorigin 113 fetchpriority="low" 114 ><link 115 rel="preload" 116 as="font" 117 href="//www.bostonproper.com/cdn/fonts/inter/inter_n7.02711e6b374660cfc7915d1afc1c204e633421e4.woff2" 118 type="font/woff2" 119 crossorigin 120 fetchpriority="low" 121 ><link 122 rel="preload" 123 as="font" 124 href="//www.bostonproper.com/cdn/fonts/inter/inter_n7.02711e6b374660cfc7915d1afc1c204e633421e4.woff2" 125 type="font/woff2" 126 crossorigin 127 fetchpriority="low" 128 > 129 130
131<script async crossorigin fetchpriority="high" src="/cdn/shopifycloud/importmap-polyfill/es-modules-shim.2.4.0.js"></script>
vendor: 1 bytes, line 131
131
132<script type="importmap"> 133 { 134 "imports": { 135 "@theme/overflow-list": "//www.bostonproper.com/cdn/shop/t/1521/assets/overflow-list.js?v=46858356770684902511790713714", 136 "@theme/product-title": "//www.bostonproper.com/cdn/shop/t/1521/assets/product-title-truncation.js?v=81744426891537421781790713714", 137 "@theme/component": "//www.bostonproper.com/cdn/shop/t/1521/assets/component.js?v=184240237101443119871790713714", 138 "@theme/dialog": "//www.bostonproper.com/cdn/shop/t/1521/assets/dialog.js?v=150888536568694194021790713714", 139 "@theme/events": "//www.bostonproper.com/cdn/shop/t/1521/assets/events.js?v=1118589085892804011790713714", 140 "@theme/focus": "//www.bostonproper.com/cdn/shop/t/1521/assets/focus.js?v=60436577539430446401790713714", 141 "@theme/morph": "//www.bostonproper.com/cdn/shop/t/1521/assets/morph.js?v=104045809772310330131790713714", 142 "@theme/paginated-list": "//www.bostonproper.com/cdn/shop/t/1521/assets/paginated-list.js?v=95298315036233333871790713714", 143 "@theme/performance": "//www.bostonproper.com/cdn/shop/t/1521/assets/performance.js?v=171015751390534793081790713714", 144 "@theme/product-form": "//www.bostonproper.com/cdn/shop/t/1521/assets/product-form.js?v=138228271698465017521790713714", 145 "@theme/recently-viewed-products": "//www.bostonproper.com/cdn/shop/t/1521/assets/recently-viewed-products.js?v=94729125001750262651790713714", 146 "@theme/scrolling": "//www.bostonproper.com/cdn/shop/t/1521/assets/scrolling.js?v=162288454571026455011790713714", 147 "@theme/section-renderer": "//www.bostonproper.com/cdn/shop/t/1521/assets/section-renderer.js?v=170547751348887101551790713714", 148 "@theme/section-hydration": "//www.bostonproper.com/cdn/shop/t/1521/assets/section-hydration.js?v=108434990705342316311790713714", 149 "@theme/money-formatting": "//www.bostonproper.com/cdn/shop/t/1521/assets/money-formatting.js?v=182191346160333327151790713714", 150 "@theme/utilities": "//www.bostonproper.com/cdn/shop/t/1521/assets/utilities.js?v=28506172887375975941790713714", 151 "@theme/variant-picker": "//www.bostonproper.com/cdn/shop/t/1521/assets/variant-picker.js?v=137587803232570873901790713714", 152 "@theme/media-gallery": "//www.bostonproper.com/cdn/shop/t/1521/assets/media-gallery.js?v=14624021005636302161790713714", 153 "@theme/quick-add": "//www.bostonproper.com/cdn/shop/t/1521/assets/quick-add.js?v=141968020560480616261790713714", 154 "@theme/paginated-list-aspect-ratio": "//www.bostonproper.com/cdn/shop/t/1521/assets/paginated-list-aspect-ratio.js?v=25254270272143792451790713714", 155 "@theme/popover-polyfill": "//www.bostonproper.com/cdn/shop/t/1521/assets/popover-polyfill.js?v=119029313658895111201790713714", 156 "@theme/component-quantity-selector": "//www.bostonproper.com/cdn/shop/t/1521/assets/component-quantity-selector.js?v=67906657287482036641790713714", 157 "@theme/comparison-slider": "//www.bostonproper.com/cdn/shop/t/1521/assets/comparison-slider.js?v=106705562947035711941790713714", 158 "@theme/sticky-add-to-cart": "//www.bostonproper.com/cdn/shop/t/1521/assets/sticky-add-to-cart.js?v=48956925339276737091790713714", 159 "@theme/fly-to-cart": "//www.bostonproper.com/cdn/shop/t/1521/assets/fly-to-cart.js?v=48483246440248933081790713714" 160 } 161 } 162</script>
162 163
164<script 165 src="//www.bostonproper.com/cdn/shop/t/1521/assets/view-transitions.js?v=41230751666534493481790713714" 166 async 167 168></script>
168 169 170<link 171 rel="modulepreload" 172 href="//www.bostonproper.com/cdn/shop/t/1521/assets/utilities.js?v=28506172887375975941790713714" 173 fetchpriority="low" 174> 175<link 176 rel="modulepreload" 177 href="//www.bostonproper.com/cdn/shop/t/1521/assets/component.js?v=184240237101443119871790713714" 178 fetchpriority="low" 179> 180<link 181 rel="modulepreload" 182 href="//www.bostonproper.com/cdn/shop/t/1521/assets/section-renderer.js?v=170547751348887101551790713714" 183 fetchpriority="low" 184> 185<link 186 rel="modulepreload" 187 href="//www.bostonproper.com/cdn/shop/t/1521/assets/section-hydration.js?v=108434990705342316311790713714" 188 fetchpriority="low" 189> 190<link 191 rel="modulepreload" 192 href="//www.bostonproper.com/cdn/shop/t/1521/assets/morph.js?v=104045809772310330131790713714" 193 fetchpriority="low" 194> 195 196 197 198<link 199 rel="modulepreload" 200 href="//www.bostonproper.com/cdn/shop/t/1521/assets/focus.js?v=60436577539430446401790713714" 201 fetchpriority="low" 202> 203<link 204 rel="modulepreload" 205 href="//www.bostonproper.com/cdn/shop/t/1521/assets/recently-viewed-products.js?v=94729125001750262651790713714" 206 fetchpriority="low" 207> 208<link 209 rel="modulepreload" 210 href="//www.bostonproper.com/cdn/shop/t/1521/assets/scrolling.js?v=162288454571026455011790713714" 211 fetchpriority="low" 212> 213<link 214 rel="modulepreload" 215 href="//www.bostonproper.com/cdn/shop/t/1521/assets/events.js?v=1118589085892804011790713714" 216 fetchpriority="low" 217>
218<script 219 src="//www.bostonproper.com/cdn/shop/t/1521/assets/popover-polyfill.js?v=119029313658895111201790713714" 220 type="module" 221 fetchpriority="low" 222></script>
vendor: 1 bytes, line 222
222
223<script 224 src="//www.bostonproper.com/cdn/shop/t/1521/assets/overflow-list.js?v=46858356770684902511790713714" 225 type="module" 226 fetchpriority="low" 227></script>
vendor: 1 bytes, line 227
227
228<script 229 src="//www.bostonproper.com/cdn/shop/t/1521/assets/quick-add.js?v=141968020560480616261790713714" 230 type="module" 231 fetchpriority="low" 232></script>
232 233 234
234<script 235 src="//www.bostonproper.com/cdn/shop/t/1521/assets/cart-discount.js?v=107077853990124422021790713714" 236 type="module" 237 fetchpriority="low" 238 ></script>
238 239 240
241<script 242 src="//www.bostonproper.com/cdn/shop/t/1521/assets/dialog.js?v=150888536568694194021790713714" 243 type="module" 244 fetchpriority="low" 245></script>
vendor: 1 bytes, line 245
245
246<script 247 src="//www.bostonproper.com/cdn/shop/t/1521/assets/variant-picker.js?v=137587803232570873901790713714" 248 type="module" 249 fetchpriority="low" 250></script>
vendor: 1 bytes, line 250
250
251<script 252 src="//www.bostonproper.com/cdn/shop/t/1521/assets/product-card.js?v=28695665944407642501790713714" 253 type="module" 254 fetchpriority="low" 255></script>
vendor: 1 bytes, line 255
255
256<script 257 src="//www.bostonproper.com/cdn/shop/t/1521/assets/product-form.js?v=138228271698465017521790713714" 258 type="module" 259 fetchpriority="low" 260></script>
vendor: 1 bytes, line 260
260
261<script 262 src="//www.bostonproper.com/cdn/shop/t/1521/assets/fly-to-cart.js?v=48483246440248933081790713714" 263 type="module" 264 fetchpriority="low" 265></script>
vendor: 1 bytes, line 265
265
266<script 267 src="//www.bostonproper.com/cdn/shop/t/1521/assets/accordion-custom.js?v=97588419883090888471790713714" 268 type="module" 269 fetchpriority="low" 270></script>
vendor: 1 bytes, line 270
270
271<script 272 src="//www.bostonproper.com/cdn/shop/t/1521/assets/media.js?v=37825095869491791421790713714" 273 type="module" 274 fetchpriority="low" 275></script>
vendor: 1 bytes, line 275
275
276<script 277 src="//www.bostonproper.com/cdn/shop/t/1521/assets/product-price.js?v=83480980452100801711790713714" 278 type="module" 279 fetchpriority="low" 280></script>
vendor: 1 bytes, line 280
280
281<script 282 src="//www.bostonproper.com/cdn/shop/t/1521/assets/product-sku.js?v=121804655373531320511790713714" 283 type="module" 284 fetchpriority="low" 285></script>
vendor: 1 bytes, line 285
285
286<script 287 src="//www.bostonproper.com/cdn/shop/t/1521/assets/product-title-truncation.js?v=81744426891537421781790713714" 288 type="module" 289 fetchpriority="low" 290></script>
vendor: 1 bytes, line 290
290
291<script 292 src="//www.bostonproper.com/cdn/shop/t/1521/assets/product-inventory.js?v=81520546963552472391790713714" 293 type="module" 294 fetchpriority="low" 295></script>
vendor: 1 bytes, line 295
295
296<script 297 src="//www.bostonproper.com/cdn/shop/t/1521/assets/show-more.js?v=94563802205717136101790713714" 298 type="module" 299 fetchpriority="low" 300></script>
vendor: 1 bytes, line 300
300
301<script 302 src="//www.bostonproper.com/cdn/shop/t/1521/assets/slideshow.js?v=168726587398279102001790713714" 303 type="module" 304 fetchpriority="low" 305></script>
vendor: 1 bytes, line 305
305
306<script 307 src="//www.bostonproper.com/cdn/shop/t/1521/assets/layered-slideshow.js?v=30135417790894624731790713714" 308 type="module" 309 fetchpriority="low" 310></script>
vendor: 1 bytes, line 310
310
311<script 312 src="//www.bostonproper.com/cdn/shop/t/1521/assets/anchored-popover.js?v=95953304814728754461790713714" 313 type="module" 314 fetchpriority="low" 315></script>
vendor: 1 bytes, line 315
315
316<script 317 src="//www.bostonproper.com/cdn/shop/t/1521/assets/floating-panel.js?v=151277338116891310371790713714" 318 type="module" 319 fetchpriority="low" 320></script>
vendor: 1 bytes, line 320
320
321<script 322 src="//www.bostonproper.com/cdn/shop/t/1521/assets/video-background.js?v=25237992823806706551790713714" 323 type="module" 324 fetchpriority="low" 325></script>
vendor: 1 bytes, line 325
325
326<script 327 src="//www.bostonproper.com/cdn/shop/t/1521/assets/component-quantity-selector.js?v=67906657287482036641790713714" 328 type="module" 329 fetchpriority="low" 330></script>
vendor: 1 bytes, line 330
330
331<script 332 src="//www.bostonproper.com/cdn/shop/t/1521/assets/media-gallery.js?v=14624021005636302161790713714" 333 type="module" 334 fetchpriority="low" 335></script>
vendor: 1 bytes, line 335
335
336<script 337 src="//www.bostonproper.com/cdn/shop/t/1521/assets/rte-formatter.js?v=40306132146590816021790713714" 338 type="module" 339 fetchpriority="low" 340></script>
vendor: 1 bytes, line 340
340
341<script 342 src="//www.bostonproper.com/cdn/shop/t/1521/assets/volume-pricing.js?v=15442793518842484731790713714" 343 type="module" 344 fetchpriority="low" 345></script>
vendor: 1 bytes, line 345
345
346<script 347 src="//www.bostonproper.com/cdn/shop/t/1521/assets/price-per-item.js?v=144512917617120124431790713714" 348 type="module" 349 fetchpriority="low" 350></script>
vendor: 1 bytes, line 350
350
351<script 352 src="//www.bostonproper.com/cdn/shop/t/1521/assets/volume-pricing-info.js?v=94479640769665442661790713714" 353 type="module" 354 fetchpriority="low" 355></script>
355 356 357 358
358<script 359 src="//www.bostonproper.com/cdn/shop/t/1521/assets/localization.js?v=15585934801240115691790713714" 360 type="module" 361 fetchpriority="low" 362 ></script>
362 363 364 365 366
366<script 367 src="//www.bostonproper.com/cdn/shop/t/1521/assets/fly-to-cart.js?v=48483246440248933081790713714" 368 type="module" 369 fetchpriority="low" 370 ></script>
370 371
371<script 372 src="//www.bostonproper.com/cdn/shop/t/1521/assets/sticky-add-to-cart.js?v=48956925339276737091790713714" 373 type="module" 374 fetchpriority="low" 375 ></script>
375 376
376<script type="module"> 377 import { RecentlyViewed } from '@theme/recently-viewed-products'; 378 RecentlyViewed.addProduct('15286962618534'); 379 </script>
379 380 381
382<script 383 src="//www.bostonproper.com/cdn/shop/t/1521/assets/auto-close-details.js?v=58350291535404441581790713714" 384 defer="defer" 385></script>
385 386
387<script> 388 const Theme = { 389 translations: { 390 placeholder_image: `Placeholder image`, 391 added: `Added`, 392 recipient_form_fields_visible: `Recipient form fields are now visible`, 393 recipient_form_fields_hidden: `Recipient form fields are now hidden`, 394 recipient_form_error: `There was an error with the form submission`, 395 items_added_to_cart_one: `1 item added to cart`, 396 items_added_to_cart_other: `{{ count }} items added to cart`, 397 cart_count: `Total items in cart`, 398 }, 399 routes: { 400 cart_add_url: '/cart/add.js', 401 cart_change_url: '/cart/change', 402 cart_update_url: '/cart/update', 403 cart_url: '/cart', 404 predictive_search_url: '/search/suggest', 405 search_url: '/search', 406 }, 407 template: { 408 name: 'product', 409 }, 410 }; 411</script>
411 412<style data-shopify> 413@font-face { 414 font-family: Inter; 415 font-weight: 400; 416 font-style: normal; 417 font-display: swap; 418 src: url("//www.bostonproper.com/cdn/fonts/inter/inter_n4.b2a3f24c19b4de56e8871f609e73ca7f6d2e2bb9.woff2") format("woff2"), 419 url("//www.bostonproper.com/cdn/fonts/inter/inter_n4.af8052d517e0c9ffac7b814872cecc27ae1fa132.woff") format("woff"); 420} 421@font-face { 422 font-family: Inter; 423 font-weight: 700; 424 font-style: normal; 425 font-display: swap; 426 src: url("//www.bostonproper.com/cdn/fonts/inter/inter_n7.02711e6b374660cfc7915d1afc1c204e633421e4.woff2") format("woff2"), 427 url("//www.bostonproper.com/cdn/fonts/inter/inter_n7.6dab87426f6b8813070abd79972ceaf2f8d3b012.woff") format("woff"); 428} 429@font-face { 430 font-family: Inter; 431 font-weight: 400; 432 font-style: italic; 433 font-display: swap; 434 src: url("//www.bostonproper.com/cdn/fonts/inter/inter_i4.feae1981dda792ab80d117249d9c7e0f1017e5b3.woff2") format("woff2"), 435 url("//www.bostonproper.com/cdn/fonts/inter/inter_i4.62773b7113d5e5f02c71486623cf828884c85c6e.woff") format("woff"); 436} 437@font-face { 438 font-family: Inter; 439 font-weight: 700; 440 font-style: italic; 441 font-display: swap; 442 src: url("//www.bostonproper.com/cdn/fonts/inter/inter_i7.b377bcd4cc0f160622a22d638ae7e2cd9b86ea4c.woff2") format("woff2"), 443 url("//www.bostonproper.com/cdn/fonts/inter/inter_i7.7c69a6a34e3bb44fcf6f975857e13b9a9b25beb4.woff") format("woff"); 444} 445@font-face { 446 font-family: Inter; 447 font-weight: 500; 448 font-style: normal; 449 font-display: swap; 450 src: url("//www.bostonproper.com/cdn/fonts/inter/inter_n5.d7101d5e168594dd06f56f290dd759fba5431d97.woff2") format("woff2"), 451 url("//www.bostonproper.com/cdn/fonts/inter/inter_n5.5332a76bbd27da00474c136abb1ca3cbbf259068.woff") format("woff"); 452} 453@font-face { 454 font-family: Inter; 455 font-weight: 500; 456 font-style: italic; 457 font-display: swap; 458 src: url("//www.bostonproper.com/cdn/fonts/inter/inter_i5.4474f48e6ab2b1e01aa2b6d942dd27fa24f2d99f.woff2") format("woff2"), 459 url("//www.bostonproper.com/cdn/fonts/inter/inter_i5.493dbd6ee8e49f4ad722ebb68d92f201af2c2f56.woff") format("woff"); 460} 461 462 463 :root { 464 /* Page Layout */ 465 --sidebar-width: 25rem; 466 --narrow-content-width: 36rem; 467 --normal-content-width: 42rem; 468 --wide-content-width: 46rem; 469 --narrow-page-width: 90rem; 470 --normal-page-width: 120rem; 471 --wide-page-width: 150rem; 472 473 /* Section Heights */ 474 --section-height-small: 15rem; 475 --section-height-medium: 25rem; 476 --section-height-large: 35rem; 477 478 @media screen and (min-width: 40em) { 479 --section-height-small: 40svh; 480 --section-height-medium: 55svh; 481 --section-height-large: 70svh; 482 } 483 484 @media screen and (min-width: 60em) { 485 --section-height-small: 50svh; 486 --section-height-medium: 65svh; 487 --section-height-large: 80svh; 488 } 489 490 /* Letter spacing */ 491 --letter-spacing-sm: 0.06em; 492 --letter-spacing-md: 0.13em; 493 494 /* Font families */ 495 --font-body--family: Inter, sans-serif; 496 --font-body--style: normal; 497 --font-body--weight: 400; 498 --font-subheading--family: Inter, sans-serif; 499 --font-subheading--style: normal; 500 --font-subheading--weight: 500; 501 --font-heading--family: Inter, sans-serif; 502 --font-heading--style: normal; 503 --font-heading--weight: 700; 504 --font-accent--family: Inter, sans-serif; 505 --font-accent--style: normal; 506 --font-accent--weight: 700; 507 508 /* Margin sizes */ 509 --font-heading--spacing: 0.25em; 510 --font-h1--spacing: var(--font-heading--spacing); 511 --font-h2--spacing: var(--font-heading--spacing); 512 --font-h3--spacing: var(--font-heading--spacing); 513 --font-h4--spacing: var(--font-heading--spacing); 514 --font-h5--spacing: var(--font-heading--spacing); 515 --font-h6--spacing: var(--font-heading--spacing); 516 --font-paragraph--spacing: 0.5em; 517 518 /* Heading colors */ 519 --font-h1--color: var(--color-foreground-heading); 520 --font-h2--color: var(--color-foreground-heading); 521 --font-h3--color: var(--color-foreground-heading); 522 --font-h4--color: var(--color-foreground-heading); 523 --font-h5--color: var(--color-foreground-heading); 524 --font-h6--color: var(--color-foreground-heading); 525 526 /** Z-Index / Layering */ 527 --layer-section-background: -2; 528 --layer-lowest: -1; 529 --layer-base: 0; 530 --layer-flat: 1; 531 --layer-raised: 2; 532 --layer-heightened: 4; 533 --layer-sticky: 8; 534 --layer-window-overlay: 10; 535 --layer-header-menu: 12; 536 --layer-overlay: 16; 537 --layer-menu-drawer: 18; 538 --layer-temporary: 20; 539 540 /* Max-width / Measure */ 541 --max-width--body-normal: 32.5em; 542 --max-width--body-narrow: 22.75em; 543 544 --max-width--heading-normal: 32.5em; 545 --max-width--heading-narrow: 19.5em; 546 547 --max-width--display-normal: 13em; 548 --max-width--display-narrow: 9.75em; 549 --max-width--display-tight: 3.25em; 550 551 /* Letter-spacing / Tracking */ 552 --letter-spacing--display-tight: -0.03em; 553 --letter-spacing--display-normal: 0em; 554 --letter-spacing--display-loose: 0.03em; 555 556 --letter-spacing--heading-tight: -0.03em; 557 --letter-spacing--heading-normal: 0em; 558 --letter-spacing--heading-loose: 0.03em; 559 560 --letter-spacing--body-tight: -0.03em; 561 --letter-spacing--body-normal: 0em; 562 --letter-spacing--body-loose: 0.03em; 563 564 /* Line height / Leading */ 565 --line-height: 1; 566 567 --line-height--display-tight: 1; 568 --line-height--display-normal: 1.1; 569 --line-height--display-loose: 1.2; 570 571 --line-height--heading-tight: 1.15; 572 --line-height--heading-normal: 1.25; 573 --line-height--heading-loose: 1.35; 574 575 --line-height--body-tight: 1.2; 576 --line-height--body-normal: 1.4; 577 --line-height--body-loose: 1.6; 578 579 /* Typography presets */ 580 --font-size--paragraph: 0.875rem;--font-size--h1: clamp(3.0rem, 5.6vw, 3.5rem);--font-size--h2: clamp(2.25rem, 4.8vw, 3.0rem);--font-size--h3: 2.0rem;--font-size--h4: 1.5rem;--font-size--h5: 0.875rem;--font-size--h6: 0.75rem;--font-paragraph--weight: 400;--font-paragraph--letter-spacing: var(--letter-spacing--body-normal);--font-paragraph--size: var(--font-size--paragraph);--font-paragraph--family: var(--font-body--family);--font-paragraph--style: var(--font-body--style);--font-paragraph--case: ;--font-paragraph--line-height: var(--line-height--body-loose);--font-h1--weight: var(--font-heading--weight);--font-h1--letter-spacing: var(--letter-spacing--heading-normal);--font-h1--size: var(--font-size--h1);--font-h1--family: var(--font-heading--family);--font-h1--style: var(--font-heading--style);--font-h1--case: none;--font-h1--line-height: var(--line-height--display-tight);--font-h2--weight: var(--font-heading--weight);--font-h2--letter-spacing: var(--letter-spacing--heading-normal);--font-h2--size: var(--font-size--h2);--font-h2--family: var(--font-heading--family);--font-h2--style: var(--font-heading--style);--font-h2--case: none;--font-h2--line-height: var(--line-height--display-tight);--font-h3--weight: var(--font-heading--weight);--font-h3--letter-spacing: var(--letter-spacing--heading-normal);--font-h3--size: var(--font-size--h3);--font-h3--family: var(--font-heading--family);--font-h3--style: var(--font-heading--style);--font-h3--case: none;--font-h3--line-height: var(--line-height--display-normal);--font-h4--weight: var(--font-heading--weight);--font-h4--letter-spacing: var(--letter-spacing--heading-normal);--font-h4--size: var(--font-size--h4);--font-h4--family: var(--font-heading--family);--font-h4--style: var(--font-heading--style);--font-h4--case: none;--font-h4--line-height: var(--line-height--display-tight);--font-h5--weight: var(--font-subheading--weight);--font-h5--letter-spacing: var(--letter-spacing--heading-normal);--font-h5--size: var(--font-size--h5);--font-h5--family: var(--font-subheading--family);--font-h5--style: var(--font-subheading--style);--font-h5--case: none;--font-h5--line-height: var(--line-height--display-loose);--font-h6--weight: var(--font-subheading--weight);--font-h6--letter-spacing: var(--letter-spacing--heading-normal);--font-h6--size: var(--font-size--h6);--font-h6--family: var(--font-subheading--family);--font-h6--style: var(--font-subheading--style);--font-h6--case: none;--font-h6--line-height: var(--line-height--display-loose); 581 582 /* Hardcoded font sizes */ 583 --font-size--3xs: 0.625rem; 584 --font-size--2xs: 0.75rem; 585 --font-size--xs: 0.8125rem; 586 --font-size--sm: 0.875rem; 587 --font-size--md: 1rem; 588 --font-size--lg: 1.125rem; 589 --font-size--xl: 1.25rem; 590 --font-size--2xl: 1.5rem; 591 --font-size--3xl: 2rem; 592 --font-size--4xl: 2.5rem; 593 --font-size--5xl: 3rem; 594 --font-size--6xl: 3.5rem; 595 596 /* Menu font sizes */ 597 --menu-font-sm--size: 0.875rem; 598 --menu-font-sm--line-height: calc(1.1 + 0.5 * min(16 / 14)); 599 --menu-font-md--size: 1rem; 600 --menu-font-md--line-height: calc(1.1 + 0.5 * min(16 / 16)); 601 --menu-font-lg--size: 1.125rem; 602 --menu-font-lg--line-height: calc(1.1 + 0.5 * min(16 / 18)); 603 --menu-font-xl--size: 1.25rem; 604 --menu-font-xl--line-height: calc(1.1 + 0.5 * min(16 / 20)); 605 --menu-font-2xl--size: 1.75rem; 606 --menu-font-2xl--line-height: calc(1.1 + 0.5 * min(16 / 28)); 607 608 /* Colors */ 609 --color-error: #8B0000; 610 --color-success: #006400; 611 --color-white: #FFFFFF; 612 --color-white-rgb: 255 255 255; 613 --color-black: #000000; 614 --color-instock: #3ED660; 615 --color-lowstock: #EE9441; 616 --color-outofstock: #C8C8C8; 617 618 /* Opacity */ 619 --opacity-5: 0.05; 620 --opacity-8: 0.08; 621 --opacity-10: 0.1; 622 --opacity-15: 0.15; 623 --opacity-20: 0.2;
624 --opacity-25: 0.25; 625 --opacity-30: 0.3; 626 --opacity-40: 0.4; 627 --opacity-50: 0.5; 628 --opacity-60: 0.6; 629 --opacity-70: 0.7; 630 --opacity-80: 0.8; 631 --opacity-85: 0.85; 632 --opacity-90: 0.9; 633 --opacity-subdued-text: var(--opacity-70); 634 635 --shadow-button: 0 2px 3px rgb(0 0 0 / 20%); 636 --gradient-image-overlay: linear-gradient(to top, rgb(0 0 0 / 0.5), transparent); 637 638 /* Spacing */ 639 --margin-3xs: 0.125rem; 640 --margin-2xs: 0.3rem; 641 --margin-xs: 0.5rem; 642 --margin-sm: 0.7rem; 643 --margin-md: 0.8rem; 644 --margin-lg: 1rem; 645 --margin-xl: 1.25rem; 646 --margin-2xl: 1.5rem; 647 --margin-3xl: 1.75rem; 648 --margin-4xl: 2rem; 649 --margin-5xl: 3rem; 650 --margin-6xl: 5rem; 651 652 --scroll-margin: 50px; 653 654 --padding-3xs: 0.125rem; 655 --padding-2xs: 0.25rem; 656 --padding-xs: 0.5rem; 657 --padding-sm: 0.7rem; 658 --padding-md: 0.8rem; 659 --padding-lg: 1rem; 660 --padding-xl: 1.25rem; 661 --padding-2xl: 1.5rem; 662 --padding-3xl: 1.75rem; 663 --padding-4xl: 2rem; 664 --padding-5xl: 3rem; 665 --padding-6xl: 4rem; 666 667 --gap-3xs: 0.125rem; 668 --gap-2xs: 0.3rem; 669 --gap-xs: 0.5rem; 670 --gap-sm: 0.7rem; 671 --gap-md: 0.9rem; 672 --gap-lg: 1rem; 673 --gap-xl: 1.25rem; 674 --gap-2xl: 2rem; 675 --gap-3xl: 3rem; 676 677 --spacing-scale-sm: 0.6; 678 --spacing-scale-md: 0.7; 679 --spacing-scale-default: 1.0; 680 681 /* Checkout buttons gap */ 682 --checkout-button-gap: 10px; 683 684 /* Borders */ 685 --style-border-width: 1px; 686 --style-border-radius-xs: 0.2rem; 687 --style-border-radius-sm: 0.6rem; 688 --style-border-radius-md: 0.8rem; 689 --style-border-radius-50: 50%; 690 --style-border-radius-lg: 1rem; 691 --style-border-radius-pills: 40px; 692 --style-border-radius-inputs: 4px; 693 --style-border-radius-buttons-primary: 14px; 694 --style-border-radius-buttons-secondary: 14px; 695 --style-border-width-primary: 0px; 696 --style-border-width-secondary: 1px; 697 --style-border-width-inputs: 1px; 698 --style-border-radius-popover: 14px; 699 --style-border-popover: 1px none rgb(var(--color-border-rgb) / 50%); 700 --style-border-drawer: 1px none rgb(var(--color-border-rgb) / 50%); 701 --style-border-swatch-opacity: 10%; 702 --style-border-swatch-width: 1px; 703 --style-border-swatch-style: solid; 704 705 /* Animation */ 706 --ease-out-cubic: cubic-bezier(0.33, 1, 0.68, 1); 707 --ease-out-quad: cubic-bezier(0.32, 0.72, 0, 1); 708 --ease-in-out-quad: cubic-bezier(0.455, 0.03, 0.515, 0.955); 709 --animation-speed-fast: 0.0625s; 710 --animation-speed: 0.125s; 711 --animation-speed-slow: 0.2s; 712 --animation-speed-medium: 0.15s; 713 --animation-easing: ease-in-out; 714 --animation-slideshow-easing: cubic-bezier(0.4, 0, 0.2, 1); 715 --drawer-animation-speed: 0.2s; 716 --animation-values-slow: var(--animation-speed-slow) var(--animation-easing); 717 --animation-values: var(--animation-speed) var(--animation-easing); 718 --animation-values-fast: var(--animation-speed-fast) var(--animation-easing); 719 --animation-values-allow-discrete: var(--animation-speed) var(--animation-easing) allow-discrete; 720 --animation-timing-hover: cubic-bezier(0.25, 0.46, 0.45, 0.94); 721 --animation-timing-active: cubic-bezier(0.5, 0, 0.75, 0); 722 --animation-timing-bounce: cubic-bezier(0.34, 1.56, 0.64, 1); 723 --animation-timing-default: cubic-bezier(0, 0, 0.2, 1); 724 --animation-timing-fade-in: cubic-bezier(0.16, 1, 0.3, 1); 725 --animation-timing-fade-out: cubic-bezier(0.4, 0, 0.2, 1); 726 /* Spring definitions suffixed with perceptual duration (d<ms>) and bounce (b<%>)*/ 727 --spring-d300-b0-easing: linear(0, 0.0033 0.81%, 0.027 2.42%, 0.0916 4.84%, 0.4745 15.32%, 0.6422 20.97%, 0.7175, 0.7789, 0.8283 30.64%, 0.876, 0.9111 38.71%, 0.941, 0.9611 48.39%, 0.9707, 0.978 54.84%, 0.9885 62.1%, 0.9949 70.97%, 0.9982 82.26%, 0.9997 100%); 728 --spring-d300-b0-duration: 0.4980s; 729 --spring-d280-b0-easing: linear(0, 0.0044, 0.0164 1.85%, 0.085 4.63%, 0.4571 14.81%, 0.575 18.52%, 0.6505, 0.7148 24.07%, 0.7849, 0.8393, 0.8809 35.18%, 0.9189, 0.9453 44.44%, 0.9662, 0.9793 55.55%, 0.9894 62.95%, 0.995 71.28%, 0.9982 82.39%, 0.9997 99.98%); 730 --spring-d280-b0-duration: 0.4648s; 731 --spring-d260-b0-easing: linear(0, 0.0058, 0.0216 2.15%, 0.0748 4.29%, 0.4646 15.03%, 0.5673, 0.655, 0.7279, 0.7872, 0.8349, 0.8727 34.36%, 0.9107, 0.9379 42.95%, 0.9609 48.31%, 0.9778 54.76%, 0.9887 62.27%, 0.9948 70.86%, 0.9983 82.67%, 0.9997 99.85%); 732 --spring-d260-b0-duration: 0.4316s; 733 --spring-d220-b0-easing: linear( 734 0, 735 0.0093 1.37%, 736 0.0696 4.12%, 737 0.4667 15.09%, 738 0.6322 20.58%, 739 0.6986 23.32%, 740 0.7793 27.44%, 741 0.8219 30.18%, 742 0.8721, 743 0.9089 38.42%, 744 0.9427, 745 0.9644, 746 0.978 54.88%, 747 0.9881 61.74%, 748 0.9944 69.97%, 749 0.998 80.95%, 750 0.9996 98.78% 751 ); 752 --spring-d220-b0-duration: 0.3818s; 753 --spring-d180-b0-easing: linear(0, 0.0234, 0.0805 4.48%, 0.4865 15.68%, 0.6211 20.16%, 0.7696 26.88%, 0.8064 29.12%, 0.8646, 0.9063, 0.9358, 0.9563 47.04%, 0.9757, 0.9867 60.48%, 0.9941 69.44%, 0.9979 80.64%, 0.9996 98.56%); 754 --spring-d180-b0-duration: 0.2988s; 755 756 /* View transitions */ 757 /* View transition old */ 758 --view-transition-old-main-content: var(--animation-speed) var(--animation-easing) both fadeOut; 759 760 /* View transition new */ 761 --view-transition-new-main-content: var(--animation-speed) var(--animation-easing) both fadeIn, var(--animation-speed) var(--animation-easing) both slideInTopViewTransition; 762 763 /* Focus */ 764 --focus-outline-width: 0.09375rem; 765 --focus-outline-offset: 0.2em; 766 767 /* Badges */ 768 --badge-blob-padding-block: 1px; 769 --badge-blob-padding-inline: 12px 8px; 770 --badge-rectangle-padding-block: 1px; 771 --badge-rectangle-padding-inline: 6px; 772 @media screen and (min-width: 750px) { 773 --badge-blob-padding-block: 4px; 774 --badge-blob-padding-inline: 16px 12px; 775 --badge-rectangle-padding-block: 4px; 776 --badge-rectangle-padding-inline: 10px; 777 } 778 779 /* Icons */ 780 --icon-size-2xs: 0.6rem; 781 --icon-size-xs: 0.85rem; 782 --icon-size-sm: 1.25rem; 783 --icon-size-md: 1.375rem; 784 --icon-size-lg: 1.5rem; 785 --icon-stroke-width: 1.5px; 786 787 /* Input */ 788 --input-email-min-width: 200px; 789 --input-search-max-width: 650px; 790 --input-padding-y: 0.8rem; 791 --input-padding-x: 0.8rem; 792 --input-padding: var(--input-padding-y) var(--input-padding-x); 793 --input-box-shadow-width: var(--style-border-width-inputs); 794 --input-box-shadow: 0 0 0 var(--input-box-shadow-width) var(--color-input-border); 795 --input-box-shadow-focus: 0 0 0 calc(var(--input-box-shadow-width) + 0.5px) var(--color-input-border); 796 --input-disabled-background-color: rgb(var(--color-foreground-rgb) / var(--opacity-10)); 797 --input-disabled-border-color: rgb(var(--color-foreground-rgb) / var(--opacity-5)); 798 --input-disabled-text-color: rgb(var(--color-foreground-rgb) / var(--opacity-50));
799 --input-textarea-min-height: 55px; 800 801 /* Checkbox */ 802 --checkbox-size: 22px; 803 --checkbox-border-radius: 7px; 804 --checkbox-border: 1px solid rgb(var(--color-foreground-rgb) / var(--opacity-35-55)); 805 --checkbox-label-padding: 8px; 806 807 @media screen and (min-width: 750px) { 808 --checkbox-size: 16px; 809 --checkbox-border-radius: 5px; 810 --checkbox-label-padding: 6px; 811 } 812 813 /* Button size */ 814 --button-size-sm: 30px; 815 --button-size-md: 36px; 816 --button-size: var(--minimum-touch-target); 817 --button-padding-inline: 24px; 818 --button-padding-block: 16px; 819 820 /* Button font-family */ 821 --button-font-family-primary: var(--font-body--family); 822 --button-font-family-secondary: var(--font-body--family); 823 824 /* Button text case */ 825 --button-text-case: ; 826 --button-text-case-primary: default; 827 --button-text-case-secondary: default; 828 829 /* Borders */ 830 --border-color: rgb(var(--color-border-rgb) / var(--opacity-50)); 831 --border-width-sm: 1px; 832 --border-width-md: 2px; 833 --border-width-lg: 5px; 834 --border-radius-sm: 0.25rem; 835 836 /* Drawers */ 837 --drawer-inline-padding: 25px; 838 --drawer-menu-inline-padding: 2.5rem; 839 --drawer-header-block-padding: 20px; 840 --drawer-content-block-padding: 10px; 841 --drawer-header-desktop-top: 0rem; 842 --drawer-padding: calc(var(--padding-sm) + 7px); 843 --drawer-height: 100dvh; 844 --drawer-width: 95vw; 845 --drawer-max-width: 500px; 846 847 /* Variant Picker Swatches */ 848 --variant-picker-swatch-width-unitless: 34; 849 --variant-picker-swatch-height-unitless: 34; 850 --variant-picker-swatch-width: 34px; 851 --variant-picker-swatch-height: 34px; 852 --variant-picker-swatch-radius: 32px; 853 --variant-picker-border-width: 1px; 854 --variant-picker-border-style: solid; 855 --variant-picker-border-opacity: 10%; 856 857 /* Variant Picker Buttons */ 858 --variant-picker-button-radius: 14px; 859 --variant-picker-button-border-width: 1px; 860 861 /* Slideshow */ 862 --slideshow-controls-size: 3.5rem; 863 --slideshow-controls-icon: 2rem; 864 --peek-next-slide-size: 3rem; 865 866 /* Utilities */ 867 --backdrop-opacity: 0.15; 868 --backdrop-color-rgb: var(--color-shadow-rgb); 869 --minimum-touch-target: 44px; 870 --disabled-opacity: 0.5; 871 --skeleton-opacity: 0.025; 872 873 /* Shapes */ 874 --shape--circle: circle(50% at center); 875 --shape--sunburst: polygon(100% 50%,94.62% 55.87%,98.3% 62.94%,91.57% 67.22%,93.3% 75%,85.7% 77.39%,85.36% 85.36%,77.39% 85.7%,75% 93.3%,67.22% 91.57%,62.94% 98.3%,55.87% 94.62%,50% 100%,44.13% 94.62%,37.06% 98.3%,32.78% 91.57%,25% 93.3%,22.61% 85.7%,14.64% 85.36%,14.3% 77.39%,6.7% 75%,8.43% 67.22%,1.7% 62.94%,5.38% 55.87%,0% 50%,5.38% 44.13%,1.7% 37.06%,8.43% 32.78%,6.7% 25%,14.3% 22.61%,14.64% 14.64%,22.61% 14.3%,25% 6.7%,32.78% 8.43%,37.06% 1.7%,44.13% 5.38%,50% 0%,55.87% 5.38%,62.94% 1.7%,67.22% 8.43%,75% 6.7%,77.39% 14.3%,85.36% 14.64%,85.7% 22.61%,93.3% 25%,91.57% 32.78%,98.3% 37.06%,94.62% 44.13%); 876 --shape--diamond: polygon(50% 0%, 100% 50%, 50% 100%, 0% 50%); 877 --shape--blob: polygon(85.349% 11.712%, 87.382% 13.587%, 89.228% 15.647%, 90.886% 17.862%, 92.359% 20.204%, 93.657% 22.647%, 94.795% 25.169%, 95.786% 27.752%, 96.645% 30.382%, 97.387% 33.048%, 98.025% 35.740%, 98.564% 38.454%, 99.007% 41.186%, 99.358% 43.931%, 99.622% 46.685%, 99.808% 49.446%, 99.926% 52.210%, 99.986% 54.977%, 99.999% 57.744%, 99.975% 60.511%, 99.923% 63.278%, 99.821% 66.043%, 99.671% 68.806%, 99.453% 71.565%, 99.145% 74.314%, 98.724% 77.049%, 98.164% 79.759%, 97.433% 82.427%, 96.495% 85.030%, 95.311% 87.529%, 93.841% 89.872%, 92.062% 91.988%, 89.972% 93.796%, 87.635% 95.273%, 85.135% 96.456%, 82.532% 97.393%, 79.864% 98.127%, 77.156% 98.695%, 74.424% 99.129%, 71.676% 99.452%, 68.918% 99.685%, 66.156% 99.844%, 63.390% 99.942%, 60.624% 99.990%, 57.856% 99.999%, 55.089% 99.978%, 52.323% 99.929%, 49.557% 99.847%, 46.792% 99.723%, 44.031% 99.549%, 41.273% 99.317%, 38.522% 99.017%, 35.781% 98.639%, 33.054% 98.170%, 30.347% 97.599%, 27.667% 96.911%, 25.024% 96.091%, 22.432% 95.123%, 19.907% 93.994%, 17.466% 92.690%, 15.126% 91.216%, 12.902% 89.569%, 10.808% 87.761%, 8.854% 85.803%, 7.053% 83.703%, 5.418% 81.471%, 3.962% 79.119%, 2.702% 76.656%, 1.656% 74.095%, 0.846% 71.450%, 0.294% 68.740%, 0.024% 65.987%, 0.050% 63.221%, 0.343% 60.471%, 0.858% 57.752%, 1.548% 55.073%, 2.370% 52.431%, 3.283% 49.819%, 4.253% 47.227%, 5.249% 44.646%, 6.244% 42.063%, 7.211% 39.471%, 8.124% 36.858%, 8.958% 34.220%, 9.711% 31.558%, 10.409% 28.880%, 11.083% 26.196%, 11.760% 23.513%, 12.474% 20.839%, 13.259% 18.186%, 14.156% 15.569%, 15.214% 13.012%, 16.485% 10.556%, 18.028% 8.261%, 19.883
877% 6.211%, 22.041% 4.484%, 24.440% 3.110%, 26.998% 2.057%, 29.651% 1.275%, 32.360% 0.714%, 35.101% 0.337%, 37.859% 0.110%, 40.624% 0.009%, 43.391% 0.016%, 46.156% 0.113%, 48.918% 0.289%, 51.674% 0.533%, 54.425% 0.837%, 57.166% 1.215%, 59.898% 1.654%, 62.618% 2.163%, 65.322% 2.750%, 68.006% 3.424%, 70.662% 4.197%, 73.284% 5.081%, 75.860% 6.091%, 78.376% 7.242%, 80.813% 8.551%, 83.148% 10.036%, 85.349% 11.712%); 878 879 /* Buy buttons */ 880 --height-buy-buttons: calc(var(--padding-lg) * 2 + var(--icon-size-sm)); 881 882 /* Card image width and height variables */ 883 --card-width-small: 10rem; 884 885 --height-small: 10rem; 886 --height-medium: 11.5rem; 887 --height-large: 13rem; 888 --height-full: 100vh; 889 890 @media screen and (min-width: 750px) { 891 --height-small: 17.5rem; 892 --height-medium: 21.25rem; 893 --height-large: 25rem; 894 } 895 896 /* Modal */ 897 --modal-max-height: 65dvh; 898 --quick-add-modal-height: min(38.5rem, 96vh); 899 --quick-add-modal-width: min(54rem, 96vw); 900 --quick-add-modal-gallery-width: 24rem; 901 902 /* Card styles for search */ 903 --card-bg-hover: rgb(var(--color-foreground-rgb) / var(--opacity-5)); 904 --card-border-hover: rgb(var(--color-foreground-rgb) / var(--opacity-30)); 905 --card-border-focus: rgb(var(--color-foreground-rgb) / var(--opacity-10)); 906 907 /* Cart */ 908 --cart-primary-font-family: var(--font-body--family); 909 --cart-primary-font-style: var(--font-body--style); 910 --cart-primary-font-weight: var(--font-body--weight); 911 --cart-secondary-font-family: var(--font-secondary--family); 912 --cart-secondary-font-style: var(--font-secondary--style); 913 --cart-secondary-font-weight: var(--font-secondary--weight); 914 } 915</style> 916 917 918<style data-shopify> 919 920 921 :root, 922 923 924 925 .color-scheme-1 { 926 --color-background: rgb(254 255 250 / 1.0); 927 /* RGB values only to apply different opacities - Relative color values are not supported in iOS < 16.4 */ 928 --color-background-rgb: 254 255 250; 929 --opacity-5-15: 0.05; 930 --opacity-10-25: 0.1; 931 --opacity-35-55: 0.35; 932 --opacity-40-60: 0.4; 933 --opacity-30-60: 0.3; 934 --color-foreground: rgb(0 0 0 / 0.81); 935 --color-foreground-rgb: 0 0 0; 936 --color-foreground-heading: rgb(0 0 0 / 1.0); 937 --color-foreground-heading-rgb: 0 0 0; 938 --color-primary: rgb(0 0 0 / 0.81); 939 --color-primary-rgb: 0 0 0; 940 --color-primary-hover: rgb(0 0 0 / 1.0); 941 --color-primary-hover-rgb: 0 0 0; 942 --color-border: rgb(0 0 0 / 0.06); 943 --color-border-rgb: 0 0 0; 944 --color-shadow: rgb(0 0 0 / 1.0); 945 --color-shadow-rgb: 0 0 0; 946 --color-primary-button-text: rgb(255 255 255 / 1.0); 947 --color-primary-button-background: rgb(0 0 0 / 1.0); 948 --color-primary-button-border: rgb(0 0 0 / 1.0); 949 --color-primary-button-hover-text: rgb(255 255 255 / 1.0); 950 --color-primary-button-hover-background: rgb(51 51 51 / 1.0); 951 --color-primary-button-hover-border: rgb(0 0 0 / 1.0); 952 --color-secondary-button-text: rgb(0 0 0 / 1.0); 953 --color-secondary-button-background: rgb(0 0 0 / 0.0); 954 --color-secondary-button-border: rgb(0 0 0 / 1.0); 955 --color-secondary-button-hover-text: rgb(51 51 51 / 1.0); 956 --color-secondary-button-hover-background: rgb(250 250 250 / 1.0); 957 --color-secondary-button-hover-border: rgb(51 51 51 / 1.0); 958 --color-input-background: rgb(255 255 255 / 0.78); 959 --color-input-text: rgb(51 51 51 / 1.0); 960 --color-input-text-rgb: 51 51 51; 961 --color-input-border: rgb(223 223 223 / 1.0); 962 --color-input-hover-background: rgb(0 0 0 / 0.01); 963 --color-variant-background: rgb(255 255 255 / 1.0); 964 --color-variant-border: rgb(0 0 0 / 0.13); 965 --color-variant-text: rgb(0 0 0 / 1.0); 966 --color-variant-text-rgb: 0 0 0; 967 --color-variant-hover-background: rgb(245 245 245 / 1.0); 968 --color-variant-hover-text: rgb(0 0 0 / 1.0); 969 --color-variant-hover-border: rgb(230 230 230 / 1.0); 970 --color-selected-variant-background: rgb(0 0 0 / 1.0); 971 --color-selected-variant-border: rgb(0 0 0 / 1.0); 972 --color-selected-variant-text: rgb(255 255 255 / 1.0); 973 --color-selected-variant-hover-background: rgb(26 26 26 / 1.0); 974 --color-selected-variant-hover-text: rgb(255 255 255 / 1.0); 975 --color-selected-variant-hover-border: rgb(26 26 26 / 1.0); 976 977 --input-disabled-background-color: rgb(var(--color-foreground-rgb) / var(--opacity-10)); 978 --input-disabled-border-color: rgb(var(--color-foreground-rgb) / var(--opacity-5-15)); 979 --input-disabled-text-color: rgb(var(--color-foreground-rgb) / var(--opacity-50)); 980 --color-foreground-muted: rgb(var(--color-foreground-rgb) / var(--opacity-60)); 981 --font-h1--color: var(--color-foreground-heading); 982 --font-h2--color: var(--color-foreground-heading); 983 --font-h3--color: var(--color-foreground-heading); 984 --font-h4--color: var(--color-foreground-heading); 985 --font-h5--color: var(--color-foreground-heading); 986 --font-h6--color: var(--color-foreground-heading); 987 988 /* Shadows */ 989 990 --shadow-drawer: 0px 4px 20px rgb(var(--color-shadow-rgb) / var(--opacity-15)); 991 992 993 --shadow-blur: 20px; 994 --shadow-popover: 0px 4px 20px rgb(var(--color-shadow-rgb) / var(--opacity-15)); 995 996 } 997 998 999 1000 1001 .color-scheme-2 {
1002 --color-background: rgb(245 245 245 / 1.0); 1003 /* RGB values only to apply different opacities - Relative color values are not supported in iOS < 16.4 */ 1004 --color-background-rgb: 245 245 245; 1005 --opacity-5-15: 0.05; 1006 --opacity-10-25: 0.1; 1007 --opacity-35-55: 0.35; 1008 --opacity-40-60: 0.4; 1009 --opacity-30-60: 0.3; 1010 --color-foreground: rgb(0 0 0 / 0.81); 1011 --color-foreground-rgb: 0 0 0; 1012 --color-foreground-heading: rgb(0 0 0 / 1.0); 1013 --color-foreground-heading-rgb: 0 0 0; 1014 --color-primary: rgb(0 0 0 / 0.81); 1015 --color-primary-rgb: 0 0 0; 1016 --color-primary-hover: rgb(255 255 255 / 1.0); 1017 --color-primary-hover-rgb: 255 255 255; 1018 --color-border: rgb(223 223 223 / 1.0); 1019 --color-border-rgb: 223 223 223; 1020 --color-shadow: rgb(0 0 0 / 1.0); 1021 --color-shadow-rgb: 0 0 0; 1022 --color-primary-button-text: rgb(255 255 255 / 1.0); 1023 --color-primary-button-background: rgb(0 0 0 / 1.0); 1024 --color-primary-button-border: rgb(0 0 0 / 1.0); 1025 --color-primary-button-hover-text: rgb(255 255 255 / 1.0); 1026 --color-primary-button-hover-background: rgb(51 51 51 / 1.0); 1027 --color-primary-button-hover-border: rgb(51 51 51 / 1.0); 1028 --color-secondary-button-text: rgb(0 0 0 / 1.0); 1029 --color-secondary-button-background: rgb(0 0 0 / 0.0); 1030 --color-secondary-button-border: rgb(0 0 0 / 1.0); 1031 --color-secondary-button-hover-text: rgb(0 0 0 / 1.0); 1032 --color-secondary-button-hover-background: rgb(255 255 255 / 0.36); 1033 --color-secondary-button-hover-border: rgb(51 51 51 / 0.73); 1034 --color-input-background: rgb(0 0 0 / 0.0); 1035 --color-input-text: rgb(0 0 0 / 0.53); 1036 --color-input-text-rgb: 0 0 0; 1037 --color-input-border: rgb(0 0 0 / 0.13); 1038 --color-input-hover-background: rgb(255 255 255 / 0.36); 1039 --color-variant-background: rgb(255 255 255 / 1.0); 1040 --color-variant-border: rgb(230 230 230 / 1.0); 1041 --color-variant-text: rgb(0 0 0 / 1.0); 1042 --color-variant-text-rgb: 0 0 0; 1043 --color-variant-hover-background: rgb(245 245 245 / 1.0); 1044 --color-variant-hover-text: rgb(0 0 0 / 1.0); 1045 --color-variant-hover-border: rgb(230 230 230 / 1.0); 1046 --color-selected-variant-background: rgb(0 0 0 / 1.0); 1047 --color-selected-variant-border: rgb(0 0 0 / 1.0); 1048 --color-selected-variant-text: rgb(255 255 255 / 1.0); 1049 --color-selected-variant-hover-background: rgb(26 26 26 / 1.0); 1050 --color-selected-variant-hover-text: rgb(255 255 255 / 1.0); 1051 --color-selected-variant-hover-border: rgb(26 26 26 / 1.0); 1052 1053 --input-disabled-background-color: rgb(var(--color-foreground-rgb) / var(--opacity-10)); 1054 --input-disabled-border-color: rgb(var(--color-foreground-rgb) / var(--opacity-5-15)); 1055 --input-disabled-text-color: rgb(var(--color-foreground-rgb) / var(--opacity-50)); 1056 --color-foreground-muted: rgb(var(--color-foreground-rgb) / var(--opacity-60)); 1057 --font-h1--color: var(--color-foreground-heading); 1058 --font-h2--color: var(--color-foreground-heading); 1059 --font-h3--color: var(--color-foreground-heading); 1060 --font-h4--color: var(--color-foreground-heading); 1061 --font-h5--color: var(--color-foreground-heading); 1062 --font-h6--color: var(--color-foreground-heading); 1063 1064 /* Shadows */ 1065 1066 --shadow-drawer: 0px 4px 20px rgb(var(--color-shadow-rgb) / var(--opacity-15)); 1067 1068 1069 --shadow-blur: 20px; 1070 --shadow-popover: 0px 4px 20px rgb(var(--color-shadow-rgb) / var(--opacity-15)); 1071 1072 } 1073 1074 1075 1076 1077 .color-scheme-3 { 1078 --color-background: rgb(238 241 234 / 1.0); 1079 /* RGB values only to apply different opacities - Relative color values are not supported in iOS < 16.4 */ 1080 --color-background-rgb: 238 241 234; 1081 --opacity-5-15: 0.05; 1082 --opacity-10-25: 0.1; 1083 --opacity-35-55: 0.35; 1084 --opacity-40-60: 0.4; 1085 --opacity-30-60: 0.3; 1086 --color-foreground: rgb(0 0 0 / 0.81); 1087 --color-foreground-rgb: 0 0 0; 1088 --color-foreground-heading: rgb(0 0 0 / 1.0); 1089 --color-foreground-heading-rgb: 0 0 0; 1090 --color-primary: rgb(0 0 0 / 0.81); 1091 --color-primary-rgb: 0 0 0; 1092 --color-primary-hover: rgb(0 0 0 / 1.0); 1093 --color-primary-hover-rgb: 0 0 0; 1094 --color-border: rgb(0 0 0 / 0.81); 1095 --color-border-rgb: 0 0 0; 1096 --color-shadow: rgb(0 0 0 / 1.0); 1097 --color-shadow-rgb: 0 0 0; 1098 --color-primary-button-text: rgb(255 255 255 / 1.0); 1099 --color-primary-button-background: rgb(0 0 0 / 1.0); 1100 --color-primary-button-border: rgb(0 0 0 / 1.0); 1101 --color-primary-button-hover-text: rgb(255 255 255 / 1.0); 1102 --color-primary-button-hover-background: rgb(51 51 51 / 1.0); 1103 --color-primary-button-hover-border: rgb(51 51 51 / 1.0); 1104 --color-secondary-button-text: rgb(0 0 0 / 1.0); 1105 --color-secondary-button-background: rgb(0 0 0 / 0.0); 1106 --color-secondary-button-border: rgb(0 0 0 / 1.0); 1107 --color-secondary-button-hover-text: rgb(0 0 0 / 0.81); 1108 --color-secondary-button-hover-background: rgb(255 255 255 / 0.36); 1109 --color-secondary-button-hover-border: rgb(0 0 0 / 0.81); 1110 --color-input-background: rgb(0 0 0 / 0.0); 1111 --color-input-text: rgb(0 0 0 / 0.81); 1112 --color-input-text-rgb: 0 0 0; 1113 --color-input-border: rgb(0 0 0 / 0.81); 1114 --color-input-hover-background: rgb(255 255 255 / 0.36); 1115 --color-variant-background: rgb(255 255 255 / 1.0); 1116 --color-variant-border: rgb(230 230 230 / 1.0); 1117 --color-variant-text: rgb(0 0 0 / 1.0); 1118 --color-variant-text-rgb: 0 0 0; 1119 --color-variant-hover-background: rgb(245 245 245 / 1.0); 1120 --color-variant-hover-text: rgb(0 0 0 / 1.0); 1121 --color-variant-hover-border: rgb(230 230 230 / 1.0); 1122 --color-selected-variant-background: rgb(0 0 0 / 1.0); 1123 --color-selected-variant-border: rgb(0 0 0 / 1.0); 1124 --color-selected-variant-text: rgb(255 255 255 / 1.0); 1125 --color-selected-variant-hover-background: rgb(26 26 26 / 1.0); 1126 --color-selected-variant-hover-text: rgb(255 255 255 / 1.0); 1127 --color-selected-variant-hover-border: rgb(26 26 26 / 1.0); 1128 1129 --input-disabled-background-color: rgb(var(--color-foreground-rgb) / var(--opacity-10)); 1130 --input-disabled-border-color: rgb(var(--color-foreground-rgb) / var(--opacity-5-15)); 1131 --input-disabled-text-color: rgb(var(--color-foreground-rgb) / var(--opacity-50)); 1132 --color-foreground-muted: rgb(var(--color-foreground-rgb) / var(--opacity-60)); 1133 --font-h1--color: var(--color-foreground-heading); 1134 --font-h2--color: var(--color-foreground-heading); 1135 --font-h3--color: var(--color-foreground-heading); 1136 --font-h4--color: var(--color-foreground-heading); 1137 --font-h5--color: var(--color-foreground-heading); 1138 --font-h6--color: var(--color-foreground-heading); 1139 1140 /* Shadows */ 1141 1142 --shadow-drawer: 0px 4px 20px rgb(var(--color-shadow-rgb) / var(--opacity-15)); 1143 1144 1145 --shadow-blur: 20px; 1146 --shadow-popover: 0px 4px 20px rgb(var(--color-shadow-rgb) / var(--opacity-15)); 1147 1148 } 1149 1150 1151 1152 1153 .color-scheme-4 { 1154 --color-background: rgb(225 237 245 / 1.0); 1155 /* RGB values only to apply different opacities - Relative color values are not supported in iOS < 16.4 */ 1156 --color-background-rgb: 225 237 245; 1157 --opacity-5-15: 0.05; 1158 --opacity-10-25: 0.1; 1159 --opacity-35-55: 0.35; 1160 --opacity-40-60: 0.4; 1161 --opacity-30-60: 0.3; 1162 --color-foreground: rgb(0 0 0 / 0.81); 1163 --color-foreground-rgb: 0 0 0; 1164 --color-foreground-heading: rgb(0 0 0 / 1.0); 1165 --color-foreground-heading-rgb: 0 0 0; 1166 --color-primary: rgb(0 0 0 / 0.81); 1167 --color-primary-rgb: 0 0 0; 1168 --color-primary-hover: rgb(0 0 0 / 1.0); 1169 --color-primary-hover-rgb: 0 0 0; 1170 --color-border: rgb(29 54 134 / 0.5); 1171 --color-border-rgb: 29 54 134; 1172 --color-shadow: rgb(0 0 0 / 1.0); 1173 --color-shadow-rgb: 0 0 0; 1174 --color-primary-button-text: rgb(255 255 255 / 1.0); 1175 --color-primary-button-background: rgb(0 0 0 / 1.0); 1176 --color-primary-button-border: rgb(29 54 134 / 1.0); 1177 --color-primary-button-hover-text: rgb(255 255 255 / 1.0); 1178 --color-primary-button-hover-background: rgb(51 51 51 / 1.0); 1179 --color-primary-button-hover-border: rgb(0 0 0 / 1.0); 1180 --color-secondary-button-text: rgb(0 0 0 / 1.0); 1181 --color-secondary-button-background: rgb(0 0 0 / 0.0); 1182 --color-secondary-button-border: rgb(0 0 0 / 1.0); 1183 --color-secondary-button-hover-text: rgb(0 0 0 / 0.81); 1184 --color-secondary-button-hover-background: rgb(255 255 255 / 0.36); 1185 --color-secondary-button-hover-border: rgb(0 0 0 / 0.81); 1186 --color-input-background: rgb(0 0 0 / 0.0); 1187 --color-input-text: rgb(0 0 0 / 0.81); 1188 --color-input-text-rgb: 0 0 0; 1189 --color-input-border: rgb(0 0 0 / 0.81); 1190 --color-input-hover-background: rgb(255 255 255 / 0.36); 1191 --color-variant-background: rgb(255 255 255 / 1.0); 1192 --color-variant-border: rgb(230 230 230 / 1.0); 1193 --color-variant-text: rgb(0 0 0 / 1.0); 1194 --color-variant-text-rgb: 0 0 0; 1195 --color-variant-hover-background: rgb(245 245 245 / 1.0); 1196 --color-variant-hover-text: rgb(0 0 0 / 1.0); 1197 --color-variant-hover-border: rgb(230 230 230 / 1.0); 1198 --color-selected-variant-background: rgb(0 0 0 / 1.0); 1199 --color-selected-variant-border: rgb(0 0 0 / 1.0); 1200 --color-selected-variant-text: rgb(255 255 255 / 1.0); 1201 --color-selected-variant-hover-background: rgb(26 26 26 / 1.0); 1202 --color-selected-variant-hover-text: rgb(255 255 255 / 1.0); 1203 --color-selected-variant-hover-border: rgb(26 26 26 / 1.0); 1204 1205 --input-disabled-background-color: rgb(var(--color-foreground-rgb) / var(--opacity-10)); 1206 --input-disabled-border-color: rgb(var(--color-foreground-rgb) / var(--opacity-5-15)); 1207 --input-disabled-text-color: rgb(var(--color-foreground-rgb) / var(--opacity-50)); 1208 --color-foreground-muted: rgb(var(--color-foreground-rgb) / var(--opacity-60)); 1209 --font-h1--color: var(--color-foreground-heading); 1210 --font-h2--color: var(--color-foreground-heading); 1211 --font-h3--color: var(--color-foreground-heading); 1212 --font-h4--color: var(--color-foreground-heading); 1213 --font-h5--color: var(--color-foreground-heading); 1214 --font-h6--color: var(--color-foreground-heading); 1215 1216 /* Shadows */ 1217 1218 --shadow-drawer: 0px 4px 20px rgb(var(--color-shadow-rgb) / var(--opacity-15)); 1219 1220 1221 --shadow-blur: 20px; 1222 --shadow-popover: 0px 4px 20px rgb(var(--color-shadow-rgb) / var(--opacity-15)); 1223 1224 } 1225 1226 1227 1228 1229 .color-scheme-5 { 1230 --color-background: rgb(51 51 51 / 1.0); 1231 /* RGB values only to apply different opacities - Relative color values are not supported in iOS < 16.4 */ 1232 --color-background-rgb: 51 51 51; 1233 --opacity-5-15: 0.15; 1234 --opacity-10-25: 0.25; 1235 --opacity-35-55: 0.55; 1236 --opacity-40-60: 0.6; 1237 --opacity-30-60: 0.6; 1238 --color-foreground: rgb(255 255 255 / 1.0); 1239 --color-foreground-rgb: 255 255 255; 1240 --color-foreground-heading: rgb(255 255 255 / 1.0); 1241 --color-foreground-heading-rgb: 255 255 255; 1242 --color-primary: rgb(255 255 255 / 1.0); 1243 --color-primary-rgb: 255 255 255; 1244 --color-primary-hover: rgb(255 255 255 / 0.69); 1245 --color-primary-hover-rgb: 255 255 255; 1246 --color-border: rgb(255 255 255 / 0.69); 1247 --color-border-rgb: 255 255 255; 1248 --color-shadow: rgb(0 0 0 / 1.0); 1249 --color-shadow-rgb: 0 0 0; 1250 --color-primary-button-text: rgb(0 0 0 / 1.0); 1251 --color-primary-button-background: rgb(255 255 255 / 1.0); 1252 --color-primary-button-border: rgb(255 255 255 / 1.0); 1253 --color-primary-button-hover-text: rgb(255 255 255 / 1.0); 1254 --color-primary-button-hover-background: rgb(0 0 0 / 1.0); 1255 --color-primary-button-hover-border: rgb(0 0 0 / 1.0); 1256 --color-secondary-button-text: rgb(255 255 255 / 1.0); 1257 --color-secondary-button-background: rgb(0 0 0 / 0.0); 1258 --color-secondary-button-border: rgb(255 255 255 / 0.69); 1259 --color-secondary-button-hover-text: rgb(255 255 255 / 0.93); 1260 --color-secondary-button-hover-background: rgb(255 255 255 / 0.04); 1261 --color-secondary-button-hover-border: rgb(255 255 255 / 0.69); 1262 --color-input-background: rgb(51 51 51 / 1.0); 1263 --color-input-text: rgb(255 255 255 / 0.93); 1264 --color-input-text-rgb: 255 255 255; 1265 --color-input-border: rgb(255 255 255 / 0.69); 1266 --color-input-hover-background: rgb(255 255 255 / 0.04); 1267 --color-variant-background: rgb(255 255 255 / 1.0); 1268 --color-variant-border: rgb(230 230 230 / 1.0); 1269 --color-variant-text: rgb(0 0 0 / 1.0); 1270 --color-variant-text-rgb: 0 0 0; 1271 --color-variant-hover-background: rgb(245 245 245 / 1.0); 1272 --color-variant-hover-text: rgb(0 0 0 / 1.0); 1273 --color-variant-hover-border: rgb(230 230 230 / 1.0); 1274 --color-selected-variant-background: rgb(0 0 0 / 1.0); 1275 --color-selected-variant-border: rgb(0 0 0 / 1.0); 1276 --color-selected-variant-text: rgb(255 255 255 / 1.0); 1277 --color-selected-variant-hover-background: rgb(26 26 26 / 1.0); 1278 --color-selected-variant-hover-text: rgb(255 255 255 / 1.0); 1279 --color-selected-variant-hover-border: rgb(26 26 26 / 1.0); 1280 1281 --input-disabled-background-color: rgb(var(--color-foreground-rgb) / var(--opacity-10)); 1282 --input-disabled-border-color: rgb(var(--color-foreground-rgb) / var(--opacity-5-15)); 1283 --input-disabled-text-color: rgb(var(--color-foreground-rgb) / var(--opacity-50)); 1284 --color-foreground-muted: rgb(var(--color-foreground-rgb) / var(--opacity-60)); 1285 --font-h1--color: var(--color-foreground-heading); 1286 --font-h2--color: var(--color-foreground-heading); 1287 --font-h3--color: var(--color-foreground-heading); 1288 --font-h4--color: var(--color-foreground-heading); 1289 --font-h5--color: var(--color-foreground-heading); 1290 --font-h6--color: var(--color-foreground-heading); 1291 1292 /* Shadows */ 1293 1294 --shadow-drawer: 0px 4px 20px rgb(var(--color-shadow-rgb) / var(--opacity-15)); 1295 1296 1297 --shadow-blur: 20px; 1298 --shadow-popover: 0px 4px 20px rgb(var(--color-shadow-rgb) / var(--opacity-15)); 1299 1300 } 1301 1302 1303 1304 1305 .color-scheme-6 { 1306 --color-background: rgb(0 0 0 / 0.0); 1307 /* RGB values only to apply different opacities - Relative color values are not supported in iOS < 16.4 */ 1308 --color-background-rgb: 0 0 0; 1309 --opacity-5-15: 0.15; 1310 --opacity-10-25: 0.25; 1311 --opacity-35-55: 0.55; 1312 --opacity-40-60: 0.6; 1313 --opacity-30-60: 0.6; 1314 --color-foreground: rgb(255 255 255 / 1.0); 1315 --color-foreground-rgb: 255 255 255; 1316 --color-foreground-heading: rgb(255 255 255 / 1.0); 1317 --color-foreground-heading-rgb: 255 255 255; 1318 --color-primary: rgb(255 255 255 / 1.0); 1319 --color-primary-rgb: 255 255 255; 1320 --color-primary-hover: rgb(255 255 255 / 0.69); 1321 --color-primary-hover-rgb: 255 255 255; 1322 --color-border: rgb(230 230 230 / 1.0); 1323 --color-border-rgb: 230 230 230; 1324 --color-shadow: rgb(0 0 0 / 1.0); 1325 --color-shadow-rgb: 0 0 0; 1326 --color-primary-button-text: rgb(0 0 0 / 1.0); 1327 --color-primary-button-background: rgb(255 255 255 / 1.0); 1328 --color-primary-button-border: rgb(255 255 255 / 1.0); 1329 --color-primary-button-hover-text: rgb(255 255 255 / 1.0); 1330 --color-primary-button-hover-background: rgb(0 0 0 / 1.0); 1331 --color-primary-button-hover-border: rgb(0 0 0 / 1.0); 1332 --color-secondary-button-text: rgb(255 255 255 / 1.0);
1332 1333 --color-secondary-button-background: rgb(0 0 0 / 0.0); 1334 --color-secondary-button-border: rgb(255 255 255 / 1.0); 1335 --color-secondary-button-hover-text: rgb(255 255 255 / 1.0); 1336 --color-secondary-button-hover-background: rgb(255 255 255 / 0.08); 1337 --color-secondary-button-hover-border: rgb(255 255 255 / 1.0); 1338 --color-input-background: rgb(255 255 255 / 1.0); 1339 --color-input-text: rgb(0 0 0 / 0.53); 1340 --color-input-text-rgb: 0 0 0; 1341 --color-input-border: rgb(0 0 0 / 0.13); 1342 --color-input-hover-background: rgb(250 250 250 / 1.0); 1343 --color-variant-background: rgb(255 255 255 / 1.0); 1344 --color-variant-border: rgb(230 230 230 / 1.0); 1345 --color-variant-text: rgb(0 0 0 / 1.0); 1346 --color-variant-text-rgb: 0 0 0; 1347 --color-variant-hover-background: rgb(245 245 245 / 1.0); 1348 --color-variant-hover-text: rgb(0 0 0 / 1.0); 1349 --color-variant-hover-border: rgb(230 230 230 / 1.0); 1350 --color-selected-variant-background: rgb(0 0 0 / 1.0); 1351 --color-selected-variant-border: rgb(0 0 0 / 1.0); 1352 --color-selected-variant-text: rgb(255 255 255 / 1.0); 1353 --color-selected-variant-hover-background: rgb(26 26 26 / 1.0); 1354 --color-selected-variant-hover-text: rgb(255 255 255 / 1.0); 1355 --color-selected-variant-hover-border: rgb(26 26 26 / 1.0); 1356 1357 --input-disabled-background-color: rgb(var(--color-foreground-rgb) / var(--opacity-10)); 1358 --input-disabled-border-color: rgb(var(--color-foreground-rgb) / var(--opacity-5-15)); 1359 --input-disabled-text-color: rgb(var(--color-foreground-rgb) / var(--opacity-50)); 1360 --color-foreground-muted: rgb(var(--color-foreground-rgb) / var(--opacity-60)); 1361 --font-h1--color: var(--color-foreground-heading); 1362 --font-h2--color: var(--color-foreground-heading); 1363 --font-h3--color: var(--color-foreground-heading); 1364 --font-h4--color: var(--color-foreground-heading); 1365 --font-h5--color: var(--color-foreground-heading); 1366 --font-h6--color: var(--color-foreground-heading); 1367 1368 /* Shadows */ 1369 1370 --shadow-drawer: 0px 4px 20px rgb(var(--color-shadow-rgb) / var(--opacity-15)); 1371 1372 1373 --shadow-blur: 20px; 1374 --shadow-popover: 0px 4px 20px rgb(var(--color-shadow-rgb) / var(--opacity-15)); 1375 1376 } 1377 1378 1379 1380 1381 .color-scheme-58084d4c-a86e-4d0a-855e-a0966e5043f7 { 1382 --color-background: rgb(0 0 0 / 0.0); 1383 /* RGB values only to apply different opacities - Relative color values are not supported in iOS < 16.4 */ 1384 --color-background-rgb: 0 0 0; 1385 --opacity-5-15: 0.15; 1386 --opacity-10-25: 0.25; 1387 --opacity-35-55: 0.55; 1388 --opacity-40-60: 0.6; 1389 --opacity-30-60: 0.6; 1390 --color-foreground: rgb(0 0 0 / 1.0); 1391 --color-foreground-rgb: 0 0 0; 1392 --color-foreground-heading: rgb(0 0 0 / 1.0); 1393 --color-foreground-heading-rgb: 0 0 0; 1394 --color-primary: rgb(0 0 0 / 1.0); 1395 --color-primary-rgb: 0 0 0; 1396 --color-primary-hover: rgb(0 0 0 / 0.53); 1397 --color-primary-hover-rgb: 0 0 0; 1398 --color-border: rgb(230 230 230 / 1.0); 1399 --color-border-rgb: 230 230 230; 1400 --color-shadow: rgb(0 0 0 / 1.0); 1401 --color-shadow-rgb: 0 0 0; 1402 --color-primary-button-text: rgb(255 255 255 / 1.0); 1403 --color-primary-button-background: rgb(0 0 0 / 1.0); 1404 --color-primary-button-border: rgb(0 0 0 / 1.0); 1405 --color-primary-button-hover-text: rgb(255 255 255 / 1.0); 1406 --color-primary-button-hover-background: rgb(51 51 51 / 1.0); 1407 --color-primary-button-hover-border: rgb(51 51 51 / 1.0); 1408 --color-secondary-button-text: rgb(0 0 0 / 1.0); 1409 --color-secondary-button-background: rgb(0 0 0 / 0.0); 1410 --color-secondary-button-border: rgb(0 0 0 / 1.0); 1411 --color-secondary-button-hover-text: rgb(51 51 51 / 1.0); 1412 --color-secondary-button-hover-background: rgb(250 250 250 / 1.0); 1413 --color-secondary-button-hover-border: rgb(51 51 51 / 1.0); 1414 --color-input-background: rgb(255 255 255 / 1.0); 1415 --color-input-text: rgb(0 0 0 / 0.53); 1416 --color-input-text-rgb: 0 0 0; 1417 --color-input-border: rgb(0 0 0 / 0.13); 1418 --color-input-hover-background: rgb(250 250 250 / 1.0); 1419 --color-variant-background: rgb(255 255 255 / 1.0); 1420 --color-variant-border: rgb(230 230 230 / 1.0); 1421 --color-variant-text: rgb(0 0 0 / 1.0); 1422 --color-variant-text-rgb: 0 0 0; 1423 --color-variant-hover-background: rgb(245 245 245 / 1.0); 1424 --color-variant-hover-text: rgb(0 0 0 / 1.0); 1425 --color-variant-hover-border: rgb(230 230 230 / 1.0); 1426 --color-selected-variant-background: rgb(0 0 0 / 1.0); 1427 --color-selected-variant-border: rgb(0 0 0 / 1.0); 1428 --color-selected-variant-text: rgb(255 255 255 / 1.0); 1429 --color-selected-variant-hover-background: rgb(26 26 26 / 1.0); 1430 --color-selected-variant-hover-text: rgb(255 255 255 / 1.0); 1431 --color-selected-variant-hover-border: rgb(26 26 26 / 1.0); 1432 1433 --input-disabled-background-color: rgb(var(--color-foreground-rgb) / var(--opacity-10)); 1434 --input-disabled-border-color: rgb(var(--color-foreground-rgb) / var(--opacity-5-15)); 1435 --input-disabled-text-color: rgb(var(--color-foreground-rgb) / var(--opacity-50)); 1436 --color-foreground-muted: rgb(var(--color-foreground-rgb) / var(--opacity-60)); 1437 --font-h1--color: var(--color-foreground-heading); 1438 --font-h2--color: var(--color-foreground-heading); 1439 --font-h3--color: var(--color-foreground-heading); 1440 --font-h4--color: var(--color-foreground-heading); 1441 --font-h5--color: var(--color-foreground-heading); 1442 --font-h6--color: var(--color-foreground-heading); 1443 1444 /* Shadows */ 1445 1446 --shadow-drawer: 0px 4px 20px rgb(var(--color-shadow-rgb) / var(--opacity-15)); 1447 1448 1449 --shadow-blur: 20px; 1450 --shadow-popover: 0px 4px 20px rgb(var(--color-shadow-rgb) / var(--opacity-15)); 1451 1452 } 1453 1454 1455 1456 1457 .color-scheme-515910f4-a3e9-48d3-8079-ac481ef96b10 { 1458 --color-background: rgb(254 255 250 / 1.0); 1459 /* RGB values only to apply different opacities - Relative color values are not supported in iOS < 16.4 */ 1460 --color-background-rgb: 254 255 250; 1461 --opacity-5-15: 0.05; 1462 --opacity-10-25: 0.1; 1463 --opacity-35-55: 0.35; 1464 --opacity-40-60: 0.4; 1465 --opacity-30-60: 0.3; 1466 --color-foreground: rgb(0 0 0 / 0.81); 1467 --color-foreground-rgb: 0 0 0; 1468 --color-foreground-heading: rgb(0 0 0 / 1.0); 1469 --color-foreground-heading-rgb: 0 0 0; 1470 --color-primary: rgb(0 0 0 / 0.81); 1471 --color-primary-rgb: 0 0 0; 1472 --color-primary-hover: rgb(0 0 0 / 1.0); 1473 --color-primary-hover-rgb: 0 0 0; 1474 --color-border: rgb(0 0 0 / 0.06); 1475 --color-border-rgb: 0 0 0; 1476 --color-shadow: rgb(0 0 0 / 1.0); 1477 --color-shadow-rgb: 0 0 0; 1478 --color-primary-button-text: rgb(255 255 255 / 1.0); 1479 --color-primary-button-background: rgb(0 0 0 / 1.0); 1480 --color-primary-button-border: rgb(0 0 0 / 1.0); 1481 --color-primary-button-hover-text: rgb(255 255 255 / 1.0); 1482 --color-primary-button-hover-background: rgb(51 51 51 / 1.0); 1483 --color-primary-button-hover-border: rgb(0 0 0 / 1.0); 1484 --color-secondary-button-text: rgb(0 0 0 / 1.0); 1485 --color-secondary-button-background: rgb(0 0 0 / 0.0); 1486 --color-secondary-button-border: rgb(0 0 0 / 1.0); 1487 --color-secondary-button-hover-text: rgb(51 51 51 / 1.0); 1488 --color-secondary-button-hover-background: rgb(250 250 250 / 1.0); 1489 --color-secondary-button-hover-border: rgb(51 51 51 / 1.0); 1490 --color-input-background: rgb(255 255 255 / 0.78); 1491 --color-input-text: rgb(51 51 51 / 1.0); 1492 --color-input-text-rgb: 51 51 51; 1493 --color-input-border: rgb(223 223 223 / 1.0); 1494 --color-input-hover-background: rgb(0 0 0 / 0.01); 1495 --color-variant-background: rgb(255 255 255 / 1.0); 1496 --color-variant-border: rgb(0 0 0 / 0.13); 1497 --color-variant-text: rgb(0 0 0 / 1.0); 1498 --color-variant-text-rgb: 0 0 0; 1499 --color-variant-hover-background: rgb(245 245 245 / 1.0); 1500 --color-variant-hover-text: rgb(0 0 0 / 1.0); 1501 --color-variant-hover-border: rgb(230 230 230 / 1.0); 1502 --color-selected-variant-background: rgb(0 0 0 / 1.0); 1503 --color-selected-variant-border: rgb(0 0 0 / 1.0); 1504 --color-selected-variant-text: rgb(255 255 255 / 1.0); 1505 --color-selected-variant-hover-background: rgb(26 26 26 / 1.0); 1506 --color-selected-variant-hover-text: rgb(255 255 255 / 1.0); 1507 --color-selected-variant-hover-border: rgb(26 26 26 / 1.0); 1508 1509 --input-disabled-background-color: rgb(var(--color-foreground-rgb) / var(--opacity-10)); 1510 --input-disabled-border-color: rgb(var(--color-foreground-rgb) / var(--opacity-5-15)); 1511 --input-disabled-text-color: rgb(var(--color-foreground-rgb) / var(--opacity-50)); 1512 --color-foreground-muted: rgb(var(--color-foreground-rgb) / var(--opacity-60)); 1513 --font-h1--color: var(--color-foreground-heading); 1514 --font-h2--color: var(--color-foreground-heading); 1515 --font-h3--color: var(--color-foreground-heading); 1516 --font-h4--color: var(--color-foreground-heading); 1517 --font-h5--color: var(--color-foreground-heading); 1518 --font-h6--color: var(--color-foreground-heading); 1519 1520 /* Shadows */ 1521 1522 --shadow-drawer: 0px 4px 20px rgb(var(--color-shadow-rgb) / var(--opacity-15)); 1523 1524 1525 --shadow-blur: 20px; 1526 --shadow-popover: 0px 4px 20px rgb(var(--color-shadow-rgb) / var(--opacity-15)); 1527 1528 } 1529 1530 1531 1532 1533 .color-scheme-d70ac8c4-eb3e-43d2-9c84-148cdbac2972 { 1534 --color-background: rgb(247 246 234 / 1.0); 1535 /* RGB values only to apply different opacities - Relative color values are not supported in iOS < 16.4 */ 1536 --color-background-rgb: 247 246 234; 1537 --opacity-5-15: 0.05; 1538 --opacity-10-25: 0.1; 1539 --opacity-35-55: 0.35; 1540 --opacity-40-60: 0.4; 1541 --opacity-30-60: 0.3; 1542 --color-foreground: rgb(23 22 9 / 1.0); 1543 --color-foreground-rgb: 23 22 9; 1544 --color-foreground-heading: rgb(23 22 9 / 1.0); 1545 --color-foreground-heading-rgb: 23 22 9; 1546 --color-primary: rgb(23 22 9 / 1.0); 1547 --color-primary-rgb: 23 22 9; 1548 --color-primary-hover: rgb(23 22 9 / 1.0); 1549 --color-primary-hover-rgb: 23 22 9; 1550 --color-border: rgb(0 0 0 / 0.06); 1551 --color-border-rgb: 0 0 0; 1552 --color-shadow: rgb(0 0 0 / 1.0); 1553 --color-shadow-rgb: 0 0 0; 1554 --color-primary-button-text: rgb(255 255 255 / 1.0); 1555 --color-primary-button-background: rgb(0 0 0 / 1.0); 1556 --color-primary-button-border: rgb(0 0 0 / 1.0); 1557 --color-primary-button-hover-text: rgb(255 255 255 / 1.0); 1558 --color-primary-button-hover-background: rgb(51 51 51 / 1.0); 1559 --color-primary-button-hover-border: rgb(0 0 0 / 1.0); 1560 --color-secondary-button-text: rgb(0 0 0 / 1.0); 1561 --color-secondary-button-background: rgb(0 0 0 / 0.0); 1562 --color-secondary-button-border: rgb(0 0 0 / 1.0); 1563 --color-secondary-button-hover-text: rgb(51 51 51 / 1.0); 1564 --color-secondary-button-hover-background: rgb(250 250 250 / 1.0); 1565 --color-secondary-button-hover-border: rgb(51 51 51 / 1.0); 1566 --color-input-background: rgb(255 255 255 / 0.78); 1567 --color-input-text: rgb(51 51 51 / 1.0); 1568 --color-input-text-rgb: 51 51 51; 1569 --color-input-border: rgb(223 223 223 / 1.0); 1570 --color-input-hover-background: rgb(0 0 0 / 0.01); 1571 --color-variant-background: rgb(255 255 255 / 1.0); 1572 --color-variant-border: rgb(0 0 0 / 0.13); 1573 --color-variant-text: rgb(0 0 0 / 1.0); 1574 --color-variant-text-rgb: 0 0 0; 1575 --color-variant-hover-background: rgb(245 245 245 / 1.0); 1576 --color-variant-hover-text: rgb(0 0 0 / 1.0); 1577 --color-variant-hover-border: rgb(230 230 230 / 1.0); 1578 --color-selected-variant-background: rgb(0 0 0 / 1.0); 1579 --color-selected-variant-border: rgb(0 0 0 / 1.0); 1580 --color-selected-variant-text: rgb(255 255 255 / 1.0); 1581 --color-selected-variant-hover-background: rgb(26 26 26 / 1.0); 1582 --color-selected-variant-hover-text: rgb(255 255 255 / 1.0); 1583 --color-selected-variant-hover-border: rgb(26 26 26 / 1.0); 1584 1585 --input-disabled-background-color: rgb(var(--color-foreground-rgb) / var(--opacity-10)); 1586 --input-disabled-border-color: rgb(var(--color-foreground-rgb) / var(--opacity-5-15)); 1587 --input-disabled-text-color: rgb(var(--color-foreground-rgb) / var(--opacity-50)); 1588 --color-foreground-muted: rgb(var(--color-foreground-rgb) / var(--opacity-60)); 1589 --font-h1--color: var(--color-foreground-heading); 1590 --font-h2--color: var(--color-foreground-heading); 1591 --font-h3--color: var(--color-foreground-heading); 1592 --font-h4--color: var(--color-foreground-heading); 1593 --font-h5--color: var(--color-foreground-heading); 1594 --font-h6--color: var(--color-foreground-heading); 1595 1596 /* Shadows */ 1597 1598 --shadow-drawer: 0px 4px 20px rgb(var(--color-shadow-rgb) / var(--opacity-15)); 1599 1600 1601 --shadow-blur: 20px; 1602 --shadow-popover: 0px 4px 20px rgb(var(--color-shadow-rgb) / var(--opacity-15)); 1603 1604 } 1605 1606 1607 body, .color-scheme-1, .color-scheme-2, .color-scheme-3, .color-scheme-4, .color-scheme-5, .color-scheme-6, .color-scheme-58084d4c-a86e-4d0a-855e-a0966e5043f7, .color-scheme-515910f4-a3e9-48d3-8079-ac481ef96b10, .color-scheme-d70ac8c4-eb3e-43d2-9c84-148cdbac2972 { 1608 color: var(--color-foreground); 1609 background-color: var(--color-background); 1610 } 1611</style> 1612<link rel="preload" as="font" href="//www.bostonproper.com/cdn/shop/t/1521/assets/MessinaSans-Regular.woff2?v=143903206038847021881790713714" type="font/woff2" crossorigin> 1613<link rel="preload" as="font" href="//www.bostonproper.com/cdn/shop/t/1521/assets/MessinaSans-Light.woff2?v=52620737138227856671790713714" type="font/woff2" crossorigin> 1614<link rel="preload" as="font" href="//www.bostonproper.com/cdn/shop/t/1521/assets/Signifier-Light.woff2?v=141591112723842981461790713714" type="font/woff2" crossorigin> 1615 1616<style data-shopify> 1617 /* Hide the browser's native clear "Ã" on type="search" inputs. BP renders 1618 its own custom clear icon (search drawer + /search page), so the native
1619 one showed as a duplicate Ã-on-à when the field had focus/hover. */ 1620 input[type="search"]::-webkit-search-cancel-button, 1621 input[type="search"]::-webkit-search-decoration { 1622 -webkit-appearance: none; 1623 appearance: none; 1624 display: none; 1625 } 1626 1627 /* Third-party tracking pixels (Dstillery, etc.) get injected as 1Ã1 1628 <img> elements appended to <body>. base.css's global 1629 `img { width: 100%; height: auto }` overrides their width/height 1630 attributes and stretches each one into a full-viewport-width square, 1631 dumping a huge blank block below the footer. Pin tracking pixels back 1632 to their intrinsic size and pull them out of flow â the beacon still 1633 fires (display/position don't block the request). 1634 1635 EXCLUSIONS: 1636 :not([class*="stylitics"]) â the Stylitics "How to Wear It" 1637 widget renders each tile as <img width="1" height="1" 1638 class="stylitics-ui-image â¦"> as a lazy-load placeholder and 1639 then resizes via the `--max-size` CSS variable. Without this 1640 exclusion the tiles stay pinned at 1Ã1. 1641 :not([data-testid]) â widget code typically carries a testid; 1642 tracking pixels don't. Defence-in-depth for other vendors 1643 that follow Stylitics's pattern. 1644 :not([loading]) â same. Lazy-loaded content images carry 1645 loading="lazy" / "eager"; tracking beacons don't. */ 1646 img[width="1"][height="1"]:not([class*="stylitics"]):not([data-testid]):not([loading]), 1647 img[width="0"][height="0"]:not([class*="stylitics"]):not([data-testid]):not([loading]) { 1648 width: 1px !important; 1649 height: 1px !important; 1650 position: absolute !important; 1651 pointer-events: none; 1652 } 1653 1654 @font-face { 1655 font-family: 'Messina Sans VF'; 1656 src: url('//www.bostonproper.com/cdn/shop/t/1521/assets/MessinaSans-Light.woff2?v=52620737138227856671790713714') format('woff2'); 1657 font-weight: 300; 1658 font-style: normal; 1659 font-display: swap; 1660 } 1661 @font-face { 1662 font-family: 'Messina Sans VF'; 1663 src: url('//www.bostonproper.com/cdn/shop/t/1521/assets/MessinaSans-Regular.woff2?v=143903206038847021881790713714') format('woff2'); 1664 font-weight: 400; 1665 font-style: normal; 1666 font-display: swap; 1667 } 1668 @font-face { 1669 font-family: 'Messina Sans VF'; 1670 src: url('//www.bostonproper.com/cdn/shop/t/1521/assets/MessinaSans-SemiBold.woff2?v=136830414853171670151790713714') format('woff2'); 1671 font-weight: 500 600; 1672 font-style: normal; 1673 font-display: swap; 1674 } 1675 @font-face { 1676 font-family: 'Messina Sans VF'; 1677 src: url('//www.bostonproper.com/cdn/shop/t/1521/assets/MessinaSans-Bold.woff2?v=151736514687132566241790713714') format('woff2'); 1678 font-weight: 700; 1679 font-style: normal; 1680 font-display: swap; 1681 } 1682 1683 @font-face { 1684 font-family: 'Signifier'; 1685 src: url('//www.bostonproper.com/cdn/shop/t/1521/assets/Signifier-Light.woff2?v=141591112723842981461790713714') format('woff2'); 1686 font-weight: 300; 1687 font-style: normal; 1688 font-display: swap; 1689 } 1690 @font-face { 1691 font-family: 'Signifier'; 1692 src: url('//www.bostonproper.com/cdn/shop/t/1521/assets/Signifier-Regular.woff2?v=76067088345373958441790713714') format('woff2'); 1693 font-weight: 400; 1694 font-style: normal; 1695 font-display: swap; 1696 } 1697 1698 /* ============================================================ 1699 Boston Proper design tokens. 1700 1701 Single source of truth for spacing, colors, and font families 1702 used by BP custom sections (bp-hero-slider, bp-shop-by-category, 1703 bp-product-carousel, ...). New sections SHOULD read from these 1704 tokens instead of hardcoding pixel values or hex colors. See 1705 CLAUDE.md for usage conventions. 1706 ============================================================ */ 1707 :root { 1708 /* Typography â Messina Sans VF is the site-wide default body 1709 family; Signifier is opt-in for display/heading copy. The five 1710 Horizon font-family vars are overridden here so every section 1711 inherits Messina Sans without per-section rules. */ 1712 --font-body--family: 'Messina Sans VF', system-ui, -apple-system, 'Segoe UI', sans-serif; 1713 --font-paragraph--family: 'Messina Sans VF', system-ui, -apple-system, 'Segoe UI', sans-serif; 1714 --font-subheading--family: 'Messina Sans VF', system-ui, -apple-system, 'Segoe UI', sans-serif; 1715 --font-heading--family: 'Messina Sans VF', system-ui, -apple-system, 'Segoe UI', sans-serif; 1716 --font-accent--family: 'Messina Sans VF', system-ui, -apple-system, 'Segoe UI', sans-serif; 1717 1718 --bp-font-body: 'Messina Sans VF', system-ui, -apple-system, 'Segoe UI', sans-serif; 1719 --bp-font-display: 'Signifier', 'Times New Roman', serif; 1720 1721 /* Spacing â values below are the MOBILE defaults; the desktop 1722 overrides live in the @media block at the bottom of :root. Section 1723 gutter is the horizontal distance from viewport edge to content; 1724 section gap is the gap between heading/nav rows or between cards 1725 in a track; card gap is the gap between elements inside a single 1726 card. */ 1727 --bp-section-pad-x: 16px; 1728 --bp-section-pad-y: 32px; 1729 --bp-section-gap: 16px; 1730 --bp-card-gap: 12px; 1731 1732 /* Colors */ 1733 --bp-color-text: #171609; 1734 --bp-color-bg-cream: #F7F6EA; 1735 --bp-color-bg-page: #FEFFFA; 1736 --bp-color-promo: #B5341B; 1737 /* #CA3D3D was only 4.55:1 on cream (razor-thin pass). Nudged to 1738 #C23636 (~5.0:1 on cream) for AA margin â imperceptibly darker red. */ 1739 --bp-color-sales-red: #C23636; 1740 --bp-color-cta-text: #FDFFFA; 1741 /* Sand CTA palette (Figma). Filled sand button + its inverse. Paired 1742 so `--sand` and `--sand-invert` just swap fg/bg. */ 1743 --bp-color-cta-sand: #E8DFD0; 1744 --bp-color-cta-sand-text: #2B241A; 1745 /* Body-copy grey darkened from #74736B (4.39:1 on cream / 4.14:1 on 1746 neutral â failed WCAG AA 4.5:1) to #666559, which clears 4.5:1 on 1747 white, cream and neutral. Disabled/OOS greys stay #74736B (exempt). */ 1748 --bp-color-body-copy: #666559; 1749 /* Muted/secondary ink for eyebrows, sub-labels, counts. Replaces the 1750 scattered rgba(23,22,9,0.45â0.55) usages that failed WCAG AA 4.5:1 1751 (~3.0â4.05:1). 0.65 â 4.9:1 on white/cream. */ 1752 --bp-color-text-muted: rgba(23, 22, 9, 0.65); 1753 --bp-color-neutral: #F0EFEA; 1754 1755 /* Type scale â single source for the sizes already used across the 1756 BP sections. Mobile values live here; desktop overrides are in the 1757 @media block below. Consumed by the .bp-fs-* utility classes and 1758 by the shared blocks (_bp-heading, _bp-body). */ 1759 --bp-fs-h1: 36px; /* hero / page lead (mobile of clamp(36,6vw,50)) */ 1760 --bp-fs-h2: 32px; /* section title (matches bp-fc/bp-itb/bp-grid mobile) */ 1761 --bp-fs-h3: 28px; 1762 --bp-fs-h4: 24px; 1763 --bp-fs-h5: 20px; 1764 --bp-fs-h6: 16px; 1765 1766 --bp-fs-body-lg: 17px; /* eyebrows + lead descriptions */ 1767 --bp-fs-body-md: 14px; /* default body copy */ 1768 --bp-fs-body-sm: 13px; /* fine print / meta */ 1769 --bp-fs-body-xs: 12px; /* micro labels */ 1770 } 1771 1772 /* Desktop spacing + type-scale overrides â single point of truth for 1773 the breakpoint where the BP layout shifts from mobile-compact to 1774 desktop-roomy. */ 1775 @media screen and (min-width: 1024px) { 1776 :root { 1777 --bp-section-pad-x: 40px; 1778 --bp-section-pad-y: 40px; 1779 --bp-section-gap: 20px; 1780 1781 --bp-fs-h1: 50px; /* hero */ 1782 --bp-fs-h2: 40px; /* section title (bp-fc h1 variant / bp-itb desktop) */ 1783 --bp-fs-h3: 32px; /* sub-section */ 1784 --bp-fs-h4: 24px; 1785 --bp-fs-h5: 20px; 1786 --bp-fs-h6: 18px; 1787 } 1788 } 1789 1790 /* Type-scale utility classes â apply just the size + tracking + 1791 leading. Family/colour come from the wrapping element so the same 1792 scale step works for display (Signifier) and body (Messina) copy. */ 1793 .bp-fs-h1 { font-size: var(--bp-fs-h1); letter-spacing: -0.02em; line-height: 110%; } 1794 .bp-fs-h2 { font-size: var(--bp-fs-h2); letter-spacing: -0.02em; line-height: 115%; } 1795 .bp-fs-h3 { font-size: var(--bp-fs-h3); letter-spacing: -0.02em; line-height: 120%; } 1796 .bp-fs-h4 { font-size: var(--bp-fs-h4); letter-spacing: -0.01em; line-height: 125%; } 1797 .bp-fs-h5 { font-size: var(--bp-fs-h5); line-height: 130%; } 1798 .bp-fs-h6 { font-size: var(--bp-fs-h6); line-height: 135%; } 1799 1800 .bp-fs-body-lg { font-size: var(--bp-fs-body-lg); line-height: 150%; } 1801 .bp-fs-body-md { font-size: var(--bp-fs-body-md); line-height: 145%; } 1802 .bp-fs-body-sm { font-size: var(--bp-fs-body-sm); line-height: 145%; } 1803 .bp-fs-body-xs { font-size: var(--bp-fs-body-xs); line-height: 145%; } 1804 1805 /* Topnav alignment: pin the header rows and announcement bar to the
1806 same 40px gutter as the BP custom sections. 1807 1808 Horizon's section system uses a 3-column grid: 1809 minmax(--page-margin, 1fr) 1810 --full-page-grid-central-column-width (clamped to --page-content-width = 1440px) 1811 minmax(--page-margin, 1fr) 1812 On viewports wider than ~1520px the side tracks grow above 1813 --page-margin to center the 1440px content column. So overriding 1814 --page-margin alone keeps the side tracks at 40px MIN but they 1815 still expand on wide screens. We also override the central column 1816 to "100% - 80px" so the 1fr side tracks collapse to exactly the 1817 40px minimum on every viewport, regardless of width. */ 1818 .header__row, 1819 .announcement-bar { 1820 --page-margin: var(--bp-section-pad-x); 1821 --full-page-grid-central-column-width: calc(100% - var(--bp-section-pad-x) * 2); 1822 } 1823 1824 /* Mobile header â keep the right-side icons (search / wishlist / 1825 bag) inside the viewport on narrow widths (â¤749px). The BP 1826 logo is an inline SVG (bp-logo.svg) rendered with a fixed 1827 --header-logo-image-width-mobile: 198px via inline style on 1828 the wrapper span. Combined with 4 à ~44px bookend cells that 1829 hold the hamburger / search / wishlist / bag, the total width 1830 hits 374px which is iPhone X (375px) viewport width â leaving 1831 no room for the bag icon. Cap the SVG container width with 1832 clamp() so the logo scales down on narrow viewports while 1833 staying close to 198px once there's enough room for it. */ 1834 @media screen and (max-width: 749px) { 1835 .header__row .header-logo { 1836 min-width: 0; 1837 max-width: 100%; 1838 overflow: hidden; 1839 } 1840 .header__row .header-logo__image, 1841 .header__row .header-logo__image--svg { 1842 width: clamp(140px, 42vw, 198px); 1843 height: auto; 1844 max-width: 100%; 1845 } 1846 .header__row .header-logo__image--svg svg { 1847 width: 100%; 1848 height: auto; 1849 max-width: 100%; 1850 display: block; 1851 } 1852 1853 /* The scroll-logo variant (small icon-only mark, "Header 1854 scroll-logo swap") must NOT be clamped up to wordmark width. 1855 It shares the .header-logo__image / .header-logo__image--svg 1856 classes with the original/inverse wordmark above, so the 1857 generic rule clamps it to 140-198px too. 44x44 (to match the 1858 right-side action icon hit area) looked oversized/misaligned 1859 in practice â 30x30 reads closer to the intended mark size 1860 while still being bigger than the original 23x18px inline 1861 value baked into blocks/_header-logo.liquid. object-fit: 1862 contain (images) / SVG's default preserveAspectRatio (inline 1863 fallback mark) keep the artwork itself from distorting. */ 1864 .header__row .header-logo__image-container--scroll .header-logo__image, 1865 .header__row .header-logo__image-container--scroll .header-logo__image--svg { 1866 width: 30px; 1867 height: 30px; 1868 max-width: none; 1869 } 1870 1871 .header__row .header-logo__image-container--scroll .header-logo__image--svg svg { 1872 width: 100%; 1873 height: 100%; 1874 max-width: none; 1875 } 1876 1877 /* Nudge to correct residual vertical misalignment of the scroll 1878 mark that flex-centering alone didn't fully resolve (mobile 1879 compact bar only â the original/inverse wordmark is unaffected 1880 since this is scoped to data-bp-nav='compact'). */ 1881 #header-component[data-bp-nav='compact'] .header__row .header-logo { 1882 padding-bottom: 6px; 1883 } 1884 } 1885 1886 /* Announcement bar slide alignment. 1887 1888 Horizon ships `.text-block--align-center` (and -left / -right) 1889 that set `--text-align: center` on the slide as a CSS variable, 1890 plus an inline `--text-align: center` from the schema, but no 1891 rule in `.text-block` actually consumes it back into the 1892 text-align property â only `.button` does. So the alignment 1893 class + inline var are inert without this consumer. 1894 1895 Apply text-align: var(--text-align) on the slide + paragraph so 1896 the schema's alignment choice takes effect. Defaults to center, 1897 which matches the design and is what the BP announcement bar 1898 always wants regardless of theme settings. */ 1899 .announcement-bar__slide, 1900 .announcement-bar__text { 1901 text-align: var(--text-align, center); 1902 } 1903 1904 /* Announcement bar pause/play control (Atlas parity). Pinned to the 1905 right edge of the bar; inherits the bar's text color. The button is 1906 rendered by sections/header-announcements.liquid and wired to 1907 Horizon's announcement-bar-component pause()/play(). */ 1908 .bp-announcement-playpause { 1909 position: absolute; 1910 right: 10px; 1911 top: 50%; 1912 transform: translateY(-50%); 1913 display: inline-flex; 1914 align-items: center; 1915 justify-content: center; 1916 width: 28px; 1917 height: 28px; 1918 padding: 0; 1919 background: transparent; 1920 border: 0; 1921 color: var(--color-foreground, currentColor); 1922 cursor: pointer; 1923 opacity: 0.75; 1924 transition: opacity 0.15s ease; 1925 } 1926 .bp-announcement-playpause:hover { opacity: 1; } 1927 /* Show exactly ONE glyph, reflecting the bar's actual state: the PAUSE bars 1928 while playing (click to pause), the PLAY triangle while paused (click to 1929 play). Driven off the bar's `[paused]` attribute so it stays correct 1930 through Horizon's re-render of the bar (which reset the JS `hidden` toggle, 1931 leaving both â or the wrong â icon showing). `!important` to beat the 1932 cascade rule that was overriding the play SVG's `hidden` (QA 7/27). */ 1933 .bp-announcement-playpause .bp-announcement-playpause__pause { display: block !important; } 1934 .bp-announcement-playpause .bp-announcement-playpause__play { display: none !important; } 1935 announcement-bar-component[paused] .bp-announcement-playpause__pause { display: none !important; } 1936 announcement-bar-component[paused] .bp-announcement-playpause__play { display: block !important; } 1937 /* The play/pause is pinned at the bar's right edge (right:10px), and 1938 Horizon's next carat also lands at the right edge (slideshow-arrows is 1939 `inset:0` with `justify-content:space-between`), so the two overlap â
1940 on desktop AND mobile (QA 7/27, Adrienne). Pull the arrow track's right 1941 edge in past the button so the next carat sits just left of it â Atlas 1942 order: [â¦slides] [next] [play/pause]. Only the next arrow moves (the prev 1943 stays at left via space-between); the centered slide text is a separate 1944 element and is unaffected. */ 1945 .announcement-bar slideshow-arrows { right: 40px; } 1946 1947 /* a11y (WCAG 2.1.1): keep the desktop mega-menu submenu open while 1948 keyboard focus is inside it. Horizon only reveals it on 1949 [aria-expanded='true'] + :hover (blocks/_header-menu.liquid:422), so 1950 tabbing into the panel could collapse it if the trigger's blur fires 1951 /deactivate before focus lands. Mirrors the :hover rule for focus. */ 1952 .menu-list__submenu:focus-within { 1953 --submenu-content-opacity: 1; 1954 visibility: visible; 1955 } 1956 1957 /* Keyboard-only down caret on top-level mega-menu items (menubar pattern â 1958 Alfred 7/27). Shown when a trigger is focused via the keyboard 1959 (:focus-visible) so keyboard users get a clear "opens a submenu" 1960 affordance; mouse hover/click never shows it. The caret is a CSS triangle 1961 in the link's own colour. bp-mega-hover.js wires the menubar behaviour 1962 (Enter/Space/ArrowDown opens, arrows navigate, Escape/Tab close). */ 1963 .menu-list__list-item.bp-menu-item-li > .menu-list__link[aria-haspopup='true'] { 1964 position: relative; 1965 } 1966 .menu-list__list-item.bp-menu-item-li > .menu-list__link[aria-haspopup='true']:focus-visible::after { 1967 content: ""; 1968 position: absolute; 1969 left: 50%; 1970 /* Centered just BELOW the label rather than inline after it â the inline 1971 caret overlapped the (fixed-min-width, centered) nav text (QA 7/27). */ 1972 top: calc(50% + 0.85em); 1973 transform: translate(-50%, -50%); 1974 width: 0; 1975 height: 0; 1976 border-inline: 4px solid transparent; 1977 border-top: 5px solid currentColor; 1978 pointer-events: none; 1979 } 1980 1981 /* Off-white page background (#FEFFFA, Figma spec) applied GLOBALLY â 1982 every template, not just index/collection (PDPs etc. were falling 1983 back to Horizon's default white). Sections that paint their own bg 1984 (cream bands, color schemes) still win over the body. The nav header 1985 rows are overridden to match. The announcement bar is intentionally 1986 NOT included â it's driven by its own merchant color scheme 1987 (header-announcements.liquid), and forcing off-white would erase a 1988 colored/dark promo bar. */ 1989 body { 1990 background-color: var(--bp-color-bg-page); 1991 } 1992 .header, 1993 .header__row, 1994 .header-wrapper { 1995 background-color: var(--bp-color-bg-page); 1996 } 1997 1998 /* QA 7/16 â the Figma page color is off-white #FEFFFA, not pure white. 1999 Horizon's DEFAULT color scheme (scheme-1) paints #ffffff, and that's 2000 what the PDP main sections, the header, and the mega-menu read through 2001 --color-background â so those surfaces stayed white despite the body 2002 override above. Remap just the white scheme to the BP page-bg token so 2003 every scheme-1 surface (all pages that use the default scheme) goes 2004 off-white. bp-fonts renders AFTER color-schemes in theme.liquid, so this 2005 wins on equal specificity without !important. The other schemes 2006 (grey #f5f5f5, cream #eef1ea, blue, dark) are intentional â left alone. */ 2007 .color-scheme-1 { 2008 --color-background: var(--bp-color-bg-page); 2009 --color-background-rgb: 254, 255, 250; 2010 } 2011 2012 /* ============================================================ 2013 BP Product Drawer â re-skin Horizon's quick-add modal as a 2014 right-side slide-in drawer. 2015 2016 MOVED to assets/bp-fonts-deferred.css (loaded non-render- 2017 blocking from theme.liquid). The drawer is a native <dialog 2018 class="quick-add-modal"> that is UA display:none until a card 2019 click opens it â an interaction-revealed surface â so its 2020 open/appearance rules don't need to block first paint. Horizon's 2021 own base .quick-add-modal styles stay render-blocking and keep 2022 the dialog functional. See CLAUDE.md "Topnav 40px gutter" note 2023 for the specificity-bumping pattern the moved rules use. 2024 ============================================================ */ 2025 2026 /* ============================================================
2027 BP product card â shared by bp-product-carousel and bp-collection 2028 (PLP). Lives here (not in either section) because both surfaces 2029 need the styles, and CSS in a section stylesheet block only loads 2030 when that section is rendered. 2031 ============================================================ */ 2032 .bp-card-host { display: contents; } 2033 2034 .bp-card { 2035 position: relative; 2036 display: flex; 2037 flex-direction: column; 2038 gap: var(--bp-card-gap); 2039 color: var(--bp-color-text); 2040 } 2041 2042 .bp-card__media { 2043 position: relative; 2044 display: block; 2045 aspect-ratio: 3 / 4; 2046 overflow: hidden; 2047 background: var(--bp-color-bg-cream); 2048 color: inherit; 2049 } 2050 .bp-card__media-link { 2051 position: absolute; 2052 inset: 0; 2053 display: block; 2054 text-decoration: none; 2055 color: inherit; 2056 } 2057 .bp-card__quick-add { 2058 position: absolute; 2059 width: 1px; 2060 height: 1px; 2061 overflow: hidden; 2062 clip: rect(0 0 0 0); 2063 pointer-events: none; 2064 } 2065 .bp-card__image { 2066 position: absolute; 2067 inset: 0; 2068 width: 100%; 2069 height: 100%; 2070 object-fit: cover; 2071 display: block; 2072 transition: opacity 0.4s ease; 2073 } 2074 .bp-card__image--secondary { opacity: 0; } 2075 /* Hover-swap default: when the merchant turns secondary-hover off 2076 via the §9.4 setting, the secondary image stays hidden. 2077 The primary only fades out when a secondary image is actually 2078 present (`:has(.bp-card__image--secondary)`). Single-image 2079 products (many Collective / Hanky Panky items) render no secondary 2080 element, so without this guard the primary faded to opacity 0 on 2081 hover and the card went BLANK â hover must keep showing the first 2082 image when there's nothing to swap to (QA 7/31). */ 2083 @media (hover: hover) { 2084 .bp-card[data-bp-card-secondary-hover='true'] .bp-card__media:has(.bp-card__image--secondary):hover .bp-card__image--primary { opacity: 0; } 2085 .bp-card[data-bp-card-secondary-hover='true'] .bp-card__media:hover .bp-card__image--secondary { opacity: 1; } 2086 } 2087 2088 .bp-card__badges { 2089 position: absolute; 2090 top: 12px; 2091 left: 12px; 2092 z-index: 2; 2093 display: flex; 2094 flex-direction: column; 2095 align-items: flex-start; 2096 gap: 6px; 2097 } 2098 2099 .bp-card__badge { 2100 background: var(--bp-color-bg-cream); 2101 color: var(--bp-color-text); 2102 padding: 4px 10px; 2103 font-family: var(--bp-font-display); 2104 font-size: 14px; 2105 font-style: normal; 2106 font-weight: 300; 2107 line-height: 120%; 2108 letter-spacing: -0.28px; 2109 } 2110 2111 .bp-card__quick { 2112 position: absolute; 2113 bottom: 12px; 2114 right: 12px; 2115 z-index: 2; 2116 width: 36px; 2117 height: 36px; 2118 border-radius: 0; 2119 border: 1px solid rgba(23, 22, 9, 0.15); 2120 background: var(--bp-color-bg-page); 2121 color: var(--bp-color-text); 2122 display: inline-flex; 2123 align-items: center; 2124 justify-content: center; 2125 cursor: pointer; 2126 opacity: 0; 2127 transform: translateY(4px); 2128 transition: opacity 0.2s ease, transform 0.2s ease, background-color 0.2s ease; 2129 } 2130 .bp-card__quick:hover { background: var(--bp-color-text); color: #fff; } 2131 2132 /* Quick-drawer gallery nav (.bp-qd-gallery-nav) is injected by JS onto 2133 <bp-pdp-gallery>. Its positioning/flip styles live inside a mobile 2134 @media block scoped to .bp-qd__media-slot, so on desktop â or when the 2135 gallery is the main PDP gallery (not in the drawer) â the buttons fall 2136 to the bottom unstyled: two disabled, both-right-pointing arrows. Hide 2137 the nav globally by default; bp-quick-drawer's mobile rules re-show it 2138 inside the drawer media slot. */ 2139 .bp-qd-gallery-nav { display: none; } 2140 2141 /* Quick-view button visibility â driven by §9.4 setting keys. 2142 "Always" applies on any device (logically: always = always); 2143 "Hover" only fires on hover-capable devices. Mobile show/hide 2144 is keyed on (hover: none) so it doesn't fight the desktop rules 2145 on touch laptops that report (hover: hover). */ 2146 2147 /* "Always visible" â opt-out of the hover-reveal animation. */ 2148 .bp-card[data-bp-card-qv-vis-d='always'] .bp-card__quick { 2149 opacity: 1; 2150 transform: none; 2151 } 2152 2153 /* Hover-capable devices: hover-to-reveal (default for desktop). */ 2154 @media (hover: hover) { 2155 .bp-card[data-bp-card-qv-vis-d='hover'] .bp-card__media:hover .bp-card__quick { 2156 opacity: 1; 2157 transform: translateY(0); 2158 } 2159 } 2160 2161 /* Touch-only: explicit mobile show/hide. */ 2162 @media (hover: none) { 2163 .bp-card[data-bp-card-qv-vis-m='shown'] .bp-card__quick { 2164 opacity: 1; 2165 transform: none; 2166 } 2167 .bp-card[data-bp-card-qv-vis-m='hidden'] .bp-card__quick { 2168 display: none; 2169 } 2170 } 2171 2172 /* Whole-product trigger style â make the card surface look clickable 2173 and remove the bag-icon affordance. The bag itself isn't rendered
2174 in this mode (see bp-product-card.liquid), so this just signals 2175 interactivity. */ 2176 .bp-card[data-bp-card-qb-style='whole_product'] { 2177 cursor: pointer; 2178 } 2179 2180 /* Swatch host wrapper â passes through for layout; the click target 2181 is the inner `.swatch` element (Horizon's). */ 2182 .bp-card__swatch-host { display: inline-flex; cursor: pointer; } 2183 2184 .bp-card__body { 2185 display: flex; 2186 flex-direction: column; 2187 gap: 6px; 2188 } 2189 .bp-card__collection { 2190 margin: 0; 2191 font-family: var(--bp-font-body); 2192 font-size: 11px; 2193 font-weight: 500; 2194 letter-spacing: 0.08em; 2195 text-transform: uppercase; 2196 color: var(--bp-color-text-muted); 2197 line-height: 1.3; 2198 } 2199 .bp-card__title { 2200 margin: 0; 2201 font-family: var(--bp-font-body); 2202 font-size: 15px; 2203 font-weight: 400; 2204 line-height: 1.3; 2205 letter-spacing: -0.15px; 2206 } 2207 .bp-card__title a { 2208 color: inherit; 2209 text-decoration: none; 2210 } 2211 .bp-card__title a:hover { text-decoration: underline; text-underline-offset: 3px; } 2212 2213 .bp-card__price-row { 2214 display: flex; 2215 flex-wrap: wrap; 2216 align-items: baseline; 2217 gap: 6px 10px; 2218 font-family: var(--bp-font-body); 2219 font-size: 14px; 2220 font-weight: 500; 2221 line-height: 145%; 2222 letter-spacing: -0.14px; 2223 } 2224 .bp-card__price { 2225 color: var(--bp-color-text, #171609); 2226 font-weight: 500; 2227 } 2228 .bp-card__compare { 2229 /* Strikethrough/compare price grey â #74736B, matching the PDP 2230 (bp-pdp-styles .compare-at-price) per QA (Amber). NB: #74736B is the 2231 WCAG-exempt grey (it fails AA 4.5:1); kept consistent with the PDP at 2232 the team's request over the darker --bp-color-body-copy (#666559). */ 2233 color: #74736B; 2234 text-decoration: line-through; 2235 font-weight: 500; 2236 } 2237 .bp-card__promo { 2238 color: #CA3D3D; 2239 font-size: 12px; 2240 font-weight: 400; 2241 line-height: 145%; 2242 letter-spacing: -0.12px; 2243 flex-basis: 100%; 2244 } 2245 /* Desktop: try to sit inline with the price; when it doesn't fit, 2246 flex-wrap pushes it to the next line. No margin-left:auto here 2247 â that would right-align the wrapped copy. Plain flex-grow:0 2248 keeps it hugging the price on the same line. */ 2249 @media screen and (min-width: 1024px) { 2250 .bp-card__promo { 2251 flex-basis: auto; 2252 margin-left: 0; 2253 text-align: left; 2254 } 2255 } 2256 2257 .bp-card__swatches { 2258 display: flex; 2259 align-items: center; 2260 gap: 10px; 2261 margin: 8px 0 0; 2262 padding: 4px; 2263 list-style: none; 2264 } 2265 .bp-card__swatch-item { 2266 display: inline-flex; 2267 align-items: center; 2268 position: relative; 2269 } 2270 /* Sale-discount badge on a swatch â Body/Semi/Tiny spec: 2271 Messina 10 / 660 / 145% / -0.1px, Sales-Red on white with a 2272 red border. Anchors top-right of the swatch dot. */ 2273 .bp-card__swatch-discount { 2274 position: absolute; 2275 top: -6px; 2276 right: -8px; 2277 display: inline-flex; 2278 flex-direction: column; 2279 justify-content: center; 2280 align-items: center; 2281 padding: 0 2px; 2282 border: 1px solid #CA3D3D; 2283 background: #FDFFFA; 2284 color: #CA3D3D; 2285 text-align: center; 2286 font-family: var(--bp-font-body); 2287 font-size: 10px; 2288 font-weight: 700; 2289 line-height: 145%; 2290 letter-spacing: -0.1px; 2291 pointer-events: none; 2292 white-space: nowrap; 2293 } 2294 /* BP a11y: interactive swatches are a <button> wrapping a decorative 2295 <img>. The button is a transparent wrapper (focus ring only) â the 2296 visual ring stays on .bp-card__swatch-img so the many bare-<span> 2297 swatch renderers (Bloomreach cards/carousel, search, you'll-also-love) 2298 keep it without needing the button. */ 2299 .bp-card__swatch { 2300 appearance: none; 2301 -webkit-appearance: none; 2302 padding: 0; 2303 border: 0; 2304 background: transparent; 2305 border-radius: 50%; 2306 cursor: pointer; 2307 display: block; 2308 line-height: 0; 2309 flex-shrink: 0; 2310 } 2311 .bp-card__swatch:focus-visible { 2312 outline: 2px solid var(--bp-color-text); 2313 outline-offset: 4px; 2314 } 2315 .bp-card__swatch-img { 2316 width: 24px; 2317 height: 24px; 2318 border-radius: 50%; 2319 object-fit: cover; 2320 display: block; 2321 flex-shrink: 0;
2322 /* 2px white gap + 1px cream ring â layered box-shadow keeps 2323 the element size stable while painting the ring outside the image. */ 2324 box-shadow: 0 0 0 2px #fff, 0 0 0 3px #E1E0D4; 2325 } 2326 /* Design-preview mock dot â solid background color, no image, 2327 same 23px footprint as the real swatch. */ 2328 .bp-card__swatch-img--mock { 2329 background-clip: content-box; 2330 background-size: cover; 2331 border-color: rgba(23, 22, 9, 0.15); 2332 } 2333 /* Active ring targets the visible dot whether the active class sits on 2334 the button (bp-product-card / bp-card-hydrate) or directly on the img 2335 (bare-span renderers). */ 2336 .bp-card__swatch--active .bp-card__swatch-img, 2337 .bp-card__swatch-img.bp-card__swatch--active { 2338 box-shadow: 0 0 0 2px #fff, 0 0 0 4px var(--bp-color-text); 2339 } 2340 .bp-card__swatch-more { 2341 font-family: var(--bp-font-body); 2342 font-size: 12px; 2343 color: rgba(23, 22, 9, 0.65); 2344 margin-left: 2px; 2345 } 2346 /* +N is now a PDP link â keep it visually identical to the old text 2347 (inherit colour, no underline) but tappable, with a hover cue. */ 2348 .bp-card__swatch-more-link { 2349 color: inherit; 2350 text-decoration: none; 2351 } 2352 .bp-card__swatch-more-link:hover { text-decoration: underline; text-underline-offset: 2px; } 2353 2354 /* ============================================================ 2355 BP Shop-the-Look drawer â global styles. 2356 2357 MOVED to assets/bp-fonts-deferred.css (loaded non-render- 2358 blocking from theme.liquid). `.bp-stl-drawer` is a native 2359 <dialog> (UA display:none until .showModal() opens it from a 2360 "Shop the look" trigger) â an interaction-revealed surface with 2361 no closed-state hide rule of its own â so its appearance rules 2362 don't need to block first paint. Markup: snippets/ 2363 bp-stl-drawer.liquid; behavior: assets/bp-stl-drawer.js. 2364 ============================================================ */ 2365 2366 /* ============================================================ 2367 BP Footer utilities bar â override Horizon's footer-utilities 2368 so the bottom copyright / policy bar inherits the cream 2369 background that bp-footer uses, and the link color stays on-brand. 2370 ============================================================ */ 2371 .footer-utilities { 2372 background-color: var(--bp-color-bg-cream); 2373 color: var(--bp-color-text); 2374 } 2375 .footer-utilities a { 2376 color: var(--bp-color-text); 2377 } 2378 2379 /* ============================================================ 2380 BP Mega Menu (desktop) â markup lives in blocks/_bp-menu-item.liquid. 2381 Horizon already handles submenu positioning, hover open/close, 2382 and the <header-menu> layout. We only add: vertical centering of 2383 the menu row + the .bp-mega* content styles for the dropdown. 2384 ============================================================ */ 2385 2386 /* Vertically center menu items in the header row */ 2387 header-menu .menu-list, 2388 .menu-list { 2389 align-items: center; 2390 } 2391 2392 /* Top-level nav links â smooth hover transitions. 2393 Uses Messina Sans VF (variable font) so font-weight can transition 2394 smoothly via font-variation-settings rather than snapping. Plus a 2395 gentle underline that grows from the center under each label. */ 2396 .menu-list__list-item .menu-list__link { 2397 position: relative; 2398 transition: opacity 0.2s ease, font-variation-settings 0.25s ease; 2399 font-variation-settings: 'wght' 400; 2400 } 2401 .menu-list__list-item .menu-list__link::after { 2402 content: ''; 2403 position: absolute; 2404 left: 50%; 2405 bottom: 0; 2406 width: 0; 2407 height: 1px; 2408 background: currentColor; 2409 transform: translateX(-50%); 2410 transition: width 0.25s ease; 2411 pointer-events: none; 2412 } 2413 .menu-list__list-item:hover .menu-list__link, 2414 .menu-list__list-item:focus-within .menu-list__link, 2415 .menu-list__list-item.bp-menu-open .menu-list__link, 2416 .menu-list__list-item .menu-list__link[aria-expanded='true'], 2417 .menu-list__list-item .menu-list__link.menu-list__link--active { 2418 font-weight: 700; 2419 font-variation-settings: 'wght' 700; 2420 } 2421 .menu-list__list-item:hover .menu-list__link::after, 2422 .menu-list__list-item:focus-within .menu-list__link::after, 2423 .menu-list__list-item.bp-menu-open .menu-list__link::after { 2424 width: 100%; 2425 } 2426 /* Sibling fade â when one nav item is hovered/open, gently dim the others */ 2427 .menu-list:hover .menu-list__list-item .menu-list__link, 2428 .menu-list:has(.bp-menu-open) .menu-list__list-item .menu-list__link { 2429 opacity: 0.55; 2430 } 2431 .menu-list:hover .menu-list__list-item:hover .menu-list__link, 2432 .menu-list .menu-list__list-item.bp-menu-open .menu-list__link, 2433 .menu-list .menu-list__list-item:focus-within .menu-list__link { 2434 opacity: 1; 2435 } 2436 2437 /* BP mega submenu â own classes (.bp-mega-submenu, not Horizon's 2438 .menu-list__submenu) so Horizon's submenu CSS doesn't apply. Open/ 2439 close is driven by .bp-menu-open on the LI, set by our JS hover 2440 handler (assets/bp-mega-hover.js) with a debounced close. */ 2441 .bp-mega-submenu { 2442 position: fixed; 2443 left: 0; 2444 right: 0; 2445 top: var(--header-height, 60px); 2446 width: 100vw; 2447 max-width: 100vw; 2448 z-index: 90; 2449 /* Off-white to match the header (QA 7/16) â falls back to the BP page 2450 bg token, not #fff, if --color-background is ever unset. */ 2451 background: var(--color-background, var(--bp-color-bg-page)); 2452 box-shadow: 0 8px 24px rgba(0, 0, 0, 0.06); 2453 opacity: 0; 2454 pointer-events: none; 2455 transition: opacity 0.15s ease; 2456 } 2457 .menu-list__list-item.bp-menu-open > .bp-mega-submenu { 2458 opacity: 1; 2459 pointer-events: auto; 2460 } 2461 .bp-mega-submenu__inner { 2462 width: 100%; 2463 /* CLAUDE.md spacing tokens â match the rest of the BP sections. 2464 Top is 20px (a touch tight against the nav row); sides + bottom 2465 use the standard --bp-section-pad-x / -y so the content's left 2466 edge aligns with the logo (which sits at --bp-section-pad-x via 2467 the topnav --page-margin override in this same file). No 2468 max-width / centering â full-bleed minus gutters. */ 2469 padding: 20px var(--bp-section-pad-x) var(--bp-section-pad-y); 2470 box-sizing: border-box; 2471 } 2472 2473 /* Layout: columns + (optional) right-side promos. Uses BP spacing 2474 tokens (CLAUDE.md): --bp-section-gap = 20px desktop. */ 2475 .bp-mega { 2476 display: flex; 2477 align-items: flex-start; 2478 gap: var(--bp-section-gap); 2479 align-self: stretch; 2480 width: 100%; 2481 white-space: normal; 2482 word-wrap: break-word; 2483 overflow-wrap: break-word; 2484 font-family: var(--font-body--family); 2485 font-size: var(--font-size--sm); 2486 line-height: 1.4; 2487 color: var(--color-foreground); 2488 } 2489 .bp-mega *, .bp-mega *::before, .bp-mega *::after { white-space: normal; } 2490 2491 /* Columns side-by-side, equal width, BP spacing */ 2492 .bp-mega__columns { 2493 flex: 1 1 auto; 2494 display: flex; 2495 gap: var(--bp-section-gap); 2496 } 2497 .bp-mega__column { 2498 flex: 1 1 0; 2499 min-width: 0; 2500 } 2501 2502 .bp-mega__column { 2503 display: flex; 2504 flex-direction: column; 2505 gap: var(--gap-sm); 2506 min-width: 0; 2507 } 2508 /* Column / tab heading â Body/Semi/Small (Messina Sans VF, 14px, 660). */ 2509 .bp-mega__heading { 2510 margin: 0 0 var(--bp-card-gap); 2511 color: var(--bp-color-text); 2512 font-family: var(--bp-font-body); 2513 font-size: 14px; 2514 font-style: normal; 2515 font-weight: 700; 2516 line-height: 145%; 2517 letter-spacing: -0.14px; 2518 } 2519 .bp-mega__heading a { color: inherit; text-decoration: none; } 2520 2521 .bp-mega__links { 2522 list-style: none; 2523 margin: 0; 2524 padding: 0; 2525 display: flex; 2526 flex-direction: column; 2527 gap: 6px; 2528 } 2529 .bp-mega__links a { 2530 font-family: var(--font-body--family); 2531 font-size: var(--font-size--sm); 2532 color: var(--color-foreground); 2533 text-decoration: none; 2534 line-height: 1.4; 2535 } 2536 .bp-mega__links a:hover { text-decoration: underline; } 2537 2538 .bp-mega__promos { 2539 flex: 0 0 auto; 2540 /* Pin to the right edge of the dropdown (just inside the 2541 --bp-section-pad-x gutter). margin-inline-start:auto pushes us 2542 past the columns regardless of how much space they take. */ 2543 margin-inline-start: auto; 2544 display: grid; 2545 /* Default = two fixed-width columns. The :has(:only-child) rule 2546 below swaps to a single wider column when only one promo 2547 renders. Was repeat(auto-fit, 220px), but auto-fit would 2548 collapse to a single column on dropdowns where the menu 2549 columns squeezed this container narrow â making the second 2550 promo wrap below the first instead of sitting beside it. */ 2551 grid-template-columns: repeat(2, 220px); 2552 gap: var(--bp-section-gap); 2553 justify-content: end; 2554 } 2555 .bp-mega__promo { min-width: 0; max-width: 100%; } 2556 .bp-mega__promo-link { 2557 display: flex; 2558 flex-direction: column; 2559 gap: var(--gap-xs); 2560 text-decoration: none; 2561 color: var(--color-foreground); 2562 width: 100%; 2563 } 2564 .bp-mega__promo-image { 2565 position: relative; 2566 width: 100%; 2567 aspect-ratio: 4 / 5; 2568 background: var(--color-foreground-muted, #ddd); 2569 overflow: hidden; 2570 } 2571 .bp-mega__promo-image svg, 2572 .bp-mega__promo-image img, 2573 .bp-mega__promo-video { 2574 position: absolute; 2575 inset: 0; 2576 width: 100%; 2577 height: 100%; 2578 object-fit: cover; 2579 display: block; 2580 } 2581 2582 /* Promo typography â Messina Sans VF semi (660) for the title; 2583 500 for the body copy. Colors: Black for title, --bp-color-body-copy 2584 (#74736B) for the supporting paragraph. */ 2585 .bp-mega__promo-title { 2586 margin: 0; 2587 color: var(--bp-color-text); 2588 font-family: var(--bp-font-body); 2589 font-size: 14px; 2590 font-style: normal; 2591 font-weight: 700; 2592 line-height: 145%; 2593 letter-spacing: -0.14px; 2594 } 2595 .bp-mega__promo-copy { 2596 margin: 0; 2597 color: var(--bp-color-body-copy); 2598 font-family: var(--bp-font-body); 2599 font-size: 12px; 2600 font-style: normal; 2601 font-weight: 500; 2602 line-height: 145%; 2603 letter-spacing: -0.12px; 2604 } 2605 .bp-mega__promo-cta { 2606 margin-top: 4px; 2607 color: var(--bp-color-text); 2608 font-family: var(--bp-font-body); 2609 font-size: 12px; 2610 font-weight: 500; 2611 line-height: 145%; 2612 letter-spacing: -0.12px; 2613 text-decoration: underline; 2614 text-underline-offset: 3px; 2615 } 2616 2617 /* Single-promo variant: when only one promo is rendered inside a 2618 columns_promos dropdown, give it the width of two regular promos 2619 (~460px) and a 3:2 landscape image, capped at 370px tall. Driven 2620 by :only-child so no Liquid changes needed. */ 2621 .bp-mega__promos:has(> .bp-mega__promo:only-child) { 2622 grid-template-columns: minmax(0, 460px); 2623 } 2624 .bp-mega__promos > .bp-mega__promo:only-child .bp-mega__promo-image { 2625 aspect-ratio: 3 / 2; 2626 height: 370px; 2627 width: 100%; 2628 align-self: stretch; 2629 } 2630 2631 @media screen and (max-width: 989px) { 2632 .bp-mega--columns-promos { flex-direction: column; } 2633 .bp-mega__promos { grid-template-columns: repeat(2, minmax(0, 1fr)); } 2634 } 2635 2636 /* ----- Layout: image_tiles (Occasions) ---------------------------- 2637 Flat row of tiles. Each tile = square image + title + Shop Now. 2638 Tile width is fluid; on tighter widths the row wraps. Designed 2639 to lay out 6 tiles in one row at desktop widths. */ 2640 .bp-mega--image-tiles { display: block; } 2641 .bp-mega__tiles { 2642 display: grid; 2643 grid-template-columns: repeat(6, minmax(0, 1fr)); 2644 gap: var(--bp-section-gap); 2645 width: 100%; 2646 } 2647 @media screen and (max-width: 1279px) { 2648 .bp-mega__tiles { grid-template-columns: repeat(4, minmax(0, 1fr)); } 2649 } 2650 @media screen and (max-width: 989px) { 2651 .bp-mega__tiles { grid-template-columns: repeat(3, minmax(0, 1fr)); } 2652 } 2653 .bp-mega__tile { 2654 display: flex; 2655 flex-direction: column; 2656 gap: var(--bp-card-gap); 2657 color: var(--bp-color-text); 2658 text-decoration: none; 2659 } 2660 .bp-mega__tile-image { 2661 position: relative; 2662 aspect-ratio: 1 / 1; 2663 background: var(--bp-color-bg-cream); 2664 overflow: hidden; 2665 } 2666 /* Image-tiles layout ONLY: 4/5 portrait per the wireframe â the square 2667 (1/1) cropped the imagery (Ernie 7/27). Scoped to the image_tiles 2668 container so other mega layouts are unaffected. The <img> still 2669 object-fit:covers this box. */ 2670 .bp-mega--image-tiles .bp-mega__tile-image { 2671 aspect-ratio: 4 / 5; 2672 } 2673 .bp-mega__tile-image img, 2674 .bp-mega__tile-image svg { 2675 position: absolute; 2676 inset: 0; 2677 width: 100%; 2678 height: 100%; 2679 object-fit: cover; 2680 display: block; 2681 } 2682 .bp-mega__tile-title { 2683 margin: 0; 2684 font-family: var(--bp-font-body); 2685 font-size: 15px; 2686 font-weight: 500; 2687 line-height: 1.3; 2688 letter-spacing: -0.15px; 2689 color: var(--bp-color-text); 2690 } 2691 .bp-mega__tile-cta { 2692 font-family: var(--bp-font-body); 2693 font-size: 13px; 2694 line-height: 1.4; 2695 color: var(--bp-color-text); 2696 text-decoration: underline; 2697 text-underline-offset: 3px; 2698 } 2699 2700 /* ----- Layout: tabs_promos (Brands We Love) ----------------------- 2701 3-column grid: vertical tab list | active panel | promos. 2702 Tab activation is handled by assets/bp-mega-hover.js (initTabs). */ 2703 .bp-mega--tabs-promos { display: block; } 2704 .bp-mega__tabs { 2705 display: grid; 2706 grid-template-columns: minmax(180px, 1fr) minmax(0, 2fr) minmax(0, 2fr); 2707 gap: var(--bp-section-gap); 2708 width: 100%; 2709 align-items: start; 2710 } 2711 .bp-mega__tabs-list { 2712 list-style: none; 2713 margin: 0; 2714 padding: 0; 2715 display: flex; 2716 flex-direction: column; 2717 gap: 4px; 2718 } 2719 .bp-mega__tab { 2720 display: flex; 2721 align-items: center; 2722 justify-content: space-between; 2723 width: 100%; 2724 padding: 14px 16px; 2725 background: transparent; 2726 border: 0; 2727 cursor: pointer; 2728 text-align: left; 2729 color: var(--bp-color-text); 2730 font-family: var(--bp-font-body); 2731 font-size: 14px; 2732 line-height: 1.3; 2733 letter-spacing: -0.14px; 2734 transition: background-color 0.15s ease, font-weight 0.15s ease; 2735 } 2736 .bp-mega__tab:hover { 2737 background: var(--bp-color-neutral); 2738 } 2739 .bp-mega__tab.is-active { 2740 background: var(--bp-color-neutral); 2741 font-weight: 600; 2742 } 2743 .bp-mega__tab-chevron { 2744 display: inline-flex; 2745 align-items: center; 2746 justify-content: center; 2747 flex: 0 0 auto; 2748 margin-left: 12px; 2749 opacity: 0.85; 2750 /* New chevron asset is drawn left-pointing; flip to point right 2751 (toward the tab panel that opens on the right). */ 2752 transform: scaleX(-1); 2753 } 2754 .bp-mega__tabs-panels { 2755 min-width: 0; 2756 } 2757 .bp-mega__tab-panel { 2758 display: none; 2759 padding: 4px 0; 2760 } 2761 .bp-mega__tab-panel.is-active { 2762 display: block; 2763 } 2764 /* Tabs layout uses a slightly looser link gap (8px vs 6px elsewhere). */ 2765 .bp-mega--tabs-promos .bp-mega__links { 2766 gap: 8px; 2767 } 2768 /* Two-column tab links (merchant toggle: block.settings.tabs_link_columns). 2769 Long lists like Brands We Love overflow one tall column and leave the 2770 right half of the panel empty; splitting into two balanced columns uses 2771 that white space beside the promos. Give the panel track more room (3fr) 2772 so both columns breathe, then flow the <ul> as CSS multi-column â items 2773 fill top-to-bottom of column 1, then column 2, which keeps an 2774 alphabetical brand list reading naturally (AâM | NâZ). */ 2775 .bp-mega__tabs[data-bp-tab-columns="2"] { 2776 grid-template-columns: minmax(180px, 1fr) minmax(0, 3fr) minmax(0, 2fr); 2777 } 2778 .bp-mega__tabs[data-bp-tab-columns="2"] .bp-mega__links { 2779 display: block; 2780 columns: 2; 2781 column-gap: var(--bp-section-gap); 2782 } 2783 .bp-mega__tabs[data-bp-tab-columns="2"] .bp-mega__links li { 2784 margin-bottom: 8px; 2785 break-inside: avoid; 2786 -webkit-column-break-inside: avoid; 2787 } 2788 .bp-mega__tabs[data-bp-tab-columns="2"] .bp-mega__links li:last-child { 2789 margin-bottom: 0; 2790 } 2791 .bp-mega__promos--tabs { 2792 /* Match the columns_promos promos (styleEDIT): fixed 220px columns so the 2793 images render full-size. `minmax(0,1fr)` collapsed them to a sliver 2794 because the promos container is content-sized (flex:0 0 auto) â the 2795 fr units had no defined width to divide, so the 4:5 images shrank to 2796 their min. The tabs dropdown has ample room on the right (tabs + brand 2797 list only fill the left half). */ 2798 grid-template-columns: repeat(2, 220px); 2799 display: grid; 2800 gap: var(--bp-section-gap); 2801 } 2802 2803 @media screen and (max-width: 989px) { 2804 .bp-mega__tabs, 2805 .bp-mega__tabs[data-bp-tab-columns="2"] { grid-template-columns: 1fr; } 2806 .bp-mega__promos--tabs { grid-template-colu
2806mns: repeat(2, minmax(0, 1fr)); } 2807 /* Not enough width for two link columns once the tabs stack â collapse. */ 2808 .bp-mega__tabs[data-bp-tab-columns="2"] .bp-mega__links { columns: 1; } 2809 } 2810 2811 @media screen and (max-width: 749px) { 2812 .bp-mega { display: none; } 2813 } 2814 2815 /* BP Cart Drawer â markup, CSS, and JS live in 2816 snippets/bp-cart-drawer.liquid (rendered from theme.liquid). 2817 Hide Horizon's cart-drawer-component since BP has its own. */ 2818 cart-drawer-component { display: none !important; } 2819 2820 /* ===== §9.1 BP Section knobs â base rules =========================== 2821 Consumes CSS variables emitted by snippets/bp-section-knobs.liquid. 2822 Sections opt in by carrying `data-bp-section`, `data-section-id`, 2823 and `data-bp-mode-{d,m}` attributes on the wrapping element. 2824 Mobile is the default; desktop overrides at â¥1024px (§1). 2825 ================================================================= */ 2826 2827 [data-bp-section] { 2828 background: var(--bp-knobs-bg-section, transparent); 2829 color: var(--bp-knobs-text, var(--bp-color-text)); 2830 } 2831 2832 [data-bp-section][data-bp-mode-m='padded'] { 2833 padding-block: var(--bp-knobs-pad-y-m, var(--bp-section-pad-y)); 2834 padding-inline: var(--bp-knobs-pad-x-m, var(--bp-section-pad-x)); 2835 } 2836 [data-bp-section][data-bp-mode-m='full'] { 2837 padding-block: var(--bp-knobs-pad-y-m, var(--bp-section-pad-y)); 2838 padding-inline: 0; 2839 } 2840 2841 /* Inner block â only renders meaningfully when padded. Holds the 2842 inner background color so a padded section can have a section bg 2843 AND a different inner-block bg (per §9.1). */ 2844 [data-bp-section] .bp-section__inner { 2845 background: var(--bp-knobs-bg-inner, transparent); 2846 } 2847 2848 /* Title alignment â opt in via class `bp-section__title` on the 2849 heading. text-align is inheritable, so descendant copy follows. */ 2850 [data-bp-section] .bp-section__title { 2851 text-align: var(--bp-knobs-align-m, start); 2852 } 2853 2854 @media screen and (min-width: 1024px) { 2855 [data-bp-section][data-bp-mode-d='padded'] { 2856 padding-block: var(--bp-knobs-pad-y-d, var(--bp-section-pad-y)); 2857 padding-inline: var(--bp-knobs-pad-x-d, var(--bp-section-pad-x)); 2858 } 2859 [data-bp-section][data-bp-mode-d='full'] { 2860 padding-block: var(--bp-knobs-pad-y-d, var(--bp-section-pad-y)); 2861 padding-inline: 0; 2862 } 2863 [data-bp-section] .bp-section__title { 2864 text-align: var(--bp-knobs-align-d, start); 2865 } 2866 } 2867 2868 /* ===== §9.3 BP Media block â base rules ============================ 2869 Used by snippets/bp-section-media.liquid. The snippet emits a 2870 [data-bp-media] wrapper with optional `--bp-media-ratio-{d,m}` 2871 custom properties and an optional .bp-media__overlay child. 2872 The overlay's solid vs. gradient + coverage live entirely in CSS 2873 so the merchant gets live-preview without re-rendering markup. 2874 2875 §9.3 rule: images always fill the full width of their slot. 2876 position:relative + absolute children + object-fit cover does 2877 this regardless of the slot's height (which the section sizes). 2878 ================================================================= */ 2879 2880 [data-bp-media] { 2881 position: relative; 2882 overflow: hidden; 2883 width: 100%; 2884 /* Mobile-first ratio. Falls through to the desktop ratio when no 2885 mobile-specific value is set. `auto` (the default) leaves the 2886 slot to be sized by its parent (e.g. fixed mobile band height, 2887 desktop grid row stretch). */ 2888 aspect-ratio: var(--bp-media-ratio-m, var(--bp-media-ratio-d, auto)); 2889 } 2890 2891 [data-bp-media] .bp-media__image, 2892 [data-bp-media] .bp-media__video { 2893 position: absolute; 2894 inset: 0; 2895 width: 100%; 2896 height: 100%; 2897 /* Fit: cover (default, fills + crops) vs contain (whole asset shown, 2898 may letterbox against the slot bg). Set per media block via the 2899 {prefix}_fit knob â e.g. a rotating-logo video that must never be 2900 cropped uses 'contain'. */ 2901 object-fit: var(--bp-media-fit, cover); 2902 /* Focal point. Mobile-first; desktop override below. 2903 Merchants pick from a 9-position select on the media block. */ 2904 object-position: var(--bp-media-pos-m, var(--bp-media-pos-d, center center)); 2905 display: block; 2906 } 2907 2908 [data-bp-media] .bp-media__placeholder { 2909 position: absolute; 2910 inset: 0; 2911 background: linear-gradient(135deg, #e8e6df 0%, #cfccc1 100%); 2912 } 2913 2914 /* Overlay â sits on top of the media, solid or gradient. The 2915 coverage % indicates how much of the height (anchored to the 2916 bottom) carries the colour: 2917 - solid: a hard band from 0 to coverage% (transparent above) 2918 - gradient: fades from full colour at the bottom to transparent 2919 at coverage% from the bottom 2920 Selectors are unscoped so block-level surfaces (hero slides, 2921 look tiles, category cards) can use the same overlay without 2922 needing the [data-bp-media] block-prefix machinery â drop the 2923 element inside any position:relative parent and it fills it. */ 2924 .bp-media__overlay { 2925 position: absolute; 2926 inset: 0; 2927 pointer-events: none; 2928 /* Opacity is responsive: --â¦-opacity-m (mobile) then the single 2929 --â¦-opacity, then a default. Desktop override lives in the media 2930 query below. */ 2931 opacity: var(--bp-media-overlay-opacity-m, var(--bp-media-overlay-opacity, 0.3)); 2932 } 2933 /* Gradient DIRECTION + COVERAGE are driven per breakpoint by 2934 --bp-media-overlay-dir-{m,d} and --bp-media-overlay-coverage-{m,d} 2935 (emitted from the "Overlay position" + "Coverage" controls â 2936 bottomâ`to top`, topâ`to bottom`, leftâ`to right`, rightâ`to left`). 2937 Each falls back to its single-value var, then the bottom-anchored / 2938 full-image default, so sections that set only the shared values (or 2939 nothing) are unchanged. */ 2940 .bp-media__overlay--solid { 2941 background: linear-gradient( 2942 var(--bp-media-overlay-dir-m, to top), 2943 var(--bp-media-overlay-color, #000) 0, 2944 var(--bp-media-overlay-color, #000) var(--bp-media-overlay-coverage-m, var(--bp-media-overlay-coverage, 100%)), 2945 transparent var(--bp-media-overlay-coverage-m, var(--bp-media-overlay-coverage, 100%)) 2946 ); 2947 } 2948 .bp-media__overlay--gradient { 2949 background: linear-gradient( 2950 var(--bp-media-overlay-dir-m, to top), 2951 var(--bp-media-overlay-color, #000) 0, 2952 transparent var(--bp-media-overlay-coverage-m, var(--bp-media-overlay-coverage, 100%)) 2953 ); 2954 } 2955 2956 @media screen and (min-width: 1024px) { 2957 [data-bp-media] { 2958 aspect-ratio: var(--bp-media-ratio-d, auto); 2959 } 2960 [data-bp-media] .bp-media__image, 2961 [data-bp-media] .bp-media__video { 2962 object-position: var(--bp-media-pos-d, center center); 2963 } 2964 /* Desktop overlay â opacity, direction, and coverage each fall back to 2965 the mobile value, then the single-value var, then the default, so a 2966 desktop-only or single-value setup still works. */ 2967 .bp-media__overlay { 2968 opacity: var(--bp-media-overlay-opacity-d, var(--bp-media-overlay-opacity-m, var(--bp-media-overlay-opacity, 0.3))); 2969 } 2970 .bp-media__overlay--solid { 2971 background: linear-gradient( 2972 var(--bp-media-overlay-dir-d, var(--bp-media-overlay-dir-m, to top)), 2973 var(--bp-media-overlay-color, #000) 0, 2974 var(--bp-media-overlay-color, #000) var(--bp-media-overlay-coverage-d, var(--bp-media-overlay-coverage-m, var(--bp-media-overlay-coverage, 100%))), 2975 transparent var(--bp-media-overlay-coverage-d, var(--bp-media-overlay-coverage-m, var(--bp-media-overlay-coverage, 100%))) 2976 ); 2977 } 2978 .bp-media__overlay--gradient { 2979 background: linear-gradient( 2980 var(--bp-media-overlay-dir-d, var(--bp-media-overlay-dir-m, to top)), 2981 var(--bp-media-overlay-color, #000) 0, 2982 transparent var(--bp-media-overlay-coverage-d, var(--bp-media-overlay-coverage-m, var(--bp-media-overlay-coverage, 100%))) 2983 ); 2984 } 2985 } 2986 2987 /* ===== WCAG 2.2.2 pause/play button (bp-section-media.liquid) ======
2988 Shown whenever a video autoplays without native controls. Positioned 2989 bottom-left inside [data-bp-media]; z-index 2 keeps it above the 2990 overlay (which sits at z-index 1 by stacking order). ============== */ 2991 .bp-media__pause-btn { 2992 position: absolute; 2993 bottom: 12px; 2994 left: 12px; 2995 z-index: 2; 2996 display: inline-flex; 2997 align-items: center; 2998 justify-content: center; 2999 width: 40px; 3000 height: 40px; 3001 padding: 0; 3002 border: none; 3003 border-radius: 50%; 3004 background-color: rgba(0, 0, 0, 0.6); 3005 color: #fff; 3006 cursor: pointer; 3007 transition: background-color 0.15s ease; 3008 } 3009 .bp-media__pause-btn:hover, 3010 .bp-media__pause-btn:focus { 3011 background-color: rgba(0, 0, 0, 0.82); 3012 } 3013 .bp-media__pause-btn:focus-visible { 3014 outline: 2px solid #fff; 3015 outline-offset: 2px; 3016 } 3017 .bp-media__pause-btn:focus:not(:focus-visible) { 3018 outline: none; 3019 } 3020 3021 /* ===== §9.2 BP CTA Group + Button â base rules ===================== 3022 Sections wrap their CTAs in `[data-bp-cta-group]` with 3023 `data-dir-d="row|column"` and `data-dir-m="row|column"` to 3024 control multi-button layout per breakpoint. Each button is a 3025 `.bp-cta` rendered by snippets/bp-cta-button.liquid carrying 3026 `data-w-{d,m}="fit|custom"` plus inline `--bp-cta-w-{d,m}` 3027 custom-percent values for the Custom width mode. 3028 ================================================================= */ 3029 3030 [data-bp-cta-group] { 3031 display: flex; 3032 flex-direction: column; 3033 flex-wrap: wrap; 3034 gap: 12px; 3035 align-items: stretch; 3036 } 3037 /* Full-width toggle stretches the group container itself (not just its 3038 buttons), so a group centered/shrink-wrapped by its parent still spans 3039 the full available width and its stacked buttons truly fill it. Only the 3040 CTA-group block opts in (data-full-width="true"); legacy section-level 3041 groups have no attribute and are unaffected. */ 3042 [data-bp-cta-group][data-full-width='true'] { width: 100%; } 3043 [data-bp-cta-group][data-dir-m='row'] { 3044 flex-direction: row; 3045 align-items: center; 3046 } 3047 @media screen and (min-width: 1024px) { 3048 [data-bp-cta-group][data-dir-d='row'] { 3049 flex-direction: row; 3050 align-items: center; 3051 } 3052 [data-bp-cta-group][data-dir-d='column'] { 3053 flex-direction: column; 3054 align-items: stretch; 3055 } 3056 } 3057 3058 /* Button width â default Fit (content-sized). Custom mode picks 3059 the width up from the inline --bp-cta-w-{d,m} variable. 3060 In a STACKED group, buttons grow to full container width â UNLESS the 3061 group opts out via the Full-width toggle (bp-cta-group emits 3062 data-full-width="false"), where align-self:start defeats the group's 3063 align-items:stretch so each button hugs its fit/custom width instead. 3064 Groups with no attribute (legacy section-level [data-bp-cta-group]) 3065 keep the full-width default. */ 3066 .bp-cta[data-w-m='fit'] { width: auto; } 3067 .bp-cta[data-w-m='custom'] { width: var(--bp-cta-w-m, 100%); } 3068 @media screen and (max-width: 1023px) { 3069 [data-bp-cta-group]:not([data-dir-m='row']):not([data-full-width='false']) .bp-block-cta { width: 100%; } 3070 [data-bp-cta-group]:not([data-dir-m='row']):not([data-full-width='false']) .bp-cta { width: 100%; } 3071 [data-bp-cta-group]:not([data-dir-m='row'])[data-full-width='false'] .bp-block-cta, 3072 [data-bp-cta-group]:not([data-dir-m='row'])[data-full-width='false'] .bp-cta { align-self: flex-start; } 3073 } 3074 @media screen and (min-width: 1024px) { 3075 .bp-cta[data-w-d='fit'] { width: auto; } 3076 .bp-cta[data-w-d='custom'] { width: var(--bp-cta-w-d, 100%); } 3077 [data-bp-cta-group][data-dir-d='column']:not([data-full-width='false']) .bp-block-cta { width: 100%; } 3078 [data-bp-cta-group][data-dir-d='column']:not([data-full-width='false']) .bp-cta { width: 100%; } 3079 [data-bp-cta-group][data-dir-d='column'][data-full-width='false'] .bp-block-cta, 3080 [data-bp-cta-group][data-dir-d='column'][data-full-width='false'] .bp-cta { align-self: flex-start; } 3081 } 3082 3083 /* Base button shape â height + padding + typography. The colour 3084 palette comes from the section-local --bp-knobs-text + a small 3085 set of role-specific defaults below; sections can also layer in 3086 their own per-section CTA tokens via extra classes. */ 3087 .bp-cta { 3088 display: inline-flex; 3089 align-items: center; 3090 justify-content: center; 3091 gap: 8px; 3092 height: 52px; 3093 padding: 0 28px; 3094 font-family: var(--bp-font-body); 3095 font-size: 17px; 3096 font-weight: 500; 3097 line-height: normal; 3098 letter-spacing: -0.17px; 3099 text-decoration: none; 3100 /* Default variant (secondary-like) â outlined dark on transparent. 3101 Always uses BP brand tokens, NOT --bp-knobs-text, so the button 3102 palette stays consistent regardless of the section's text color. 3103 Sections that need a different palette layer in their own scoped 3104 override (e.g. hero slides on dark imagery use `primary-light`). */ 3105 border: 1px solid var(--bp-color-text); 3106 color: var(--bp-color-text); 3107 background: transparent; 3108 transition: background-color 0.2s ease, color 0.2s ease, border-color 0.2s ease, opacity 0.2s ease; 3109 cursor: pointer; 3110 white-space: nowrap; 3111 box-sizing: border-box; 3112 }
3113 /* Keyboard focus indicator (WCAG 2.4.7). No focus style existed, so tabbing 3114 to a CTA showed nothing â flagged on the Image + Text Banner (Alfred, 7/27, 3115 ADA). `:focus-visible` (not `:focus`) so a mouse click doesn't leave a 3116 ring. The ring is OFFSET outside the button so it sits on the section/page 3117 background: a dark ring reads on the light surfaces every standard variant 3118 lives on; the on-dark variants (used over imagery) switch to the light 3119 label color below. Matches the existing `.bp-card__swatch:focus-visible` 3120 pattern. Also keep link-style CTAs underlined on hover so they never 3121 collapse to plain text (the second half of the ticket). */ 3122 .bp-cta:focus-visible { 3123 outline: 2px solid var(--bp-color-text); 3124 outline-offset: 3px; 3125 } 3126 .bp-cta--primary-light:focus-visible, 3127 .bp-cta--secondary-light:focus-visible, 3128 .bp-cta--link-white:focus-visible { 3129 outline-color: var(--bp-color-cta-text); 3130 } 3131 .bp-cta--link:hover, 3132 .bp-cta--link:focus-visible, 3133 .bp-cta--link-white:hover, 3134 .bp-cta--link-white:focus-visible { 3135 text-decoration: underline; 3136 } 3137 .bp-cta__icon { 3138 display: inline-flex; 3139 align-items: center; 3140 width: 20px; 3141 height: 20px; 3142 } 3143 .bp-cta__icon img { 3144 width: 100%; 3145 height: 100%; 3146 display: block; 3147 object-fit: contain; 3148 } 3149 3150 /* Primary â fixed BP brand palette: dark fill + light label. The 3151 palette stays consistent across sections regardless of the 3152 section's `bp_text_color` setting; sections that need a different 3153 look on dark imagery pick `primary-light` (white fill, dark 3154 label) instead. */ 3155 .bp-cta--primary { 3156 background: var(--bp-color-text); 3157 color: var(--bp-color-cta-text); 3158 border-color: var(--bp-color-text); 3159 } 3160 .bp-cta--primary:hover { 3161 background: transparent; 3162 color: var(--bp-color-text); 3163 } 3164 3165 /* Primary-light: inverse of primary â white fill + dark label. 3166 Use on sections that sit on dark imagery (e.g. hero closing 3167 sliders) where a dark filled CTA clashes with the background. */ 3168 .bp-cta--primary-light { 3169 background: var(--bp-color-bg-page); 3170 color: var(--bp-color-text); 3171 border-color: #fff; 3172 } 3173 /* primary-light is the white-filled button used ON DARK imagery / dark 3174 sections. Its hover must stay LIGHT â transparent fill + near-white label 3175 and border â otherwise it resolves to dark-on-dark and the button reads 3176 "all black" on hover (QA: Dragana â "Find your look" / "View Details" on 3177 the homepage promo header). This is the correct default for the style, so 3178 it no longer needs the per-CTA "Hover style: White" opt-in. */ 3179 .bp-cta--primary-light:hover { 3180 background: transparent; 3181 color: var(--bp-color-cta-text); 3182 border-color: var(--bp-color-cta-text); 3183 } 3184 3185 /* Sand: filled sand (#E8DFD0) button with dark-brown (#2B241A) label. 3186 Hover inverts to the sand-invert palette. */ 3187 .bp-cta--sand { 3188 background: var(--bp-color-cta-sand); 3189 color: var(--bp-color-cta-sand-text); 3190 border-color: var(--bp-color-cta-sand); 3191 } 3192 .bp-cta--sand:hover { 3193 background: var(--bp-color-cta-sand-text); 3194 color: var(--bp-color-cta-sand); 3195 border-color: var(--bp-color-cta-sand-text); 3196 } 3197 3198 /* Sand (inverted): dark-brown fill with sand label â the invert of 3199 --sand. Hover swaps to the filled-sand palette. */ 3200 .bp-cta--sand-invert { 3201 background: var(--bp-color-cta-sand-text); 3202 color: var(--bp-color-cta-sand); 3203 border-color: var(--bp-color-cta-sand-text); 3204 } 3205 .bp-cta--sand-invert:hover { 3206 background: var(--bp-color-cta-sand); 3207 color: var(--bp-color-cta-sand-text); 3208 border-color: var(--bp-color-cta-sand); 3209 } 3210 3211 .bp-cta--secondary:hover { 3212 background: var(--bp-color-text); 3213 color: var(--bp-color-cta-text); 3214 } 3215 3216 /* Secondary-light: the on-dark counterpart to `secondary`. Same 3217 outlined geometry, but a WHITE border + WHITE label on a 3218 transparent fill so it reads on dark imagery (e.g. the "Who we are" 3219 banner over a photo) where the standard dark `secondary` outline 3220 would disappear. Hover inverts exactly like `secondary` does â 3221 fills with the border color (white) and flips the label dark. */ 3222 .bp-cta--secondary-light { 3223 background: transparent; 3224 color: var(--bp-color-cta-text); 3225 border-color: var(--bp-color-cta-text); 3226 } 3227 .bp-cta--secondary-light:hover { 3228 background: var(--bp-color-cta-text); 3229 color: var(--bp-color-text); 3230 border-color: var(--bp-color-cta-text); 3231 } 3232 .bp-cta--link { 3233 height: auto; 3234 padding: 0; 3235 border: 0; 3236 text-decoration: underline; 3237 text-underline-offset: 4px; 3238 } 3239 .bp-cta--link:hover { opacity: 0.65; } 3240 3241 /* Underline link, white text â for CTAs sitting on dark imagery / 3242 dark section backgrounds where the standard `link` (which inherits 3243 foreground color) would read black-on-black. Same geometry as 3244 `--link`; explicitly sets color to var(--bp-color-cta-text) so it 3245 doesn't pick up the surrounding text color. */ 3246 .bp-cta--link-white { 3247 height: auto; 3248 padding: 0; 3249 border: 0; 3250 background: transparent; 3251 color: var(--bp-color-cta-text, #FDFFFA); 3252 text-decoration: underline; 3253 text-underline-offset: 4px; 3254 } 3255 .bp-cta--link-white:hover { opacity: 0.65; } 3256 3257 /* Opt-in hover: transparent fill + white border + white text. Set per CTA 3258 via the BP CTA block's "Hover style" = White. For CTAs on dark backgrounds 3259 (e.g. the black homepage promo bar) where the default per-style hover 3260 resolves to dark-on-dark (QA: Dragana). !important so it wins over the 3261 equal-specificity per-variant :hover rules regardless of source order. */ 3262 .bp-cta--hover-white:hover { 3263 background: transparent !important; 3264 color: #fff !important; 3265 border-color: #fff !important; 3266 opacity: 1 !important; 3267 } 3268 3269 /* (Legacy --bp-cta-on override removed â primary's palette is now 3270 fixed at the base, so there's no longer a white-on-white scenario 3271 for sections to opt out of. Sections that need a light CTA on 3272 dark imagery should pick the `primary-light` variant instead.) */ 3273 3274 /* ===== §9.1.x Text-overlay placement (9-position) ================== 3275 Used by sections that lay copy on top of media (hero slides, 3276 grid tiles). The wrapping element opts in via `[data-bp-overlay]` 3277 and reads four custom properties per breakpoint emitted by 3278 snippets/bp-overlay-place.liquid: 3279 --bp-overlay-block-{d,m} start | center | end (vertical) 3280 --bp-overlay-inline-{d,m} start | center | end (horizontal) 3281 --bp-overlay-text-{d,m} start | center | end 3282 3283 The element is a column-direction flex container, so the 3284 vertical (block) axis is the MAIN axis (justify-content) and the 3285 horizontal (inline) axis is the CROSS axis (align-items). The 3286 text-align inherits from this container down to descendants. */ 3287 [data-bp-overlay] { 3288 display: flex; 3289 flex-direction: column; 3290 justify-content: var(--bp-overlay-block-m, end); 3291 align-items: var(--bp-overlay-inline-m, center); 3292 } 3293 [data-bp-overlay] > * { 3294 text-align: var(--bp-overlay-text-m, center); 3295 } 3296 @media screen and (min-width: 1024px) { 3297 [data-bp-overlay] { 3298 justify-content: var(--bp-overlay-block-d, center); 3299 align-items: var(--bp-overlay-inline-d, start); 3300 } 3301 [data-bp-overlay] > * { 3302 text-align: var(--bp-overlay-text-d, start); 3303 } 3304 } 3305 3306 /* ============================================================ 3307 Discount Kit (Helium) â Peak Progress GWP bar 3308 Targets the markup the app's embed injects (`#peak-progress-wrapper`). 3309 Restyled to match the BP free-gift bar in the Figma: Messina Sans 3310 typography, BP color tokens, slim 4px track with cream background + 3311 gold fill + a dot indicator at the fill's right edge. 3312 If marketing later swaps in a different DK block (e.g. tiered), 3313 these selectors stop matching gracefully â DK's own styles show 3314 through. 3315 ============================================================ */ 3316 #peak-progress-wrapper { 3317 padding: 16px 0 24px; 3318 border-bottom: 1px solid rgba(23, 22, 9, 0.08); 3319 font-family: var(--bp-font-body); 3320 color: var(--bp-color-text); 3321 } 3322 #peak-progress-wrapper .peak-progress-container { 3323 display: block; 3324 } 3325 #peak-progress-wrapper .peak-progress-message { 3326 display: flex; 3327 justify-content: space-between; 3328 align-items: baseline; 3329 margin-bottom: 8px; 3330 gap: 12px; 3331 } 3332 #peak-progress-wrapper .threshold-title, 3333 #peak-progress-wrapper .threshold-count-message, 3334 #peak-progress-wrapper .threshold-counter { 3335 font-family: var(--bp-font-body); 3336 font-size: 14px; 3337 font-weight: 700; 3338 letter-spacing: -0.14px; 3339 color: var(--bp-color-text); 3340 } 3341 #peak-progress-wrapper .peak-progress-bar-outer { 3342 position: relative; 3343 height: 4px; 3344 background: #E8DFD0; 3345 border-radius: 0; 3346 overflow: visible; 3347 } 3348 #peak-progress-wrapper .peak-progress-bar-inner { 3349 position: relative; 3350 height: 100%; 3351 background: #EBAE55; 3352 border-radius: 0; 3353 transition: width 0.3s ease; 3354 } 3355 /* Dot indicator at the right edge of the fill â matches the BP Figma. 3356 Pinned to the inner bar's end via `right: 0` + `translate(50%, -50%)` 3357 so it tracks the fill width without extra JS. */ 3358 #peak-progress-wrapper .peak-progress-bar-inner::after { 3359 content: ''; 3360 position: absolute; 3361 top: 50%; 3362 right: 0; 3363 transform: translate(50%, -50%); 3364 width: 12px; 3365 height: 12px; 3366 border-radius: 50%; 3367 background: var(--bp-color-bg-page); 3368 border: 2px solid #EBAE55; 3369 box-sizing: border-box; 3370 } 3371 #peak-progress-wrapper .progress-copy { 3372 margin: 12px 0 0; 3373 } 3374 #peak-progress-wrapper .peak-progress-amount { 3375 font-family: var(--bp-font-body); 3376 font-size: 13px; 3377 color: var(--bp-color-body-copy); 3378 } 3379 3380 /* ============================================================ 3381 BP Search Drawer + Search Panel 3382 ============================================================ */ 3383 3384 /* Prevent Horizon's search modal from appearing â bp-search-drawer 3385 always intercepts the trigger and handles search instead (its own JS 3386 branches internally on settings.bp_br_search_enabled to pick 3387 Bloomreach vs. native Shopify results; the container/UI is the same 3388 either way). Target both the custom-element wrapper and the inner dialog. */ 3389 dialog-component#search-modal, 3390 #search-modal dialog { 3391 display: none !important; 3392 } 3393 3394 /* /search page â hide the "Search" / "Search results" title (it stays in 3395 the DOM as the page's a11y <h1>, just visually hidden) and let the 3396 search input span full width, so the page opens with a full-width bar 3397 over the two-column results, matching the topnav search popup. */ 3398 .search-page__header h1 { 3399 position: absolute !important; 3400 width: 1px; 3401 height: 1px; 3402 margin: -1px; 3403 padding: 0; 3404 border: 0; 3405 overflow: hidden; 3406 clip: rect(0 0 0 0); 3407 white-space: nowrap; 3408 } 3409 .search-page__header { 3410 display: block; 3411 } 3412 3413 /* Restyle Horizon's boxed _search-input into the Figma underline bar: 3414 search icon + input on one row, a single bottom rule, full width. */ 3415 .search-page__header .search-page-input__parent { width: 100%; } 3416 .search-page__header search-page-input-component { 3417 max-width: 100% !important; 3418 width: 100%; 3419 display: flex; 3420 align-items: center; 3421 gap: 8px; 3422 margin: 0 0 28px; 3423 padding-block: 10px; 3424 border-bottom: 1px solid var(--bp-color-body-copy, #74736B); 3425 background: transparent; 3426 } 3427 .search-page__header .search-page-input { 3428 border: 0 !important; 3429 border-radius: 0 !important; 3430 background: transparent !important; 3431 padding: 4px 0 !important; 3432 font-family: var(--bp-font-body); 3433 font-size: 16px; 3434 color: var(--bp-color-text); 3435 } 3436 .search-page__header .search-page-input::placeholder { 3437 color: rgba(23, 22, 9, 0.62); 3438 } 3439 /* Icon inline (Horizon positions it absolutely for the boxed look). */ 3440 .search-page__header search-page-input-component .search__icon { 3441 position: static !important; 3442 left: auto !important; 3443 flex-shrink: 0; 3444 width: 44px; 3445 height: 44px; 3446 aspect-ratio: 1 / 1; 3447 color: var(--bp-color-text); 3448 opacity: 0.6; 3449 } 3450 .search-page__header search-page-input-component .search__icon svg { 3451 width: 44px; 3452 height: 44px; 3453 } 3454 /* Keep the clear button pinned to the right edge of the row. */ 3455 .search-page__header search-page-input-component .search__reset-button { 3456 right: 0; 3457 } 3458
3459 .bp-sp { 3460 background: var(--bp-color-bg-page); 3461 padding: 28px var(--bp-section-pad-x) 40px; 3462 } 3463 3464 .bp-sp__body { 3465 display: grid; 3466 grid-template-columns: 200px 1fr; 3467 gap: 48px; 3468 align-items: start; 3469 } 3470 3471 .bp-sp__sidebar { 3472 display: flex; 3473 flex-direction: column; 3474 gap: 24px; 3475 flex-shrink: 0; 3476 } 3477 3478 .bp-sp__group-title { 3479 margin: 0 0 10px; 3480 font-family: var(--bp-font-body); 3481 font-size: 13px; 3482 font-weight: 700; 3483 line-height: 145%; 3484 color: var(--bp-color-text); 3485 } 3486 3487 .bp-sp__links { 3488 list-style: none; 3489 margin: 0; 3490 padding: 0; 3491 display: flex; 3492 flex-direction: column; 3493 gap: 8px; 3494 } 3495 3496 .bp-sp__link { 3497 font-family: var(--bp-font-body); 3498 font-size: 14px; 3499 font-weight: 400; 3500 line-height: 145%; 3501 color: var(--bp-color-text); 3502 text-decoration: none; 3503 } 3504 .bp-sp__link:hover { 3505 text-decoration: underline; 3506 text-underline-offset: 3px; 3507 } 3508 3509 .bp-sp__products-title { 3510 margin: 0 0 16px; 3511 font-family: var(--bp-font-body); 3512 font-size: 13px; 3513 font-weight: 700; 3514 line-height: 145%; 3515 color: var(--bp-color-text); 3516 } 3517 3518 .bp-sp__grid { 3519 list-style: none; 3520 margin: 0; 3521 padding: 0; 3522 display: grid; 3523 /* Tablet 3 per row; desktop 4 (below); mobile 2 (below). 3-up read as too 3524 large on desktop for both the /search results and the topnav search- 3525 drawer suggested results, which share this grid (QA 7/16, Adrienne). */ 3526 grid-template-columns: repeat(3, 1fr); 3527 gap: var(--bp-section-gap); 3528 } 3529 @media screen and (min-width: 1024px) { 3530 .bp-sp__grid { 3531 grid-template-columns: repeat(4, 1fr); 3532 } 3533 } 3534 3535 .bp-sp__item { 3536 min-width: 0; 3537 } 3538 .bp-sp__item > product-card, 3539 .bp-sp__item .bp-card-host { display: contents; } 3540 3541 /* Match the sitewide product-card ratio (3 / 4, see .bp-card__media above). 3542 The old 325 / 513 (~0.634) was taller/narrower than the source art, so 3543 object-fit: cover cropped the sides of every search-drawer thumbnail. */ 3544 .bp-sp__item .bp-card__media { 3545 aspect-ratio: 3 / 4; 3546 height: auto; 3547 } 3548 3549 .bp-sp__footer { 3550 margin-top: 24px; 3551 } 3552 3553 .bp-sp__view-all { 3554 display: inline-flex; 3555 align-items: center; 3556 justify-content: center; 3557 padding: 10px 28px; 3558 font-family: var(--bp-font-body); 3559 font-size: 14px; 3560 font-weight: 500; 3561 line-height: 1; 3562 letter-spacing: 0.01em; 3563 color: var(--bp-color-text); 3564 background: transparent; 3565 border: 1px solid var(--bp-color-text); 3566 cursor: pointer; 3567 text-decoration: none; 3568 transition: background 0.15s ease, color 0.15s ease; 3569 } 3570 .bp-sp__view-all:hover { 3571 background: var(--bp-color-text); 3572 color: var(--bp-color-cta-text, #FDFFFA); 3573 } 3574 3575 @media screen and (max-width: 1023px) { 3576 .bp-sp__body { 3577 grid-template-columns: 180px 1fr; 3578 gap: 32px; 3579 } 3580 } 3581 3582 @media screen and (max-width: 767px) { 3583 .bp-sp { 3584 padding: 20px 16px 32px; 3585 } 3586 .bp-sp__body { 3587 grid-template-columns: 1fr; 3588 gap: 24px; 3589 } 3590 .bp-sp__grid { 3591 grid-template-columns: repeat(2, 1fr); 3592 } 3593 .bp-sp__view-all { 3594 width: 100%; 3595 } 3596 } 3597 3598 /* GTM / ad-network tracking pixels injected into <body> are 1Ã1 3599 images rendered inline, adding ~20px of line-height space below 3600 the footer. Force them out of the normal flow. We target by 3601 domain and by size so legitimate content images are unaffected. 3602 3603 EXCLUSIONS on the size-based selector mirror the earlier rule 3604 (~L37) â the Stylitics "How to Wear It" widget renders each tile 3605 as <img width="1" height="1" class="stylitics-ui-image" 3606 data-testid="stylitics-ui-image" loading="eager"> as a lazy-load 3607 placeholder, then resizes via Stylitics' --max-size CSS variable. 3608 Without these :not() guards the tiles stay pinned at 1Ã1 and the 3609 widget looks empty. */ 3610 img[src*="dstillery.com"], 3611 img[src*="doubleclick.net"], 3612 img[src*="google-analytics.com"], 3613 img[src*="facebook.com/tr"], 3614 img[src*="bat.bing.com"], 3615 img[width="1"][height="1"]:not([class*="stylitics"]):not([data-testid]):not([loading]) { 3616 position: absolute !important; 3617 width: 1px !important; 3618 height: 1px !important; 3619 overflow: hidden !important; 3620 pointer-events: none !important; 3621 } 3622 3623 /* ============================================================ 3624 Narvar (Shipping Protection) â restyle injected buttons + hide 3625 the Narvar product surface. 3626 3627 Narvar's storefront JS injects: 3628 - <button id="CartDrawer-Checkout-DP-With"> primary checkout 3629 - <button id="CartDrawer-Checkout-DP-Without"> secondary "without 3630 shipping protection" 3631 - A product page for the underlying SKU `narvardeliveryprotection` 3632 (shouldn't be browsable as a normal product) 3633 3634 Atlas's CSS used Tailwind utilities (rgb(0 0 0 / opacity), 50px 3635 height, font-weight 600). We rewrite using BP design tokens 3636 (--bp-color-text, --bp-color-cta-text, --bp-font-body, 52px to 3637 match the BP cart drawer's other checkout buttons) so the 3638 Narvar buttons land in the same visual rhythm as the rest of 3639 the BP cart drawer instead of looking like an Atlas/Tailwind 3640 graft. 3641 3642 Atlas reference: Narvar custom CSS pasted by client 2026-05-22. 3643 ============================================================ */ 3644 3645 /* NOTE: previously had a global 3646 div[data-product-handle="narvardeliveryprotection"] 3647 { display: none !important; } 3648 rule here to hide the Narvar SKU from PLP/search. Removed â 3649 our cart line items now (correctly, for Narvar to detect them) 3650 carry data-product-handle, so the rule also hid the Narvar 3651 line ITEM in the cart, breaking checkout. Hide the product 3652 from PLP/search via Shopify product visibility instead 3653 (Admin â Product â Sales channels â uncheck Online Store) 3654 or by leaving it out of every collection. 3655 3656 Now also adding the canonical CartPage-* IDs alongside the 3657 CartDrawer-* ones â Narvar uses different ID prefixes on the 3658 /cart page vs the drawer, so the styling has to cover both. */ 3659 3660 /* Primary checkout button (Narvar injects this in place of the 3661 stock checkout button when the toggle is ON). Match BP's 3662 `.bp-cart-drawer__btn--primary` look. */ 3663 button#CartDrawer-Checkout-DP-With { 3664 height: 52px; 3665 margin: 0; 3666 padding: 0 24px; 3667 background: var(--bp-color-text); 3668 color: var(--bp-color-cta-text); 3669 border: 1px solid var(--bp-color-text); 3670 font-family: var(--bp-font-body); 3671 font-size: 17px; 3672 font-weight: 500; 3673 line-height: normal; 3674 letter-spacing: -0.17px; 3675 text-transform: none; 3676 cursor: pointer; 3677 transition: opacity 0.2s ease, background 0.2s ease; 3678 } 3679 button#CartDrawer-Checkout-DP-With:hover { 3680 opacity: 0.85; 3681 } 3682 3683 /* Narvar's current widget build adds its own stable class 3684 (.narvar-cart-widget-button-checkout) alongside the DP-With id and 3685 ships `background` + text-color rules on that class via a 3686 stylesheet it injects itself (loaded later, after bp-fonts.liquid, 3687 via GTM) â same load-order problem already documented above for 3688 the CTA-casing fix. Out-specify with the bp-cart-drawer ancestor +
3689 !important so BP black always wins regardless of when Narvar's 3690 stylesheet lands, instead of just hoping the ID selector above is 3691 specific enough. */ 3692 bp-cart-drawer button#CartDrawer-Checkout-DP-With, 3693 bp-cart-drawer button.narvar-cart-widget-button-checkout { 3694 background: var(--bp-color-text, #171609) !important; 3695 color: var(--bp-color-cta-text) !important; 3696 } 3697 3698 /* Protection total appended to the primary Checkout button. Narvar 3699 renders a `#checkout-price` span inside its DP-With button on BOTH 3700 the flyout and the /cart page. We previously hid it on the flyout 3701 only (`bp-cart-drawer #checkout-price { display:none }`), per an 3702 earlier "design doesn't show the price" note â but the /cart page 3703 was never scoped by that rule, so it always showed the total. That 3704 left the two surfaces inconsistent: page had the total, flyout did 3705 not. QA 7/27 (Dragana) asks the flyout to match the page, so the 3706 hide is removed and the total (subtotal + protection) shows on the 3707 checkout button on both surfaces. */ 3708 3709 /* Secondary "Checkout without shipping protection" button. Match 3710 BP's `.bp-cart-drawer__btn--secondary` look â same height, BP 3711 typography, dark outline on cream/white surface. */ 3712 button#CartDrawer-Checkout-DP-Without { 3713 height: 52px; 3714 margin: 0; 3715 padding: 0 24px; 3716 background: transparent; 3717 color: var(--bp-color-text); 3718 border: 1px solid var(--bp-color-text); 3719 font-family: var(--bp-font-body); 3720 font-size: 17px; 3721 font-weight: 500; 3722 line-height: normal; 3723 letter-spacing: -0.17px; 3724 text-transform: none; 3725 cursor: pointer; 3726 transition: background 0.2s ease, color 0.2s ease; 3727 } 3728 button#CartDrawer-Checkout-DP-Without:hover { 3729 background: var(--bp-color-bg-cream); 3730 } 3731 3732 /* Same buttons but on the /cart page â Narvar uses CartPage-* IDs 3733 there. Mirror the styling so the cart page looks consistent with 3734 the drawer. */ 3735 button#CartPage-Checkout-DP-With { 3736 height: 52px; 3737 margin: 0; 3738 padding: 0 24px; 3739 background: var(--bp-color-text); 3740 color: var(--bp-color-cta-text); 3741 border: 1px solid var(--bp-color-text); 3742 font-family: var(--bp-font-body); 3743 font-size: 17px; 3744 font-weight: 500; 3745 line-height: normal; 3746 letter-spacing: -0.17px; 3747 cursor: pointer; 3748 transition: opacity 0.2s ease; 3749 } 3750 button#CartPage-Checkout-DP-With:hover { 3751 opacity: 0.85; 3752 } 3753 button#CartPage-Checkout-DP-Without { 3754 height: 52px; 3755 margin: 0; 3756 padding: 0 24px; 3757 background: transparent; 3758 color: var(--bp-color-text); 3759 border: 1px solid var(--bp-color-text); 3760 font-family: var(--bp-font-body); 3761 font-size: 17px; 3762 font-weight: 500; 3763 line-height: normal; 3764 letter-spacing: -0.17px; 3765 cursor: pointer; 3766 transition: background 0.2s ease; 3767 } 3768 button#CartPage-Checkout-DP-Without:hover { 3769 background: var(--bp-color-bg-cream); 3770 } 3771 3772 /* Narvar widget mount â collapse margin until Narvar populates it, 3773 so the empty <div> doesn't leave a phantom gap above the 3774 subtotal block. `:not(:empty)` keeps the natural spacing once 3775 Narvar's React renders content into the mount. */ 3776 .bp-cart-drawer__narvar-widget:not(:empty) { 3777 margin: 0; 3778 } 3779 3780 /* ---- Shipping Protection loading skeleton (QA 7/27, Dragana) ---- 3781 Narvar injects the real cream box a few seconds after the flyout opens / 3782 the cart page loads (its async bundle). This shimmer placeholder holds the 3783 box's shape until then, so there's no empty-whitespace gap that then pops. 3784 It mimics `.narvar-cart-widget-card` (cream, 8px padding, column, ~64px). 3785 Hidden the instant Narvar's real widget lands in the adjacent mount 3786 (`:has(+ mount > *)`) and when the JS marks it done / times out. 3787 Supersedes the earlier reserve-space approach (#307), which read as an 3788 empty whitespace gap. */ 3789 .bp-cart-drawer__sp-skeleton, 3790 .bp-cart-page__sp-skeleton { 3791 display: flex; 3792 flex-direction: column; 3793 gap: 8px; 3794 padding: 8px; 3795 min-height: 64px; 3796 box-sizing: border-box; 3797 background: var(--bp-color-bg-cream, #F7F6EA); 3798 border-radius: 0; 3799 } 3800 .bp-cart-drawer__sp-skeleton:has(+ .bp-cart-drawer__narvar-widget > *), 3801 .bp-cart-page__sp-skeleton:has(+ .bp-cart-page__narvar-widget > *), 3802 .bp-sp-skeleton--hidden { 3803 display: none !important; 3804 } 3805 .bp-cart-drawer__sp-skeleton-head, 3806 .bp-cart-page__sp-skeleton-head { 3807 display: flex; 3808 align-items: center; 3809 justify-content: space-between; 3810 gap: 8px; 3811 }
3812 .bp-sp-skeleton-bar { 3813 display: block; 3814 height: 12px; 3815 border-radius: 2px; 3816 background: linear-gradient( 3817 90deg, 3818 rgba(23, 22, 9, 0.06) 25%, 3819 rgba(23, 22, 9, 0.12) 37%, 3820 rgba(23, 22, 9, 0.06) 63% 3821 ); 3822 background-size: 400% 100%; 3823 animation: bpSpShimmer 1.4s ease infinite; 3824 } 3825 .bp-cart-drawer__sp-skeleton-title, 3826 .bp-cart-page__sp-skeleton-title { width: 128px; height: 14px; } 3827 .bp-cart-drawer__sp-skeleton-amt, 3828 .bp-cart-page__sp-skeleton-amt { width: 44px; height: 14px; } 3829 .bp-cart-drawer__sp-skeleton-desc, 3830 .bp-cart-page__sp-skeleton-desc { width: 82%; height: 10px; } 3831 @keyframes bpSpShimmer { 3832 0% { background-position: 100% 50%; } 3833 100% { background-position: 0 50%; } 3834 } 3835 @media (prefers-reduced-motion: reduce) { 3836 .bp-sp-skeleton-bar { animation: none; } 3837 } 3838 3839 /* Shipping Protection â skin Narvar's NATIVE widget as the BP cream box 3840 (design parity). Narvar owns the price + copy + opt-out logic; we only 3841 style its card. Key off Narvar's STABLE classes (narvar-cart-widget-*) 3842 only â the _*_196oa_* hashes change on every app version. */ 3843 .narvar-cart-widget-card { 3844 background: var(--bp-color-bg-cream, #F7F6EA) !important; 3845 /* Figma parity (QA 7/23, Brooke): 8px padding + flex container. 3846 Direct children are Narvar's header row (title + amount) and the 3847 description â they MUST stack, so column direction is required; 3848 row (the flex default) would sit the description beside the 3849 header. Narvar owns the header's own internal left/right layout 3850 via its hashed classes; we don't touch it. */ 3851 display: flex !important; 3852 flex-direction: column !important; 3853 padding: 8px !important; 3854 gap: 8px !important; 3855 justify-content: center !important; 3856 /* stretch (not flex-start) so the direct children fill the row â 3857 Narvar's header row needs full width for its own space-between to 3858 push the amount to the right. Setting width:100% on the header 3859 directly isn't an option: its class (_196oa_) is a per-version 3860 hash. Text stays left-aligned either way. */ 3861 align-items: stretch !important; 3862 align-self: stretch !important; 3863 color: var(--bp-color-text, #171609) !important; 3864 border-radius: 0 !important; 3865 } 3866 /* Force BP black on ALL widget text â Narvar's --color-accent (blue) leaks 3867 into nested spans (the amount + title inner spans). */ 3868 .narvar-cart-widget-card, 3869 .narvar-cart-widget-card * { 3870 color: var(--bp-color-text, #171609) !important; 3871 } 3872 /* The design shows just the "Shipping Protection" label + info icon â 3873 hide Narvar's UPS/InsureShield logo image IN THE CARD only (the info 3874 modal shares the .narvar-cart-widget-logo class and keeps its hero logo). */ 3875 .narvar-cart-widget-card .narvar-cart-widget-logo { 3876 display: none !important; 3877 } 3878 .narvar-cart-widget-title { 3879 /* Heading / H6 â Signifier */ 3880 font-family: var(--bp-font-display) !important; 3881 font-size: 14px !important; 3882 font-weight: 300 !important; 3883 line-height: 120% !important; 3884 letter-spacing: -0.28px !important; 3885 color: var(--bp-color-text, #171609) !important; 3886 } 3887 .narvar-cart-widget-amount, 3888 .narvar-cart-widget-amount span { 3889 /* Body / Book / Small â Messina, right-aligned */ 3890 font-family: var(--bp-font-body) !important; 3891 font-size: 14px !important; 3892 font-weight: 400 !important; 3893 letter-spacing: -0.14px !important; 3894 color: var(--bp-color-text, #171609) !important; 3895 } 3896 .narvar-cart-widget-description { 3897 /* Body / Book / Tiny â Messina */ 3898 font-family: var(--bp-font-body) !important; 3899 font-size: 10px !important; 3900 font-weight: 400 !important; 3901 line-height: 145% !important; 3902 letter-spacing: -0.1px !important; 3903 color: var(--bp-color-text, #171609) !important; 3904 margin-top: -12px; 3905 } 3906 /* Mobile: pull the description up tight under the header (QA 7/23, Brooke). */ 3907 @media screen and (max-width: 749px) { 3908 .narvar-cart-widget-description { 3909 margin-top: -16px !important; 3910 } 3911 } 3912 /* Info icon â BP black (Narvar defaults to a blue accent) + clickable 3913 (the drawer closes on click so Narvar's info surface isn't clipped). */ 3914 .narvar-cart-widget-info-icon { 3915 fill: var(--bp-color-text, #171609) !important; 3916 width: 13px !important; 3917 height: 13px !important; 3918 cursor: pointer; 3919 } 3920 3921 /* ---- Narvar Shipping Protection info modal ("Peace of mind, delivered"). 3922 Narvar injects it with minimal/broken styles; skin it with BP tokens. 3923 Stable IDs (#insurance-modal-*) are reliable â the _*_1mgdm_* hashes churn 3924 per app version. Shown when the widget info icon is clicked (the cart 3925 drawer closes so this isn't clipped). ---- */ 3926 #insurance-modal-container { 3927 position: fixed; 3928 inset: 0; 3929 width: 100vw; 3930 z-index: 9999999999; 3931 display: flex; 3932 align-items: center; 3933 justify-content: center; 3934 padding: 16px; 3935 background: rgba(23, 22, 9, 0.45); 3936 } 3937 #insurance-modal-content { 3938 position: relative; 3939 width: 100%; 3940 max-width: 420px; 3941 max-height: 88vh; 3942 overflow-y: auto; 3943 background: #FDFFFA; 3944 color: var(--bp-color-text, #171609); 3945 font-family: var(--bp-font-body); 3946 padding: 32px 24px 24px; 3947 } 3948 #insurance-modal-header { 3949 position: relative; 3950 text-align: center; 3951 margin-bottom: 20px; 3952 } 3953 #insurance-modal-icon-wrapper { 3954 display: flex; 3955 justify-content: center; 3956 margin-bottom: 12px; 3957 } 3958 #insurance-modal-icon-image { 3959 width: auto !important; 3960 height: 40px !important; 3961 } 3962 #insurance-modal-hero-title { 3963 font-family: var(--bp-font-display); 3964 font-size: 22px; 3965 font-weight: 300; 3966 line-height: 120%; 3967 letter-spacing: -0.44px; 3968 margin: 0; 3969 color: var(--bp-color-text, #171609); 3970 } 3971 #insurance-modal-close-button { 3972 position: absolute; 3973 top: -14px; 3974 right: -8px; 3975 background: transparent; 3976 border: 0; 3977 padding: 4px; 3978 line-height: 0; 3979 cursor: pointer; 3980 } 3981 #insurance-modal-features-list { 3982 list-style: none; 3983 padding: 0; 3984 margin: 0 0 4px; 3985 } 3986 #insurance-modal-features-list li { margin: 0 0 14px; } 3987 [id^="insurance-modal-feature-"][id$="-title"] { 3988 font-family: var(--bp-font-body); 3989 font-size: 14px; 3990 font-weight: 600; 3991 color: var(--bp-color-text, #171609); 3992 } 3993 [id^="insurance-modal-feature-"][id$="-text"] { 3994 font-family: var(--bp-font-body); 3995 font-size: 13px; 3996 line-height: 145%; 3997 color: var(--bp-color-text, #171609); 3998 } 3999 #insurance-modal-content hr { 4000 border: 0; 4001 border-top: 1px solid rgba(23, 22, 9, 0.15); 4002 margin: 16px 0; 4003 } 4004 #insurance-modal-body-text, 4005 #insurance-modal-footer { 4006 font-family: var(--bp-font-body); 4007 font-size: 11px; 4008 line-height: 150%; 4009 color: var(--bp-color-body-copy, #74736B); 4010 margin: 0; 4011 } 4012 #insurance-modal-footer a { color: inherit; text-decoration: underline; }
4013 #insurance-modal-powered-by { align-items: center; } 4014 /* The "Powered by Narvar Secure" logo has no width/height attr, so a global 4015 img rule was stretching it â pin it to its intrinsic 16px. */ 4016 #insurance-modal-powered-by-icon { 4017 width: 16px !important; 4018 height: 16px !important; 4019 flex: 0 0 16px; 4020 } 4021 4022 /* Narvar's toggle-card shell ships with a built-in `margin: 1rem 0` 4023 from its CSS module (hashed class `_card_196oa_1`, which changes 4024 on every Narvar extension bump). We target the stable widget-style 4025 class instead â `.opt-out-button` is the class Narvar's React 4026 applies to the card root when its widget_style setting is the 4027 opt-out button variant (matches what BP uses). Zero out the 4028 margin so our drawer's own `gap` controls spacing. 4029 4030 If a future widget-style switch is made (toggle / checkbox / 4031 opt-in-button / add-button / flyout), add the corresponding 4032 class here â see Narvar's `C` constant for the full list. */ 4033 .opt-out-button { 4034 margin: 0 !important; 4035 } 4036 4037 /* Spacing between Narvar's primary (DP-With) and secondary 4038 (DP-Without) injected buttons. Narvar's React inlines 4039 `style="margin: 1rem 0"` on the DP-Without button; force a 4040 top-only gap so the buttons sit flush against the form 4041 bottom but still breathe between each other. Applied to both 4042 surfaces (drawer + cart page). */ 4043 button#CartDrawer-Checkout-DP-Without, 4044 button#CartPage-Checkout-DP-Without { 4045 margin: 0.5rem 0 0 !important; 4046 } 4047 /* Mobile: tighter 4px gap between the primary + secondary Narvar 4048 buttons (half the desktop value, same ratio as before â was 8px 4049 over 1rem/16px). Kept directly after the 0.5rem rule above â equal 4050 specificity + !important on both means source order decides, so 4051 this MUST come last to win on mobile. */ 4052 @media screen and (max-width: 749px) { 4053 button#CartDrawer-Checkout-DP-Without, 4054 button#CartPage-Checkout-DP-Without { 4055 margin: 4px 0 0 !important; 4056 } 4057 } 4058 4059 /* CTA casing (QA 7/14) â Narvar's injected checkout buttons should read 4060 Title Case ("Checkout Without Shipping Protection"), matching the native 4061 "Checkout" / "View Bag" CTAs and the design. The Narvar app's custom_css 4062 (config/settings_data.json) forces text-transform: uppercase at the same 4063 `button#id` specificity as bp-fonts, so load order decided it â hence the 4064 inconsistent casing. Out-specify by scoping to the BP cart wrappers 4065 (bp-cart-drawer / bp-cart-page â higher specificity) + !important so BP 4066 always wins. `capitalize` (not `none`) because Narvar's own label text is 4067 lower/upper-cased, so capitalize is what yields the Title-Case look. */ 4068 bp-cart-drawer button#CartDrawer-Checkout-DP-With, 4069 bp-cart-drawer button#CartDrawer-Checkout-DP-Without, 4070 bp-cart-page button#CartPage-Checkout-DP-With, 4071 bp-cart-page button#CartPage-Checkout-DP-Without { 4072 text-transform: capitalize !important; 4073 } 4074 4075 /* ============================================================ 4076 BP hero slide + shop-by-category link styles â moved here 4077 from sections/blocks during the block migration. 4078 4079 Why: Shopify's per-block / per-section stylesheet loading 4080 proved unreliable for these specific markup patterns when 4081 the section was migrated from inline section-defined blocks 4082 to theme blocks. Putting the rules in the global bp-fonts 4083 style block guarantees they apply regardless of which 4084 section/block stylesheet Shopify chooses to load. 4085 4086 References: 4087 - blocks/_bp-hero-slide.liquid (rendered article markup) 4088 - blocks/_bp-shop-link.liquid (rendered <li> markup) 4089 - sections/bp-hero-slider.liquid (wrapper + nav + dots) 4090 - sections/bp-shop-by-category.liquid (heading + <ul>) 4091 ============================================================ */ 4092 4093 /* Shopify wraps every rendered theme block in a 4094 <div class="shopify-block" data-shopify-editor-block="..."> 4095 wrapper for theme-editor click-to-edit. That wrapper breaks 4096 direct-child layouts (the slide track's grid, the link list's 4097 flex row, etc.) because the slide/list-item is no longer a 4098 direct child of the parent â it sits inside the wrapper. 4099 4100 `display: contents` makes the wrapper layout-transparent: its 4101 children behave as if they were direct children of the 4102 parent for layout purposes (grid items, flex items). Block 4103 identity for the editor stays â the wrapper still exists in 4104 the DOM and carries data-shopify-editor-block. */ 4105 .bp-hero__track > .shopify-block, 4106 .bp-hero__content-inner > .shopify-block, 4107 .bp-shop-by-category__list > .shopify-block { 4108 display: contents; 4109 } 4110 4111 /* ----- bp-hero-slide ----- */ 4112 4113 .bp-hero__slide { 4114 grid-column: 1 / 2; 4115 grid-row: 1 / 2; 4116 position: relative; 4117 display: block; 4118 width: 100%; 4119 max-width: 100vw;
4120 min-height: var(--bp-hero-min-height-mobile); 4121 aspect-ratio: var(--bp-hero-ratio-m, auto); 4122 color: var(--bp-hero-text-color); 4123 4124 opacity: 0; 4125 visibility: hidden; 4126 pointer-events: none; 4127 transition: opacity 0.8s cubic-bezier(0.4, 0, 0.2, 1), 4128 visibility 0s linear 0.8s; 4129 } 4130 4131 bp-hero-slider[data-has-ratio-m='true'] .bp-hero__slide { 4132 display: flex; 4133 flex-direction: column; 4134 /* Unset the min-height so the slide's own aspect-ratio (set on 4135 .bp-hero__slide above) drives its height on mobile. A min-height 4136 floor here (e.g. max(50vh, max-content)) overrode the ratio and 4137 pinned every slide to ~50vh, so the picked "Aspect ratio (mobile)" 4138 had no visible effect. Copy taller than the ratio still grows the 4139 slide via the flex column's max-content. */ 4140 min-height: unset; 4141 } 4142 bp-hero-slider[data-has-ratio-m='true'] .bp-hero__content { 4143 flex: 1; 4144 position: relative; 4145 width: 100%; 4146 min-height: auto; 4147 } 4148 @media screen and (min-width: 1024px) { 4149 bp-hero-slider[data-has-ratio-d='true'] .bp-hero__slide { 4150 display: flex; 4151 flex-direction: column; 4152 min-height: max-content; 4153 } 4154 bp-hero-slider[data-has-ratio-d='true'] .bp-hero__content { 4155 flex: 1; 4156 position: relative; 4157 width: 100%; 4158 min-height: auto; 4159 } 4160 } 4161 4162 .bp-hero__slide.is-active { 4163 opacity: 1; 4164 visibility: visible; 4165 pointer-events: auto; 4166 transition: opacity 0.8s cubic-bezier(0.4, 0, 0.2, 1), 4167 visibility 0s linear 0s; 4168 } 4169 4170 @media (prefers-reduced-motion: reduce) { 4171 .bp-hero__slide, 4172 .bp-hero__slide.is-active { 4173 transition: none; 4174 } 4175 /* a11y (WCAG 2.3.3): neutralise every smooth-scroll rail site-wide in 4176 one place â the BP carousels (product-carousel, category-tiles, 4177 shop-the-look, tabbed-gallery, our-most-loved, cart, etc.) each set 4178 `scroll-behavior: smooth` locally. This overrides them all for users 4179 who prefer reduced motion, instead of guarding 13 files. */ 4180 html:focus-within, 4181 *, 4182 *::before, 4183 *::after { 4184 scroll-behavior: auto !important; 4185 } 4186 } 4187 4188 .bp-hero__media { 4189 position: absolute; 4190 inset: 0; 4191 width: 100%; 4192 height: 100%; 4193 overflow: hidden; 4194 background: var(--color-background-muted, #f3f3f0); 4195 z-index: 0; 4196 } 4197 4198 .bp-hero__picture { 4199 position: absolute; 4200 inset: 0; 4201 width: 100%; 4202 height: 100%; 4203 display: block; 4204 } 4205 4206 .bp-hero__image { 4207 position: absolute; 4208 inset: 0; 4209 width: 100%; 4210 height: 100%; 4211 object-fit: cover; 4212 object-position: var(--bp-hero-img-pos-m); 4213 display: block; 4214 } 4215 @media screen and (min-width: 1024px) { 4216 .bp-hero__image { 4217 object-position: var(--bp-hero-img-pos-d); 4218 } 4219 } 4220 4221 .bp-hero__image--placeholder { 4222 background: linear-gradient(135deg, #e8e6df 0%, #cfccc1 100%); 4223 } 4224 4225 .bp-hero__content { 4226 position: relative; 4227 z-index: 1; 4228 flex-direction: column; 4229 min-height: var(--bp-hero-min-height-mobile); 4230 padding: var(--bp-hero-content-padding-mobile); 4231 padding-bottom: 88px; 4232 gap: 16px; 4233 color: var(--bp-hero-text-color); 4234 } 4235 4236 .bp-hero__content-inner { 4237 display: flex; 4238 flex-direction: column; 4239 gap: 16px; 4240 width: 100%; 4241 max-width: var(--bp-hero-content-max-mobile); 4242 align-items: var(--bp-overlay-inline-m, start); 4243 } 4244 @media screen and (min-width: 1024px) { 4245 .bp-hero__content-inner { 4246 align-items: var(--bp-overlay-inline-d, start); 4247 } 4248 } 4249 4250 .bp-hero__eyebrow { 4251 margin: 0; 4252 color: var(--bp-hero-text-color); 4253 font-family: var(--bp-font-body); 4254 font-size: 17px; 4255 font-style: normal; 4256 font-weight: 500; 4257 line-height: 110%; 4258 letter-spacing: -0.17px; 4259 text-transform: uppercase; 4260 } 4261 4262 .bp-hero__title { 4263 margin: 0; 4264 color: var(--bp-hero-text-color); 4265 font-family: var(--bp-font-display); 4266 font-size: clamp(36px, 6vw, 50px); 4267 font-style: normal; 4268 font-weight: 300; 4269 line-height: 120%; 4270 letter-spacing: -1px; 4271 } 4272 4273 .bp-hero__description { 4274 margin: 0; 4275 color: var(--bp-hero-text-color); 4276 font-family: var(--bp-font-body); 4277 font-size: 17px; 4278 font-style: normal; 4279 font-weight: 500; 4280 line-height: 145%; 4281 letter-spacing: -0.17px; 4282 } 4283 .bp-hero__description > * { 4284 margin: 0; 4285 color: inherit; 4286 font: inherit; 4287 letter-spacing: inherit; 4288 } 4289 .bp-hero__description > * + * { 4290 margin-top: 8px; 4291 } 4292 4293 .bp-hero__ctas { 4294 margin-top: 16px; 4295 width: 100%; 4296 align-items: var(--bp-overlay-inline-m, start); 4297 justify-content: var(--bp-overlay-inline-m, start); 4298 } 4299 4300 .bp-hero__cta.bp-cta { 4301 border-color: var(--bp-hero-text-color); 4302 color: #000; 4303 } 4304 .bp-hero__cta.bp-cta--primary { 4305 background: var(--bp-color-text); 4306 color: #fff; 4307 } 4308 .bp-hero__cta.bp-cta--primary:hover { 4309 background: transparent; 4310 color: var(--bp-hero-text-color); 4311 } 4312 .bp-hero__cta.bp-cta--primary-light { 4313 background: var(--bp-color-bg-page); 4314 color: var(--bp-color-text); 4315 border-color: #fff; 4316 } 4317 .bp-hero__cta.bp-cta--primary-light:hover { 4318 background: transparent; 4319 color: var(--bp-hero-text-color); 4320 border-color: var(--bp-hero-text-color); 4321 } 4322 .bp-hero__cta.bp-cta--secondary:hover { 4323 background: var(--bp-hero-text-color); 4324 color: #fff; 4325 } 4326 4327 @media screen and (max-width: 480px) { 4328 .bp-hero__content { 4329 padding-bottom: 24px; 4330 } 4331 } 4332 4333 @media screen and (max-width: 749px) { 4334 .bp-hero__title { 4335 font-size: clamp(24px, 6vw, 36px); 4336 letter-spacing: -0.5px; 4337 } 4338 } 4339 4340 @media screen and (min-width: 1024px) { 4341 .bp-hero__slide {
4342 min-height: var(--bp-hero-min-height-desktop); 4343 aspect-ratio: var(--bp-hero-ratio-d, auto); 4344 } 4345 4346 .bp-hero__content { 4347 min-height: var(--bp-hero-min-height-desktop); 4348 padding: var(--bp-hero-content-padding-desktop); 4349 padding-bottom: var(--bp-hero-content-padding-desktop); 4350 } 4351 4352 .bp-hero__content-inner { 4353 max-width: none; 4354 gap: 20px; 4355 } 4356 .bp-hero__description { 4357 max-width: var(--bp-hero-content-max-desktop); 4358 } 4359 4360 .bp-hero__ctas { 4361 width: 100%; 4362 margin-top: 24px; 4363 align-items: var(--bp-overlay-inline-d, start); 4364 justify-content: var(--bp-overlay-inline-d, start); 4365 } 4366 4367 .bp-hero__cta.bp-cta { 4368 min-width: 140px; 4369 } 4370 } 4371 4372 /* ----- bp-shop-by-category link ----- */ 4373 4374 .bp-shop-by-category__item { 4375 flex-shrink: 0; 4376 } 4377 4378 .bp-shop-by-category__link { 4379 display: inline-block; 4380 color: inherit; 4381 text-decoration: underline; 4382 text-underline-offset: 4px; 4383 font-family: var(--bp-font-body); 4384 font-size: 17px; 4385 font-style: normal; 4386 font-weight: 500; 4387 line-height: normal; 4388 letter-spacing: -0.17px; 4389 transition: opacity 0.2s ease; 4390 } 4391 .bp-shop-by-category__link:hover { opacity: 0.65; } 4392 4393 /* ============================================================ 4394 PAGE.CAREERS / PAGE.ABOUTUS â default body-copy color 4395 ------------------------------------------------------------ 4396 Body-style bp-text blocks default to var(--bp-color-body-copy, 4397 #74736B). Overrides the FINAL fallback in bp-text's color chain 4398 (`color: var(--bp-text-color, var(--bp-knobs-text, 4399 var(--bp-color-text))))` â not the `color` property itself â 4400 so a merchant-set text_color on the block (which writes 4401 --bp-text-color inline) STILL WINS. 4402 4403 Scoped to .bp-text--ff-body so display-family bp-text (h1-h6 4404 using Signifier) keeps the dark --bp-color-text default. 4405 ============================================================ */ 4406 main[data-template='page.careers'] .bp-text--ff-body, 4407 main[data-template='page.aboutus'] .bp-text--ff-body, 4408 main[data-template='page.sherylsloves'] .bp-text--ff-body, 4409 main[data-template='page.prospect-introduction'] .bp-text--ff-body { 4410 --bp-color-text: var(--bp-color-body-copy, #74736B); 4411 } 4412 4413 /* /pages/customer-care â hero (bp-image-text-banner) keeps the 32px 4414 mobile top padding but drops bottom padding to 0 so the link 4415 grid below sits flush against the hero. The §9.1 bp_padding_y_m 4416 knob is a single value (block top+bottom); the override below 4417 zeros only padding-block-end. */ 4418 @media screen and (max-width: 1023px) { 4419 main[data-template='page.customer-care'] [data-section-id$='__hero'] { 4420 padding-block-end: 0 !important; 4421 } 4422 } 4423 4424 /* /pages/prospect-introduction â imagery grid content-tile spec. 4425 The Figma frame is 40px padding / 10px gap / flex-column / 4426 flex-start / stretch on the inner copy stack. Default §9.1.x 4427 tile-content uses 24Ã16 padding and 8px gap; override here. */ 4428 main[data-template='page.prospect-introduction'] [data-section-id$='__bp_intro_imagery_grid'] .bp-grid__tile-content { 4429 padding: 40px !important; 4430 } 4431 main[data-template='page.prospect-introduction'] [data-section-id$='__bp_intro_imagery_grid'] .bp-grid__tile-content-inner { 4432 display: flex !important; 4433 flex-direction: column !important; 4434 align-items: flex-start !important; 4435 gap: 10px !important; 4436 align-self: stretch !important; 4437 } 4438 4439 /* /pages/brands-we-love â intro section spec. 4440 The section stays full-bleed (bp_bg_section cream extends to 4441 viewport edges) and uses its natural stacked layout. The 4442 overrides just dial the vertical padding to 80/40 and zero 4443 the bp-fc__inner padding + min-height that were stacking on 4444 top of the §9.1 section padding. */ 4445 html body [data-section-id$='__bwl_intro'], 4446 html body [data-section-id='bwl_intro'] { 4447 padding-block: 80px 40px !important; 4448 } 4449 html body [data-section-id$='__bwl_intro'] .bp-fc__inner, 4450 html body [data-section-id='bwl_intro'] .bp-fc__inner { 4451 padding: 0 !important; 4452 min-height: 0 !important; 4453 gap: 0 !important; 4454 } 4455 html body [data-section-id$='__bwl_intro'] .bp-fc__features:empty, 4456 html body [data-section-id='bwl_intro'] .bp-fc__features:empty { 4457 display: none !important; 4458 } 4459 @media (max-width: 1023px) { 4460 html body [data-section-id$='__bwl_intro'], 4461 html body [data-section-id='bwl_intro'] { 4462 padding-block: 40px 24px !important; 4463 } 4464 } 4465 4466 /* /pages/brands-we-love â brand-grid tile titles match Figma H4: 4467 24/300/Signifier/120%/-0.48px, white. The global .bp-fs-h4 sets 4468 125% / -0.01em; this scoped rule tightens line-height + letter- 4469 spacing to the spec exactly. The H4 preset on bp-text already 4470 supplies size 24 + tag h4; per-instance JSON overrides 4471 (font_family: display, font_weight: 300) carry the family/weight. */ 4472 html body [data-section-id$='__bwl_grid'] .bp-grid__tile .bp-text.bp-fs-h4, 4473 html body [data-section-id='bwl_grid'] .bp-grid__tile .bp-text.bp-fs-h4 { 4474 line-height: 120% !important; 4475 letter-spacing: -0.48px !important; 4476 } 4477 4478 /* /pages/brands-we-love â hero "big one" CTA forced to left. 4479 The single Shop Now CTA must hug the left edge of the slide on 4480 both breakpoints. Forces the group's main-axis to flex-start 4481 AND defeats the §9.2 mobile stretch rule 4482 ([data-bp-cta-group]:not([data-dir-m='row']) .bp-block-cta { width: 100% }) 4483 which was making the button full-width on mobile and centering 4484 the label via the button's own justify-content: center. */ 4485 html body [data-section-id$='__bwl_hero'] .bp-hero__content [data-bp-cta-group], 4486 html body [data-section-id='bwl_hero'] .bp-hero__content [data-bp-cta-group] { 4487 justify-content: flex-start !important;
4488 align-items: flex-start !important; 4489 } 4490 html body [data-section-id$='__bwl_hero'] .bp-hero__content [data-bp-cta-group] .bp-block-cta, 4491 html body [data-section-id$='__bwl_hero'] .bp-hero__content [data-bp-cta-group] .bp-cta, 4492 html body [data-section-id='bwl_hero'] .bp-hero__content [data-bp-cta-group] .bp-block-cta, 4493 html body [data-section-id='bwl_hero'] .bp-hero__content [data-bp-cta-group] .bp-cta { 4494 width: auto !important; 4495 margin-right: auto !important; 4496 align-self: flex-start !important; 4497 } 4498 4499 /* ============================================================ 4500 YOTPO WIDGET â BP typography overrides 4501 ============================================================ 4502 Yotpo injects its widget via tf-integration / yotpo-loader 4503 scripts and inlines its own font (Futura-PT / Open Sans) on 4504 the rendered elements. Force the BP body family on every 4505 Yotpo text node so reviews + Q&A match the rest of the site. 4506 The review BODY copy (.yotpo-read-more-text + 4507 .yotpo-comment-content retailer reply) additionally gets the 4508 Figma "Body/XSmall Book" spec â Messina 12 / 400 / 145% / 4509 -0.12 / body-copy gray. */ 4510 .yotpo-widget-instance, 4511 .yotpo-widget-instance *, 4512 .yotpo-reviews-main-widget, 4513 .yotpo-reviews-main-widget *, 4514 .yotpo-reviews-star-ratings-widget, 4515 .yotpo-reviews-star-ratings-widget * { 4516 font-family: var(--bp-font-body), 'Messina Sans VF', system-ui, -apple-system, sans-serif !important; 4517 } 4518 .yotpo-widget-instance .yotpo-read-more-text, 4519 .yotpo-widget-instance .yotpo-text-container, 4520 .yotpo-widget-instance .yotpo-comment-content { 4521 color: var(--bp-color-body-copy, #74736B) !important; 4522 font-family: var(--bp-font-body), 'Messina Sans VF', system-ui, sans-serif !important; 4523 font-size: 12px !important; 4524 font-style: normal !important; 4525 font-weight: 400 !important; 4526 line-height: 145% !important; 4527 letter-spacing: -0.12px !important; 4528 } 4529 /* Bold title elements â review headline + retailer reply heading. 4530 Figma "Body/Semi/Small" spec: Messina 14 / 660 / 145% / -0.14 4531 in BP black (#171609). */ 4532 .yotpo-widget-instance .yotpo-review-title, 4533 .yotpo-widget-instance .yotpo-comment-title { 4534 color: var(--bp-color-text, #171609) !important; 4535 font-family: var(--bp-font-body), 'Messina Sans VF', system-ui, sans-serif !important; 4536 font-size: 14px !important; 4537 font-style: normal !important; 4538 font-weight: 700 !important; 4539 line-height: 145% !important; 4540 letter-spacing: -0.14px !important; 4541 } 4542 4543 /* "With media" toggle â Yotpo renders a button > label > SVG-circle 4544 stack that flexes weirdly under the BP font override (label 4545 becomes block-level, SVG drops below the text). Pin the button 4546 to flex/row + 8px gap, keep the SVG inline. When aria-pressed, 4547 fill the circle so the selected state reads. */ 4548 .yotpo-widget-instance .yotpo-media-select { 4549 display: inline-flex !important; 4550 flex-direction: row !important; 4551 align-items: center !important; 4552 gap: 8px !important; 4553 background: transparent !important; 4554 border: 1px solid rgba(23, 22, 9, 0.18) !important; 4555 border-radius: 0 !important; 4556 padding: 10px 12px !important; 4557 cursor: pointer !important; 4558 color: var(--bp-color-text, #171609) !important; 4559 font-family: var(--bp-font-body), 'Messina Sans VF', system-ui, sans-serif !important; 4560 font-size: 14px !important; 4561 font-weight: 500 !important; 4562 line-height: 145% !important; 4563 letter-spacing: -0.14px !important; 4564 } 4565 .yotpo-widget-instance .yotpo-media-select .yotpo-radio-base { 4566 display: inline-flex !important; 4567 align-items: center !important; 4568 flex: 1 1 auto !important; 4569 margin: 0 !important; 4570 padding: 0 !important; 4571 cursor: pointer !important; 4572 } 4573 .yotpo-widget-instance .yotpo-media-select .yotpo-selected-value { 4574 color: inherit !important; 4575 line-height: inherit !important; 4576 } 4577 .yotpo-widget-instance .yotpo-media-select .yotpo-radio-icon { 4578 flex: 0 0 auto !important; 4579 width: 16px !important; 4580 height: 16px !important; 4581 display: block !important; 4582 } 4583 .yotpo-widget-instance .yotpo-media-select[aria-pressed='true'] .yotpo-radio-icon circle { 4584 fill: var(--bp-color-text, #171609) !important; 4585 stroke: var(--bp-color-text, #171609) !important; 4586 } 4587 4588 /* ââ Header scroll-logo swap âââââââââââââââââââââââââââââââââââââ 4589 Swaps the header logo to the dedicated "scroll" variant
4590 (settings.logo_scroll, rendered in blocks/_header-logo.liquid) 4591 when the nav enters its compact state. Independent of Horizon's 4592 transparent/inverse logo system â only the logo mark swaps. 4593 Selectors carry #header-component so they outrank the existing 4594 `.header-logo:has(...)` / `.header-logo__image-container--inverse` 4595 display rules regardless of source order. 4596 4597 ââ Why data-bp-nav and not data-sticky-state ââââââââââââââââââ 4598 These rules used to key off Horizon's [data-sticky-state='active'] 4599 (assets/header.js #observeStickyPosition). That flag is written by 4600 Horizon's IntersectionObserver from the header's own visibility â 4601 and the rules below CHANGE the header's height and position. Any 4602 state whose styles perturb the geometry that produces the state is 4603 a feedback loop: the observer writes a value, the header resizes / 4604 unpins, the observer re-fires, writes the opposite value. The 4605 header strobes between variants. (Measured 2026-07-14: with 4606 threshold: 1, isIntersecting is false at ratio 0.9888 and true at 4607 ratio 1, so Horizon's flag flips at exactly rect.top === 0 â the 4608 single offset where a header pinned at top: -1px hands off from 4609 in-flow to pinned. That is where the strobe appeared.) 4610 4611 data-bp-nav is owned solely by the scroll-threshold state machine 4612 in layout/theme.liquid, driven by scrollY â an input the header's 4613 own size cannot perturb. Horizon keeps writing data-sticky-state 4614 for its own purposes (opacity, view-transition-name); it no longer 4615 drives any geometry, so the two can never fight. */ 4616 .header-logo__image-container--scroll { 4617 display: none; 4618 } 4619 4620 /* ââ Positioning model ââââââââââââââââââââââââââââââââââââââââââââ 4621 Resting nav is IN FLOW and scrolls away with the page; only the 4622 compact bar is fixed. Cancels Horizon's `position: sticky; 4623 top: -1px` for sticky="always" (sections/header.liquid:553). 4624 4625 .header-section keeps the resting header's height via 4626 --bp-header-rest-h even while the child is fixed, so nothing below 4627 ever shifts when the compact bar engages. That reserved space is 4628 what makes scrollY a stable trigger: no reflow â no scroll 4629 anchoring â no feedback into the threshold. Without it, going 4630 fixed would pull ~70px of content up and move the very scroll 4631 offset the state machine reads. 4632 4633 !important + the body prefix because Horizon's rule is 4634 `.header-section:has(> #header-component[sticky='always'])`, whose 4635 :has() takes the specificity of its argument (an id + attr), and 4636 section stylesheet blocks are bundled separately from this inline 4637 style tag â source order isn't guaranteed. 4638 4639 NB: don't write Liquid tag delimiters in these comments, even 4640 inside a CSS comment. Liquid parses tags before CSS exists, so a 4641 literal stylesheet/style tag written here is read as a real tag 4642 nested inside this style block and throws at compile. */ 4643 body .header-section:has(> #header-component[sticky='always']) { 4644 position: static !important; 4645 top: auto !important; 4646 min-height: var(--bp-header-rest-h, auto); 4647 } 4648 4649 #header-component[data-bp-nav='compact'] { 4650 position: fixed; 4651 top: 0; 4652 left: 0; 4653 right: 0; 4654 z-index: var(--layer-sticky); 4655 } 4656 4657 /* flex (not block) so the icon centers on both axes instead of 4658 sitting on the inline text baseline, which left descender 4659 whitespace below it and made it look pushed toward the top of 4660 the row. */ 4661 #header-component[data-bp-nav='compact'] .header-logo__image-container--scroll { 4662 display: flex; 4663 align-items: center; 4664 justify-content: center; 4665 } 4666 4667 #header-component[data-bp-nav='compact'] .header-logo__image-container--original, 4668 #header-component[data-bp-nav='compact'] .header-logo__image-container--inverse { 4669 display: none; 4670 } 4671 4672 /* Design request: collapse header__columns vertical padding to 0 4673 the moment the header becomes sticky, so the sticky bar sits flush 4674 rather than keeping the resting-state --header-padding. Overrides 4675 header.liquid's `.header__columns { --padding-block-start/end }` 4676 (assets/base.css .spacing-style reads these into padding-block). */ 4677 #header-component[data-bp-nav='compact'] .header__columns { 4678 --padding-block-start: 0px; 4679 --padding-block-end: 0px; 4680 } 4681 4682 /* Design request: once the logo has swapped to the compact scroll 4683 mark, let the left and right grid tracks (.header__column--left,
4684 .header__column--right) hug their content width instead of the 4685 resting-state 1fr / minmax(max-content, 1fr) tracks â frees up 4686 space for the center column while sticky. --header-left/-right 4687 feed --header-template-columns (grid-template-columns) above; 4688 the ID selector's specificity beats every non-sticky :has()-based 4689 --header-left/-right override in this file. */ 4690 #header-component[data-bp-nav='compact'] .header__columns { 4691 --header-left: max-content; 4692 --header-right: max-content; 4693 } 4694 4695 /* --------------------------------------------------------------- 4696 Quick-view product template (?view=quickview) â Atlas parity. 4697 4698 The Publitas digital-catalog product modal loads 4699 /products/<handle>?view=quickview inside its #prod-frame iframe. 4700 That renders templates/product.quickview.json (the buy box only). 4701 Strip the site chrome so the iframe shows an optimized product 4702 view â no topnav, announcement bar, footer, sticky ATC, or chat â 4703 rather than the whole storefront. 4704 4705 Scoped with :has() off the <main data-template> attribute 4706 (theme.liquid emits data-template="product", which 4707 resolves to "product.quickview" here), so nothing leaks onto 4708 the normal PDP. Matches Atlas templates/product.quickview.liquid, 4709 which display:none's #shopify-section-header / -footer / etc. 4710 --------------------------------------------------------------- */ 4711 4712 /* Hide all site chrome in the quickview modal. display:none is correct 4713 here: the cart drawer is removed from the iframe entirely (see 4714 bp-quickview-cart-bridge.liquid). The just-added item is shown by the 4715 PARENT catalog page's cart drawer instead â it runs in full page 4716 context (so its section-scoped line re-render works reliably) and 4717 opens in the top layer, above the Publitas modal. Trying to render the 4718 drawer inside the iframe was fragile: its syncItemsList() walks 4719 ancestor shopify-section wrappers and re-fetches sections against the 4720 iframe URL, which didn't repopulate the just-added line. */ 4721 body:has(main[data-template='product.quickview']) #header-group, 4722 body:has(main[data-template='product.quickview']) > footer, 4723 body:has(main[data-template='product.quickview']) .bp-sticky-atc, 4724 body:has(main[data-template='product.quickview']) #chat-button { 4725 display: none !important; 4726 } 4727 4728 /* Same hides keyed off the BODY CLASS (layout/theme.liquid adds 4729 `template-quickview`) instead of `:has(mainâ¦)`. The `:has(mainâ¦)` form only 4730 matches AFTER <main> parses â which comes after the header â so on each 4731 product change the header / announcement bar / BP logo / top menu flashed 4732 visible for a frame before being hidden (Dragana QA 7/22). The body class 4733 is present at first paint, so these hide with no flash. 4734 This block ALSO strips the PDP-only buy-box extras the digital-catalog quick 4735 view shouldn't show (Dragana QA 7/22): the gallery breadcrumb + "How to Wear 4736 It" chip, the size-chart link, the TrueFit find-my-size widget, and the 4737 Product Detail / Size & Fit / Shipping & Returns accordions. */ 4738 body.template-quickview #header-group, 4739 body.template-quickview > footer, 4740 body.template-quickview .bp-sticky-atc, 4741 body.template-quickview #chat-button, 4742 body.template-quickview .bp-pdp-gallery__crumbs, 4743 body.template-quickview .bp-pdp-gallery__htwi, 4744 body.template-quickview .bp-size-chart-link, 4745 body.template-quickview .tfc-fitrec-product, 4746 body.template-quickview .bp-acc { 4747 display: none !important; 4748 } 4749 4750 body:has(main[data-template='product.quickview']) main.content-for-layout { 4751 padding-top: 0; 4752 margin-top: 0; 4753 } 4754 4755 /* PDP sticky add-to-cart bar (.bp-sticky-atc, z-index: 100) sits above 4756 everything by design, but that also puts it above the mobile nav 4757 drawer (bp-mobile-drawer, z-index ~5-6 off --layer-heightened) â 4758 covering the bottom menu items when the drawer is open on a PDP. 4759 Hide it for the duration instead of escalating the drawer's z-index, 4760 which would ripple into every other component sharing that token. */ 4761 body:has(bp-mobile-drawer .bp-mobile-drawer__details[open]) .bp-sticky-atc { 4762 display: none !important; 4763 } 4764 4765 /* ============================================================ 4766 AB Tasty social-proof popup ("POPULAR! N views in the last N days", 4767 injected at runtime via GTM/AB Tasty â .ab_widget_container_social-proof_*). 4768 It was anchored to the left of the PDP gallery and overflowed the image
4769 frame / got clipped at the viewport's left edge on wide monitors (QA 7/21). 4770 Re-anchor it as a safe bottom-left toast so it stays fully on-screen at any 4771 width and can't overlap the gallery. NOTE: the auto-dismiss timing ("not 4772 gone in a few seconds") is an AB Tasty CAMPAIGN setting, not theme code. 4773 ============================================================ */ 4774 .ab_widget_container_social-proof_content { 4775 position: fixed !important; 4776 left: 16px !important; 4777 right: auto !important; 4778 bottom: 16px !important; 4779 top: auto !important; 4780 transform: none !important; 4781 max-width: min(320px, calc(100vw - 32px)) !important; 4782 z-index: 30 !important; 4783 } 4784 4785 /* ============================================================ 4786 Yotpo referral widget (instance 506320) â BP-controlled overrides. 4787 These previously lived only in Yotpo's dashboard Custom CSS; kept here 4788 so they're version-controlled and survive a Yotpo re-config. Renders on 4789 the refer-a-friend page and the Proper Insider rewards page. Class names 4790 are doubled to outrank Yotpo's runtime-injected single-class rules. 4791 ============================================================ */ 4792 .yotpo-widget-referral-widget .yotpo-center-container.yotpo-center-container { 4793 height: 626px; 4794 } 4795 /* Don't force the copy-code text white â keep it readable (BP text color) 4796 against the widget's light surface. */ 4797 .yotpo-widget-referral-widget .yotpo-copy-text-button .yotpo-copy-text.yotpo-copy-text { 4798 color: var(--bp-color-text) !important; 4799 } 4800 4801 /* ============================================================ 4802 Cart-flyout (quick-add drawer) Add-to-bag button â match the PDP CTA. 4803 The drawer's buy button is redistributed into .bp-qd__cta-slot, OUTSIDE 4804 .product-information, so the PDP button rules (bp-pdp-styles) never reach 4805 it â it came out mis-sized / stretched with no bag icon (QA: Amber). 4806 Mirror the PDP spec: 17px / -0.17px / 500, filled black, 20px bag icon, 4807 hover inverts. Icon = shared assets/bp-bag-icon.svg (white); hover uses 4808 filter:brightness(0) to flip it black on the transparent button. 4809 ============================================================ */ 4810 dialog.quick-add-modal.bp-qd add-to-cart-component button, 4811 dialog.quick-add-modal.bp-qd button[name="add"], 4812 dialog.quick-add-modal.bp-qd .buy-buttons button[type="submit"] { 4813 width: 100%; 4814 background: var(--bp-color-text, #171609) !important; 4815 color: var(--bp-color-cta-text, #FDFFFA) !important; 4816 border: 1px solid var(--bp-color-text, #171609) !important; 4817 border-radius: 0 !important; 4818 padding: 16px 24px !important; 4819 font-family: var(--bp-font-body) !important; 4820 font-size: 17px !important; 4821 font-weight: 500 !important; 4822 line-height: normal !important; 4823 letter-spacing: -0.17px !important; 4824 text-transform: none !important; 4825 display: inline-flex !important; 4826 align-items: center !important; 4827 justify-content: center !important; 4828 gap: 10px !important; 4829 } 4830 dialog.quick-add-modal.bp-qd add-to-cart-component button .svg-wrapper.add-to-cart-icon, 4831 dialog.quick-add-modal.bp-qd add-to-cart-component button .add-to-cart-icon, 4832 dialog.quick-add-modal.bp-qd button[name="add"] .svg-wrapper.add-to-cart-icon, 4833 dialog.quick-add-modal.bp-qd button[name="add"] .add-to-cart-icon { 4834 display: none !important; 4835 } 4836 dialog.quick-add-modal.bp-qd add-to-cart-component button::after, 4837 dialog.quick-add-modal.bp-qd button[name="add"]::after, 4838 dialog.quick-add-modal.bp-qd .buy-buttons button[type="submit"]::after { 4839 content: '' !important; 4840 display: inline-block !important; 4841 flex: 0 0 20px !important; 4842 width: 20px !important; 4843 height: 20px !important; 4844 background-repeat: no-repeat; 4845 background-position: center; 4846 background-size: contain; 4847 background-image: url(//www.bostonproper.com/cdn/shop/t/1521/assets/bp-bag-icon.svg?v=5174825450042497821790713714); 4848 } 4849 dialog.quick-add-modal.bp-qd add-to-cart-component button:hover, 4850 dialog.quick-add-modal.bp-qd button[name="add"]:hover, 4851 dialog.quick-add-modal.bp-qd .buy-buttons button[type="submit"]:hover, 4852 dialog.quick-add-modal.bp-qd add-to-cart-component button:hover .add-to-cart-text, 4853 dialog.quick-add-modal.bp-qd add-to-cart-component button:hover .add-to-cart-text *, 4854 dialog.quick-add-modal.bp-qd button[name="add"]:hover .add-to-cart-text, 4855 dialog.quick-add-modal.bp-qd button[name="add"]:hover .add-to-cart-text * { 4856 background: transparent !important; 4857 color: var(--bp-color-text, #171609) !important; 4858 } 4859 dialog.quick-add-modal.bp-qd add-to-cart-component button:hover::after, 4860 dialog.quick-add-modal.bp-qd button[name="add"]:hover::after, 4861 dialog.quick-add-modal.bp-qd .buy-buttons button[type="submit"]:hover::after { 4862 filter: brightness(0); 4863 } 4864 4865 /* ============================================================ 4866 Cart Shop Pay Installments logo â black (QA: Amber). Shopify's 4867 native installments banner (the payment_terms filter in the 4868 Horizon snippet cart-summary.liquid) injects a purple Shop Pay
4869 wordmark as an img with id shoppay-installments-icon. BP wants it 4870 black in the cart. filter:brightness(0) flattens the artwork to 4871 solid black while preserving its transparency (same trick used on 4872 the cart-flyout bag icon above). Scoped to .cart-totals__installments 4873 so only the cart surfaces (drawer + cart page) are recoloured, not 4874 any other Shop Pay mark. 4875 ============================================================ */ 4876 .cart-totals__installments #shoppay-installments-icon { 4877 filter: brightness(0) !important; 4878 } 4879 4880 /* ============================================================ 4881 PLP Back-nav restore veil 4882 bp-bloomreach.js adds html.bp-plp-restoring while a saved scroll 4883 position is restored on a full-reload Back (PDP â Back â PLP), so the 4884 shopper sees a brief loader instead of the grid painting at the TOP and 4885 then jumping to their spot. Removed the instant the scroll lands (JS, 4886 with a safety timeout). A bfcache Back never sets it, so that stays 4887 instant. pointer-events:none so a real scroll/tap still passes through 4888 and cancels the restore. Global (targets <html>) so it covers both the 4889 /collections and /search PLPs, whose grid CSS lives in separate scoped 4890 stylesheets. 4891 ============================================================ */ 4892 .bp-plp-restore-veil { 4893 position: fixed; 4894 inset: 0; 4895 z-index: 2147483000; 4896 background: var(--bp-color-bg-page, #FEFFFA); 4897 display: flex; 4898 align-items: center; 4899 justify-content: center; 4900 pointer-events: none; 4901 opacity: 1; 4902 transition: opacity 0.4s ease; 4903 } 4904 /* Kill smooth-scroll while we pin the restore offset â Safari will 4905 otherwise animate (and lose) competing scrollTo calls. */ 4906 html.bp-plp-restoring { 4907 scroll-behavior: auto !important; 4908 } 4909 html.bp-plp-restoring body { 4910 scroll-behavior: auto !important; 4911 } 4912 /* JS adds .is-out to fade the veil to 0, then removes the element once 4913 faded â so it disappears SMOOTHLY, not "de golpe". */ 4914 .bp-plp-restore-veil.is-out { opacity: 0; } 4915 .bp-plp-restore-veil__spin { 4916 width: 34px; 4917 height: 34px; 4918 border-radius: 50%; 4919 border: 3px solid rgba(23, 22, 9, 0.15); 4920 border-top-color: var(--bp-color-text, #171609); 4921 animation: bpPlpRestoreSpin 0.7s linear infinite; 4922 } 4923 @media (prefers-reduced-motion: reduce) { 4924 .bp-plp-restore-veil { transition: none; } 4925 .bp-plp-restore-veil__spin { animation: none; } 4926 } 4927 @keyframes bpPlpRestoreSpin { to { transform: rotate(360deg); } } 4928 4929 /* ============================================================ 4930 PDP title de-flicker 4931 Each colourway is a separate product whose Shopify title carries the 4932 colour name; snippets/bp-pdp-title-color.liquid strips it (the colour is 4933 already shown by the swatch). Server-side stripping isn't possible â the 4934 title is a Text block bound to a dynamic source â so hide the title 4935 until the snippet strips it and marks it [data-bp-title-ready], so the full 4936 "â¦Colour" title never flashes first. visibility:hidden keeps its box (no 4937 CLS). A pure-CSS animation reveals it after 1.2s as a fail-safe, so it can 4938 never get stuck hidden if the JS doesn't run. 4939 ============================================================ */ 4940 .product-information h1:not([data-bp-title-ready]), 4941 .product-details h1:not([data-bp-title-ready]) { 4942 visibility: hidden; 4943 animation: bpTitleRevealSafety 0s linear 1.2s forwards; 4944 } 4945 @keyframes bpTitleRevealSafety { to { visibility: visible; } } 4946</style> 4947<link rel="stylesheet" href="//www.bostonproper.com/cdn/shop/t/1521/assets/bp-fonts-deferred.css?v=138496893836640374981790713714" media="print" onload="this.media='all'"> 4948 <noscript><link rel="stylesheet" href="//www.bostonproper.com/cdn/shop/t/1521/assets/bp-fonts-deferred.css?v=138496893836640374981790713714"></noscript>
4948<script src="//www.bostonproper.com/cdn/shop/t/1521/assets/bp-preorder-dates.js?v=90299843317653306041790713714"></script>
4948<style id="dg-consent-custom-style"> 4949 :host(.dg-consent-banner) {--dg-primary-font: var(--bp-font-body, 'Messina Sans VF', system-ui, sans-serif); 4950 --dg-consent-background-border: #ffffff; 4951 --consent-border-radius: 0;--dg-heading-font-size: 20px; 4952 --dg-heading-font-weight: 500;--dg-explanation-font-size: 15px; 4953 --dg-explanation-font-weight: 400;--dg-policy-option-heading-size: 15px; 4954 --dg-policy-option-heading-weight: 500; 4955 --dg-policy-option-description-font-size: 15px; 4956 --dg-policy-option-description-font-wieght: 400; /* `wieght` is not a typo here â it is on DataGrail's side, and this must match their variable name */ 4957 --dg-policy-option-essential-label-font-color: #666; 4958 --dg-policy-option-essential-label-font-wieght: 400; /* `wieght` â see above */--dg-slider-enabled-secondary: #CC0066 !important;--dg-button-color: var(--bp-color-cta-text, #FDFFFA) !important; 4959 --dg-button-background: var(--bp-color-text, #171609) !important; 4960 --dg-button-border: none !important; 4961 --dg-button-radius: 0 !important;letter-spacing: 0.25px !important; 4962 font-family: var(--bp-font-body, 'Messina Sans VF', system-ui, sans-serif) !important; 4963 }:host(.dg-consent-banner) * { 4964 letter-spacing: 0.25px !important; 4965 font-family: var(--bp-font-body, 'Messina Sans VF', system-ui, sans-serif) !important; 4966 } 4967 4968 :host(.dg-consent-banner) .dg-app { 4969 max-width: 501px !important; 4970 } 4971 4972 :host(.dg-consent-banner) h1.dg-header { 4973 border-bottom: none !important; 4974 line-height: 28px !important; 4975 } 4976 4977 :host(.dg-consent-banner) .dg-header-close { 4978 position: absolute !important; 4979 top: 12px !important; 4980 right: 12px !important; 4981 width: 18px !important; 4982 height: 18px !important; 4983 padding: 4px !important; 4984 } 4985 4986 :host(.dg-consent-banner) .dg-header-close svg { 4987 display: none !important; 4988 }:host(.dg-consent-banner) .dg-header-close::before { 4989 content: url("//www.bostonproper.com/cdn/shop/t/1521/assets/icon-close.svg?v=127645775129621355101790713714") !important; 4990 position: absolute !important; 4991 top: 0 !important; 4992 right: 0 !important; 4993 bottom: 0 !important; 4994 left: 0 !important; 4995 margin: auto !important; 4996 width: 18px !important; 4997 height: 18px !important; 4998 } 4999 5000 :host(.dg-consent-banner) .dg-main-content-explanation { 5001 padding-top: 0 !important; 5002 line-height: 20px !important; 5003 } 5004 5005 :host(.dg-consent-banner) .dg-main-actions { 5006 border-color: transparent !important; 5007 } 5008 5009 :host(.dg-consent-banner) .dg-main-content-policies { 5010 border-top: 1px solid #999 !important; 5011 margin: 0 32px !important; 5012 padding: 0 !important; 5013 } 5014 5015 :host(.dg-consent-banner) .dg-slider.essential-label { 5016 font-style: italic !important; 5017 font-size: 15px !important; 5018 } 5019 5020 :host(.dg-consent-banner) .dg-main-actions button { 5021 text-transform: uppercase !important; 5022 font-size: 15px !important; 5023 font-weight: 500 !important; 5024 } 5025 5026 :host(.dg-consent-banner) .dg-button.essential_only { 5027 display: none !important; 5028 visibility: hidden !important; 5029 } 5030 5031 .dg-main-content-policy-option-description p { 5032 margin-top: 0 !important; 5033 } 5034 5035 .dg-main-content-declaration { 5036 padding: 0 !important; 5037 } 5038 5039 :host(.dg-consent-banner) ._48mA0q_buttonUnstyled { 5040 color: #000000 !important; 5041 } 5042</style> 5043
5044<script> 5045 // Consent bridge â Atlas parity (site-datagrail-style-and-logs.liquid). 5046 // DataGrail calls back with the customer's cookie preferences; we hand 5047 // them to Shopify so its pixels/managed scripts honour the choice. 5048 window.dgEvent = window.dgEvent || []; 5049 5050 function bpSyncReceivePrefs(dgEventData) { 5051 const cookiePreferences = 5052 dgEventData && dgEventData.consentPreferences && dgEventData.consentPreferences.cookieOptions; 5053 // No preferences payload â nothing to sync. Bail rather than 5054 // asserting consent the customer never gave. 5055 if (!cookiePreferences || !cookiePreferences.find) return; 5056 5057 const enabled = (key) => { 5058 const opt = cookiePreferences.find((item) => item && item.gtm_key === key); 5059 return opt ? opt.isEnabled : false; 5060 }; 5061 5062 window.Shopify.loadFeatures( 5063 [{ name: 'consent-tracking-api', version: '0.1' }], 5064 (error) => { 5065 if (error) { 5066 console.error('[bp-datagrail] Shopify Customer Privacy API failed to load', error); 5067 return; 5068 } 5069 window.Shopify.customerPrivacy.setTrackingConsent( 5070 { 5071 preferences: true, 5072 analytics: enabled('dg-category-performance'), 5073 marketing: enabled('dg-category-marketing'), 5074 sale_of_data: enabled('dg-category-marketing') 5075 }, 5076 (arg) => { 5077 if (window.Shopify && window.Shopify.designMode) { 5078 console.log('[bp-datagrail] consent captured', arg); 5079 } 5080 } 5081 ); 5082 } 5083 ); 5084 } 5085 5086 window.dgEvent.push({ event: 'preference_callback', params: bpSyncReceivePrefs }); 5087</script>
vendor: 1 bytes, line 5087
5087
5088<script> 5089 window.shop = { 5090 url: "https:\/\/www.bostonproper.com", 5091 template: "product", 5092 environment: "production", 5093 theme: (typeof Shopify !== 'undefined' && Shopify.theme) ? Shopify.theme : null, 5094 themeSettings: { 5095 amplience_cdn_base_url: "https:\/\/media.bostonproper.com" 5096 }, 5097 br: { 5098 host: "https:\/\/core.dxpapi.com\/api\/v1\/core\/", 5099 suggestHost: "https:\/\/suggest.dxpapi.com\/api\/v2\/suggest\/", 5100 accountId: "8261", 5101 domainKey: "bostonproper", 5102 authKey: "xv7ok1fkm5gve1ms" 5103 }, 5104 badges: { 5105 bestseller: { 5106 enabled: true, 5107 label: "Best Seller" 5108 }, 5109 trending: { 5110 enabled: true, 5111 label: "Trending" 5112 } 5113 }, 5114 methods: {} 5115 }; 5116</script>
5116<script> 5117 window.bpColorSiblings = (function () { 5118 // Storefront token: the theme setting wins, but we fall back to the 5119 // known-good public token (recovered from Atlas's live theme.min.js 5120 // and verified against the Storefront API) so the GraphQL path works 5121 // even when config/settings_data.json isn't synced to the theme by 5122 // Shopify's GitHub integration. This is a PUBLIC unauthenticated 5123 // Storefront token â already exposed in Atlas's live JS â so it's 5124 // safe to embed client-side. NB: the older 23797⦠in Atlas's 5125 // .env.example is a stale placeholder and returns UNAUTHORIZED. 5126 var TOKEN = "04534ca2c4aa793f02c0c735d5d6160c"; 5127 var AMPLIENCE = "https:\/\/media.bostonproper.com"; 5128 var API_VERSION = '2024-07'; 5129 5130 function apiUrl() { 5131 var root = (window.Shopify && Shopify.routes && Shopify.routes.root) ? Shopify.routes.root : '/'; 5132 return root + 'api/' + API_VERSION + '/graphql.json'; 5133 } 5134 5135 function swatchUrl(style) { 5136 if (!style) return ''; 5137 return AMPLIENCE + '/i/bostonproper/' + style + '_2?$poiswatch$&img404=' + style + '_1'; 5138 } 5139 5140 /* Batched fetch. handles: array of product handles. Resolves to 5141 { handle: { available, style, swatchUrl, goLiveDate, price, compareAt, discount, promo } | null }. 5142 null = the colour is listed in bp_stylecolorlist but no product 5143 exists at that handle (drop the swatch). Rejects on transport / 5144 API error so callers can fall back to the legacy per-sibling path. */ 5145 function fetchSiblings(handles) { 5146 if (!handles || !handles.length) return Promise.resolve({}); 5147 if (!TOKEN) return Promise.reject(new Error('bpColorSiblings: no storefront token configured')); 5148 var aliasToHandle = {}; 5149 var parts = handles.map(function (h, i) { 5150 var alias = 'p' + i; 5151 aliasToHandle[alias] = h; 5152 return alias + ': product(handle: ' + JSON.stringify(h) + ') {' + 5153 ' availableForSale' + 5154 ' tags' + 5155 ' style: metafield(namespace: "BP_ProductAttributes", key: "bp_style") { value }' + 5156 ' goLive: metafield(namespace: "BP_ProductAttributes", key: "bp_golivedate") { value }' + 5157 ' colorList: metafield(namespace: "BP_ProductAttributes", key: "bp_stylecolorlist") { value }' + 5158 ' promo: metafield(namespace: "discounts", key: "discount_message") { value }' + 5159 ' priceRange { minVariantPrice { amount } }' + 5160 ' compareAtPriceRange { minVariantPrice { amount } }' + 5161 ' }'; 5162 }); 5163 5164 return fetch(apiUrl(), { 5165 method: 'POST', 5166 headers: { 5167 'Content-Type': 'application/json', 5168 'Accept': 'application/json', 5169 'X-Shopify-Storefront-Access-Token': TOKEN 5170 }, 5171 body: JSON.stringify({ query: '{' + parts.join(' ') + '}' }) 5172 }).then(function (r) { 5173 if (!r.ok) throw new Error('storefront HTTP ' + r.status); 5174 return r.json(); 5175 }).then(function (res) { 5176 var data = res && res.data; 5177 if (!data) throw new Error('storefront: no data'); 5178 var out = {}; 5179 Object.keys(aliasToHandle).forEach(function (alias) { 5180 var handle = aliasToHandle[alias]; 5181 var p = data[alias]; 5182 if (!p) { out[handle] = null; return; } 5183 var style = (p.style && p.style.value) ? p.style.value : ''; 5184 var price = (p.priceRange && p.priceRange.minVariantPrice) ? parseFloat(p.priceRange.minVariantPrice.amount) : null; 5185 var compareAt = (p.compareAtPriceRange && p.compareAtPriceRange.minVariantPrice) ? parseFloat(p.compareAtPriceRange.minVariantPrice.amount) : null; 5186 var discount = 0; 5187 if (price != null && compareAt != null && compareAt > price) { 5188 discount = Math.round(((compareAt - price) / compareAt) * 100); 5189 } 5190 var tags = Array.isArray(p.tags) ? p.tags : []; 5191 out[handle] = { 5192 available: p.availableForSale === true, 5193 style: style, 5194 swatchUrl: swatchUrl(style), 5195 goLiveDate: (p.goLive && p.goLive.value) ? p.goLive.value : '', 5196 // Sibling's own bp_stylecolorlist â the raw material for the PDP 5197 // swatch block's canonical-family union crawl. Each colour's list 5198 // drifts, so callers UNION them to reconstruct the full family. 5199 colorList: (p.colorList && p.colorList.value) ? p.colorList.value : '', 5200 price: price, 5201 compareAt: compareAt, 5202 discount: discount, 5203 // INV5/INV7 colour-family flag â the swatch row shows only for these 5204 // (matches the PDP + Liquid card gate), so bp-card-hydrate can hide 5205 // lone swatches on non-family products. 5206 isInv: tags.some(function (t) { return /INV5|INV7/i.test(t); }), 5207 // Red sale/promo line (Shopify-native discounts.discount_message, 5208 // same field the PDP + Liquid card read). PLP cards are BR-rendered 5209 // with no promo, so bp-card-hydrate injects it from here. 5210 promo: (p.promo && p.promo.value) ? p.promo.value : '' 5211 }; 5212 }); 5213 return out; 5214 }); 5215 } 5216 5217 /* Group-tag family discovery â the ROBUST family source. 5218 5219 The composed-handle path (fetchSiblings + hydrateRoot's crawl) derives each 5220 sibling's handle as `master + '-' + handleize(colourName)`. That breaks on 5221 BAD DATA where a product's handle token doesn't match its Colour name / 5222 bp_stylecolorlist entry â e.g. 2DO2220 whose handle ends `-black` but whose 5223 Colour option is "Luxe Links" and whose list is "Blue Bell, Luxe Links". 5224 From the blue PDP the composed black handle resolves to `â¦-luxe-links` 5225 (does NOT exist; the real handle is `â¦-black`), so the black sibling is 5226 unreachable and the swatch disappears with no way back (Critical 2DO2220). 5227 5228 Every colourway of a family shares a GROUP TAG (`YGroup_*`, also 5229 `INV5_*`/`nvgroup_*`). Querying Storefront by that tag returns the REAL 5230 siblings with their REAL handles + Colour-option names regardless of any 5231 name/handle/list drift â so every colour is reachable from every colourway. 5232
5233 `tag` is the product's ACTUAL YGroup_* tag string, read verbatim from Liquid 5234 (its format is inconsistent across families: `YGroup_2DO22` bare code vs. 5235 `YGroup_2BK45 Marilyn Cowl-Neck Charmeuse Blouse` code+name), so we pass it 5236 through as a search phrase unchanged. 5237 5238 Resolves to { realHandle: { handle, color, available, style, swatchUrl, 5239 goLiveDate, discount, colorList, price, compareAt, promo, isInv } } â the 5240 same shape hydrateRoot's crawl consumes from fetchSiblings, plus `color` 5241 (the Colour option value) so callers can build nameByHandle from REAL 5242 handles instead of composed ones. Rejects on transport/API error so callers 5243 can fall back to the composed-handle crawl. */ 5244 function fetchByGroup(tag) { 5245 if (!tag) return Promise.resolve({}); 5246 if (!TOKEN) return Promise.reject(new Error('bpColorSiblings: no storefront token configured')); 5247 // Mirror the verified probe: tag:'<value>' phrase match. Escape any single 5248 // quote in the tag value so the search phrase stays well-formed. 5249 var q = "tag:'" + String(tag).replace(/'/g, "\\'") + "'"; 5250 // first:100 covers large families (e.g. Marilyn = 24 colourways) in one 5251 // round-trip; families don't approach that ceiling in practice. 5252 var query = 'query($q:String!){products(first:100,query:$q){nodes{' + 5253 ' handle' + 5254 ' availableForSale' + 5255 ' tags' + 5256 ' options(first: 10) { name values }' + 5257 ' style: metafield(namespace: "BP_ProductAttributes", key: "bp_style") { value }' + 5258 ' goLive: metafield(namespace: "BP_ProductAttributes", key: "bp_golivedate") { value }' + 5259 ' colorList: metafield(namespace: "BP_ProductAttributes", key: "bp_stylecolorlist") { value }' + 5260 ' promo: metafield(namespace: "discounts", key: "discount_message") { value }' + 5261 ' priceRange { minVariantPrice { amount } }' + 5262 ' compareAtPriceRange { minVariantPrice { amount } }' + 5263 ' }}}'; 5264 return fetch(apiUrl(), { 5265 method: 'POST', 5266 headers: { 5267 'Content-Type': 'application/json', 5268 'Accept': 'application/json', 5269 'X-Shopify-Storefront-Access-Token': TOKEN 5270 }, 5271 body: JSON.stringify({ query: query, variables: { q: q } }) 5272 }).then(function (r) { 5273 if (!r.ok) throw new Error('storefront HTTP ' + r.status); 5274 return r.json(); 5275 }).then(function (res) { 5276 var nodes = res && res.data && res.data.products && res.data.products.nodes; 5277 if (!Array.isArray(nodes)) throw new Error('storefront: no group products'); 5278 var out = {}; 5279 nodes.forEach(function (p) { 5280 if (!p || !p.handle) return; 5281 var style = (p.style && p.style.value) ? p.style.value : ''; 5282 var price = (p.priceRange && p.priceRange.minVariantPrice) ? parseFloat(p.priceRange.minVariantPrice.amount) : null; 5283 var compareAt = (p.compareAtPriceRange && p.compareAtPriceRange.minVariantPrice) ? parseFloat(p.compareAtPriceRange.minVariantPrice.amount) : null; 5284 var discount = 0; 5285 if (price != null && compareAt != null && compareAt > price) { 5286 discount = Math.round(((compareAt - price) / compareAt) * 100); 5287 } 5288 // Colour option value â each sibling is ONE colourway, so its "Color" 5289 // (or "Colour") option carries the single real Colour name. This is the 5290 // authoritative colour label, immune to handle/list drift. 5291 var color = ''; 5292 var opts = Array.isArray(p.options) ? p.options : []; 5293 for (var i = 0; i < opts.length; i++) { 5294 var nm = (opts[i] && opts[i].name || '').toLowerCase(); 5295 if (nm === 'color' || nm === 'colour') { 5296 color = (opts[i].values && opts[i].values[0]) || ''; 5297 break; 5298 } 5299 } 5300 var tags = Array.isArray(p.tags) ? p.tags : []; 5301 out[p.handle] = { 5302 handle: p.handle, 5303 color: color, 5304 available: p.availableForSale === true, 5305 style: style, 5306 swatchUrl: swatchUrl(style), 5307 goLiveDate: (p.goLive && p.goLive.value) ? p.goLive.value : '', 5308 colorList: (p.colorList && p.colorList.value) ? p.colorList.value : '', 5309 price: price, 5310 compareAt: compareAt, 5311 discount: discount, 5312 isInv: tags.some(function (t) { return /INV5|INV7/i.test(t); }), 5313 promo: (p.promo && p.promo.value) ? p.promo.value : '' 5314 }; 5315 }); 5316 return out; 5317 }); 5318 } 5319 5320 /* Batched live-inventory lookup for GA4 item_stock_qty. `variantIds` is an 5321 array of numeric variant ids (from cart.js line.variant_id) or gids. 5322 Resolves to { numericVariantId: quantityAvailable }. Uses the Storefront 5323 `quantityAvailable` field â requires the token's 5324 `unauthenticated_read_product_inventory` scope; if it's missing or the 5325 variant doesn't track inventory, that entry is simply omitted so callers 5326 leave item_stock_qty blank (graceful, never throws). */ 5327 function fetchInventory(variantIds) { 5328 var ids = (variantIds || []) 5329 .filter(Boolean) 5330 .map(function (v) { 5331 var s = String(v); 5332 return s.indexOf('gid://') === 0 ? s : 'gid://shopify/ProductVariant/' + s; 5333 }); 5334 if (!ids.length || !TOKEN) return Promise.resolve({}); 5335 // qty + price + compareAtPrice per variant. price/compareAtPrice feed the 5336 // GA4 sale fields (item_category3 "Sale Price", item_original_price, 5337 // item_sale_discount) on cart events, which have no compare-at in cart.js. 5338 var query = 'query($ids:[ID!]!){nodes(ids:$ids){... on ProductVariant{id quantityAvailable price{amount} compareAtPrice{amount}}}}'; 5339 return fetch(apiUrl(), { 5340 method: 'POST', 5341 headers: { 5342 'Content-Type': 'application/json', 5343 'Accept': 'application/json', 5344 'X-Shopify-Storefront-Access-Token': TOKEN 5345 }, 5346 body: JSON.stringify({ query: query, variables: { ids: ids } }) 5347 }).then(function (r) { 5348 return r.ok ? r.json() : null; 5349 }).then(function (res) { 5350 var out = {}; 5351 var nodes = res && res.data && res.data.nodes; 5352 if (Array.isArray(nodes)) {
5353 nodes.forEach(function (n) { 5354 if (n && n.id) { 5355 out[n.id.replace(/^gid:\/\/shopify\/ProductVariant\//, '')] = { 5356 qty: (n.quantityAvailable != null) ? n.quantityAvailable : null, 5357 price: (n.price && n.price.amount != null) ? parseFloat(n.price.amount) : null, 5358 compareAt: (n.compareAtPrice && n.compareAtPrice.amount != null) ? parseFloat(n.compareAtPrice.amount) : null 5359 }; 5360 } 5361 }); 5362 } 5363 return out; 5364 }).catch(function () { return {}; }); 5365 } 5366 5367 /* On-demand GA4 track enrichment for BR cards, which carry no variant id / 5368 department / inventory. Accepts a single handle OR an array (batched, 5369 aliased â one request for a whole PLP grid). Resolves to a map 5370 { handle: { variantId, department, quantityAvailable } } so callers fill 5371 item_shopify_variant + item_category + item_stock_qty. Never throws. */ 5372 function fetchTrackData(handles) { 5373 var list = (Array.isArray(handles) ? handles : [handles]).filter(Boolean); 5374 // dedupe 5375 list = list.filter(function (h, i) { return list.indexOf(h) === i; }); 5376 if (!list.length || !TOKEN) return Promise.resolve({}); 5377 var aliasToHandle = {}; 5378 var parts = list.map(function (h, i) { 5379 var a = 't' + i; 5380 aliasToHandle[a] = h; 5381 return a + ': product(handle: ' + JSON.stringify(h) + ') {' + 5382 ' variants(first: 1) { nodes { id quantityAvailable selectedOptions { name value } } }' + 5383 ' dept: metafield(namespace: "BP_ProductAttributes", key: "bp_department") { value }' + 5384 ' }'; 5385 }); 5386 return fetch(apiUrl(), { 5387 method: 'POST', 5388 headers: { 5389 'Content-Type': 'application/json', 5390 'Accept': 'application/json', 5391 'X-Shopify-Storefront-Access-Token': TOKEN 5392 }, 5393 body: JSON.stringify({ query: '{' + parts.join(' ') + '}' }) 5394 }).then(function (r) { 5395 return r.ok ? r.json() : null; 5396 }).then(function (res) { 5397 var out = {}; 5398 var data = res && res.data; 5399 if (data) { 5400 Object.keys(aliasToHandle).forEach(function (a) { 5401 var p = data[a]; 5402 if (!p) return; 5403 var v = p.variants && p.variants.nodes && p.variants.nodes[0]; 5404 // Colour (option "Color") + size (option "Size") from the first 5405 // variant, so GA4 cards can fill item_category4 (colour) / 5406 // item_category5 (size). Size is the first variant's = the smallest 5407 // (matches the product id), per the GA4 QA. 5408 var so = (v && v.selectedOptions) || []; 5409 var color = '', size = ''; 5410 so.forEach(function (o) { 5411 var n = (o && o.name || '').toLowerCase(); 5412 if (n === 'color' || n === 'colour') color = o.value; 5413 else if (n === 'size') size = o.value; 5414 }); 5415 out[aliasToHandle[a]] = { 5416 variantId: (v && v.id) ? v.id.replace(/^gid:\/\/shopify\/ProductVariant\//, '') : '', 5417 quantityAvailable: (v && v.quantityAvailable != null) ? v.quantityAvailable : null, 5418 department: (p.dept && p.dept.value) || '', 5419 color: color, 5420 size: size 5421 }; 5422 }); 5423 } 5424 return out; 5425 }).catch(function () { return {}; }); 5426 } 5427 5428 /* ââ Canonical colour-family hydrator (shared: PDP swatch block + the 5429 quick-add / quick-view drawer) ââââââââââââââââââââââââââââââââââââââ 5430 Rebuilds the drift-free colour family for ONE [data-bp-colors] root via 5431 a bounded UNION crawl of every sibling's bp_stylecolorlist, then renders 5432 in canonical bp_style-ascending order. Because no single colour's own 5433 list is authoritative (each drifts), the union is identical on every 5434 colourway â so the swatch set + order never changes as the shopper 5435 switches colour. 5436 5437 Reads ALL its config from the root's own DOM + this module's AMPLIENCE 5438 base, so it works on any root: the PDP (where blocks/bp-color-siblings's 5439 inline script tag calls it) AND the drawer (where the morphed block's 5440 inline script tag NEVER executes â Horizon's morph only re-runs 5441 .shopify-app-block script[src], not theme-block inline scripts â so the
5442 drawer controller invokes this same shared function instead of forking 5443 the logic). See blocks/bp-color-siblings.liquid (PDP) and 5444 sections/bp-quick-drawer.liquid hydrateColorSiblings (drawer) for the two 5445 callers. Idempotent per root element. */ 5446 function hydrateRoot(root, opts) { 5447 opts = opts || {}; 5448 if (!root || root.__bpColorsHydrated) return; 5449 root.__bpColorsHydrated = true; 5450 var ul = root.querySelector('.bp-colors__swatches'); 5451 if (!ul) return; 5452 var labelName = root.querySelector('[data-bp-active-color]'); 5453 var activeItem = ul.querySelector('.bp-colors__item.is-active'); 5454 5455 var INV5 = root.getAttribute('data-bp-inv5-handle') || ''; 5456 var GROUP_TAG = root.getAttribute('data-bp-group-tag') || ''; 5457 var CURRENT_HANDLE = root.getAttribute('data-bp-current-handle') || ''; 5458 var CURRENT_COLOR = (activeItem && activeItem.getAttribute('data-bp-color-name')) 5459 || (labelName && (labelName.textContent || '').replace(/^\s+|\s+$/g, '')) || ''; 5460 var CURRENT_STYLE = (activeItem && activeItem.getAttribute('data-bp-color-style')) || ''; 5461 var QV = opts.qv || ''; 5462 5463 function handleize(s) { 5464 return String(s || '').toLowerCase().replace(/^\s+|\s+$/g, '') 5465 .replace(/[^a-z0-9]+/g, '-').replace(/^-+|-+$/g, ''); 5466 } 5467 function composeHandle(name) { return INV5 + '-' + handleize(name); } 5468 function poiswatch(style) { 5469 return AMPLIENCE + '/i/bostonproper/' + style + '_2?$poiswatch$&img404=' + style + '_1'; 5470 } 5471 function styleFromImage(src) { 5472 if (!src) return ''; 5473 var file = src.split('/').pop().split('?')[0]; 5474 var m = file.match(/^([A-Za-z0-9]+)_/); 5475 return m ? m[1] : ''; 5476 } 5477 // Parse bp_golivedate robustly across formats so a backend/feed format 5478 // change can't break the go-live window. Handles the current US 5479 // M/D/YYYY, ISO (YYYY-MM-DD / full ISO datetime), and epoch (seconds or 5480 // milliseconds). `new Date()` alone mis-reads epoch-seconds (as ms) and 5481 // is locale-dependent on M/D/YYYY, so we normalize first. 5482 function parseGoLive(v) { 5483 if (v == null || v === '') return null; 5484 var s = String(v).trim(); 5485 if (/^\d+$/.test(s)) { 5486 var n = Number(s); 5487 if (!isFinite(n)) return null; 5488 return new Date(n < 1e12 ? n * 1000 : n); // <1e12 â seconds 5489 } 5490 var m = s.match(/^(\d{1,2})[\/\-](\d{1,2})[\/\-](\d{4})$/); // M/D/YYYY 5491 if (m) return new Date(Number(m[3]), Number(m[1]) - 1, Number(m[2])); 5492 var d = new Date(s); // ISO (YYYY-MM-DD / full ISO) + anything else 5493 return isNaN(d.getTime()) ? null : d; 5494 } 5495 function withinGoLiveWindow(goLiveDate) { 5496 var d = parseGoLive(goLiveDate); 5497 if (!d || isNaN(d.getTime())) return false; 5498 var now = new Date(); 5499 if (d > now) return true; 5500 var days = Math.round((now.getTime() - d.getTime()) / 86400000); 5501 return days <= 45; 5502 } 5503 function paintDiscount(item, pct) { 5504 if (pct > 0 && !item.querySelector('[data-bp-color-discount]')) { 5505 var badge = document.createElement('span'); 5506 badge.className = 'bp-colors__discount'; 5507 badge.dataset.bpColorDiscount = ''; 5508 badge.setAttribute('aria-label', pct + '% off'); 5509 badge.textContent = '-' + pct + '%'; 5510 item.appendChild(badge); 5511 } 5512 } 5513 function wireName(item) { 5514 if (item.dataset.bpNameWired) return; 5515 item.dataset.bpNameWired = '1'; 5516 var show = function () { if (labelName && item.dataset.bpColorName) labelName.textContent = item.dataset.bpColorName; }; 5517 var reset = function () { if (labelName) labelName.textContent = CURRENT_COLOR; }; 5518 item.addEventListener('mouseenter', show); 5519 item.addEventListener('mouseleave', reset); 5520 item.addEventListener('focusin', show); 5521 item.addEventListener('focusout', reset); 5522 } 5523 function swatchOf(item) { return item.querySelector('[data-bp-swatch]'); } 5524 function paintCrop(item, style) { 5525 if (!style) return; 5526 var sw = swatchOf(item); 5527 if (sw && !sw.style.backgroundImage) sw.style.backgroundImage = "url('" + poiswatch(style) + "')"; 5528 item.dataset.bpColorStyle = style; 5529 } 5530 5531 var nameByHandle = {}; 5532 Array.prototype.forEach.call(ul.querySelectorAll('.bp-colors__item'), function (li) { 5533 var h = li.dataset.bpColorHandle; 5534 if (h && !(h in nameByHandle)) nameByHandle[h] = li.dataset.bpColorName || ''; 5535 }); 5536 if (CURRENT_HANDLE) nameByHandle[CURRENT_HANDLE] = CURRENT_COLOR; 5537 5538 var fetched = {}; // handle -> info | null (null = no product at that handle) 5539 5540 function unionFromFetched() {
5541 Object.keys(fetched).forEach(function (h) { 5542 var info = fetched[h]; 5543 if (info && info.colorList) { 5544 info.colorList.split(',').forEach(function (raw) { 5545 var name = raw.replace(/^\s+|\s+$/g, ''); 5546 if (!name) return; 5547 var sh = composeHandle(name); 5548 if (!(sh in nameByHandle)) nameByHandle[sh] = name; 5549 }); 5550 } 5551 }); 5552 } 5553 5554 /* PRIMARY family source: the group tag. Returns the REAL siblings with 5555 REAL handles + Colour-option names, so nameByHandle is the true family 5556 and every colour is reachable from every colourway â no composed-handle 5557 guessing. We pre-populate `fetched` from the same response (it carries 5558 style/available/goLive/discount/colorList) so render() needs no extra 5559 round-trip, and we DELIBERATELY skip the colorListâcomposeHandle union 5560 here: on bad-data families (2DO2220) that union would re-introduce the 5561 bogus composed handles this path exists to avoid. Resolves true on 5562 success, false when the group query yields nothing (caller falls back to 5563 the composed-handle crawl). */ 5564 function groupCrawl() { 5565 return fetchByGroup(GROUP_TAG).then(function (byHandle) { 5566 var handles = Object.keys(byHandle || {}); 5567 if (!handles.length) return false; 5568 handles.forEach(function (h) { 5569 fetched[h] = byHandle[h]; 5570 if (byHandle[h] && !(h in nameByHandle)) nameByHandle[h] = byHandle[h].color || ''; 5571 }); 5572 // Keep the current colourway's server-rendered label to avoid a flash; 5573 // only fill from the group colour when the seed had none. 5574 if (CURRENT_HANDLE) nameByHandle[CURRENT_HANDLE] = CURRENT_COLOR || nameByHandle[CURRENT_HANDLE] || ''; 5575 return true; 5576 }); 5577 } 5578 5579 function crawl() { 5580 var MAX_ROUNDS = 3; 5581 var round = 0; 5582 function nextBatch() { 5583 var toFetch = Object.keys(nameByHandle).filter(function (h) { return !(h in fetched); }); 5584 if (!toFetch.length || round >= MAX_ROUNDS) return Promise.resolve(); 5585 round++; 5586 return fetchSiblings(toFetch).then(function (byHandle) {
5587 toFetch.forEach(function (h) { fetched[h] = byHandle[h] || null; }); 5588 unionFromFetched(); 5589 return nextBatch(); 5590 }); 5591 } 5592 return nextBatch(); 5593 } 5594 5595 function makeItem(handle, name) { 5596 var li = document.createElement('li'); 5597 li.className = 'bp-colors__item bp-colors__item--pending'; 5598 li.setAttribute('data-bp-color-name', name || ''); 5599 li.setAttribute('data-bp-color-handle', handle); 5600 li.setAttribute('data-bp-color-style', ''); 5601 var a = document.createElement('a'); 5602 a.className = 'bp-colors__swatch'; 5603 a.href = '/products/' + handle + QV; 5604 a.setAttribute('data-bp-swatch', ''); 5605 a.setAttribute('aria-label', name || ''); 5606 li.appendChild(a); 5607 return li; 5608 } 5609 5610 // Reconcile the whole row to canonical order in ONE pass, then reveal. 5611 // Every item stays hidden (--pending) while the crawl runs, so moving 5612 // nodes into sorted position causes NO visible movement; a single reveal 5613 // at the end drops each swatch straight into its final place. Canonical 5614 // order = ascending bp_style code (the merchandised sequence), identical 5615 // on every colourway. 5616 function render() { 5617 var existing = {}; 5618 Array.prototype.forEach.call(ul.querySelectorAll('.bp-colors__item'), function (li) { 5619 if (li.dataset.bpColorHandle) existing[li.dataset.bpColorHandle] = li; 5620 }); 5621 var entries = Object.keys(nameByHandle).map(function (h) { 5622 var info = fetched[h]; 5623 var isCurrent = (h === CURRENT_HANDLE); 5624 var visible; 5625 if (isCurrent) visible = true; 5626 else if (!info) visible = false; 5627 else if (info.available) visible = true; 5628 else visible = withinGoLiveWindow(info.goLiveDate); 5629 var li = existing[h]; 5630 var style = (info && info.style) ? info.style 5631 : ((li && li.dataset.bpColorStyle) ? li.dataset.bpColorStyle : (isCurrent ? CURRENT_STYLE : '')); 5632 return { h: h, info: info, isCurrent: isCurrent, visible: visible, style: style || '', li: li }; 5633 }); 5634 entries.sort(function (a, b) { 5635 if (a.style !== b.style) return a.style < b.style ? -1 : 1; 5636 return a.h < b.h ? -1 : (a.h > b.h ? 1 : 0); 5637 }); 5638 entries.forEach(function (e) { 5639 if (e.visible && !e.li) { e.li = makeItem(e.h, nameByHandle[e.h]); existing[e.h] = e.li; } 5640 if (!e.li) return; 5641 if (e.visible) { 5642 paintCrop(e.li, e.style); 5643 if (e.info) paintDiscount(e.li, e.info.discount); 5644 if (!e.isCurrent) wireName(e.li); 5645 } else { 5646 e.li.classList.add('bp-colors__item--pending'); 5647 } 5648 ul.appendChild(e.li); 5649 }); 5650 entries.forEach(function (e) { 5651 if (e.visible && e.li) e.li.classList.remove('bp-colors__item--pending'); 5652 }); 5653 } 5654 5655 function paintActive() { 5656 var active = ul.querySelector('.bp-colors__item.is-active'); 5657 if (!active) return; 5658 var sw = swatchOf(active); 5659 if (!sw || sw.style.backgroundImage) return; 5660 var info = fetched[CURRENT_HANDLE]; 5661 var style = (info && info.style) ? info.style : CURRENT_STYLE; 5662 if (style) { sw.style.backgroundImage = "url('" + poiswatch(style) + "')"; active.dataset.bpColorStyle = style; } 5663 } 5664 function revealActive() { 5665 var active = ul.querySelector('.bp-colors__item.is-active'); 5666 if (active) active.classList.remove('bp-colors__item--pending'); 5667 paintActive(); 5668 } 5669 5670 // Fallback (Storefront token/API unavailable): reveal in-stock seed items 5671 // and paint their crop from /products/{handle}.js filenames. Can't build 5672 // the union without metafields, so it degrades to the seed list only. 5673 function legacyFallback() { 5674 Array.prototype.forEach.call(ul.querySelectorAll('.bp-colors__item:not(.is-active)'), function (item) { 5675 var handle = item.dataset.bpColorHandle; 5676 if (!handle) return; 5677 wireName(item); 5678 var sw = swatchOf(item); 5679 var style = item.dataset.bpColorStyle || ''; 5680 fetch('/products/' + handle + '.js', { credentials: 'same-origin' }) 5681 .then(function (r) { return r.ok ? r.json() : null; }) 5682 .then(function (data) { 5683 if (!data || data.available === false) { item.classList.add('bp-colors__item--pending'); return; } 5684 if (!style && data.featured_image) style = styleFromImage(data.featured_image); 5685 if (!style) { item.classList.add('bp-colors__item--pending'); return; } 5686 if (sw && !sw.style.backgroundImage) { sw.style.backgroundImage = "url('" + poiswatch(style) + "')"; item.dataset.bpColorStyle = style; } 5687 if (data.compare_at_price && data.price && data.compare_at_price > data.price) { 5688 paintDiscount(item, Math.round(((data.compare_at_price - data.price) / data.compare_at_price) * 100)); 5689 } 5690 item.classList.remove('bp-colors__item--pending'); 5691 }) 5692 .catch(function () { item.classList.add('bp-colors__item--pending'); }); 5693 }); 5694 } 5695 5696 if (TOKEN) { 5697 // Hide EVERY item (incl. the active swatch) up front so the single 5698 // ordered reveal after the crawl has zero visible reflow. 5699 Array.prototype.forEach.call(ul.querySelectorAll('.bp-colors__item'), function (li) { 5700 li.classList.add('bp-colors__item--pending'); 5701 }); 5702 // Prefer group-tag discovery (drift-proof). Fall back to the composed- 5703 // handle union crawl when there's no group tag or the group query is 5704 // empty/errors, then to the legacy per-sibling .js path. 5705 var primary; 5706 if (GROUP_TAG) { 5707 primary = groupCrawl() 5708 .then(function (ok) { return ok ? true : crawl(); }) 5709 .catch(function () { return crawl(); }); 5710 } else { 5711 primary = crawl(); 5712 } 5713 primary 5714 .then(function () { render(); }) 5715 .catch(function () { revealActive(); legacyFallback(); }); 5716 } else { 5717 legacyFallback(); 5718 paintActive(); 5719 } 5720 } 5721 5722 return { 5723 fetch: fetchSiblings, 5724 fetchByGroup: fetchByGroup, 5725 fetchInventory: fetchInventory, 5726 fetchTrackData: fetchTrackData, 5727 swatchUrl: swatchUrl, 5728 hydrateRoot: hydrateRoot, 5729 amplience: AMPLIENCE 5730 }; 5731 })(); 5732</script>
5732<script> 5733 window.bpColorSwatches = (function () { 5734 var TOKEN = "04534ca2c4aa793f02c0c735d5d6160c"; 5735 var API_VERSION = '2024-07'; 5736 var CACHE_KEY = 'bp_color_swatches_v1'; 5737 var map = {}; 5738 var ready = false; 5739 5740 function apiUrl() { 5741 var root = (window.Shopify && Shopify.routes && Shopify.routes.root) ? Shopify.routes.root : '/'; 5742 return root + 'api/' + API_VERSION + '/graphql.json'; 5743 } 5744 // MUST match bp-bloomreach.js handleize() so facet-value lookups line up: 5745 // lowercase, non-alphanumeric runs -> '-', trim leading/trailing dashes. 5746 function key(name) { 5747 return String(name == null ? '' : name) 5748 .toLowerCase().trim() 5749 .replace(/[^a-z0-9]+/g, '-') 5750 .replace(/^-+|-+$/g, ''); 5751 } 5752 function get(name) { return map[key(name)] || ''; } 5753 5754 // 1) hydrate synchronously from cache (removes the first-paint race on 5755 // every PLP view after the first in a session). 5756 try { 5757 var cached = sessionStorage.getItem(CACHE_KEY); 5758 if (cached) { map = JSON.parse(cached) || {}; ready = true; } 5759 } catch (_) {} 5760 5761 // 2) fetch fresh (paginated) and refresh the cache. 5762 function page(after) { 5763 var q = '{ metaobjects(type: "product_color_swatches", first: 250' + 5764 (after ? ', after: ' + JSON.stringify(after) : '') + 5765 ') { pageInfo { hasNextPage endCursor } edges { node { fields { key value } } } } }'; 5766 return fetch(apiUrl(), { 5767 method: 'POST', 5768 headers: { 5769 'Content-Type': 'application/json', 5770 'Accept': 'application/json', 5771 'X-Shopify-Storefront-Access-Token': TOKEN 5772 }, 5773 body: JSON.stringify({ query: q }) 5774 }).then(function (r) { 5775 if (!r.ok) throw new Error('storefront HTTP ' + r.status); 5776 return r.json(); 5777 }); 5778 } 5779 function load() { 5780 if (!TOKEN) return; 5781 var acc = {}; 5782 function walk(after) { 5783 return page(after).then(function (res) { 5784 var mo = res && res.data && res.data.metaobjects; 5785 if (!mo) return; 5786 (mo.edges || []).forEach(function (e) { 5787 var f = {}; 5788 (e.node.fields || []).forEach(function (x) { f[x.key] = x.value; }); 5789 // Prefer an optional `css_background` TEXT field (holds a full CSS 5790 // background â a hex OR a linear-gradient for a shimmer), falling 5791 // back to the existing `css_color` Color field (single hex only â 5792 // a Color metafield can't store a gradient). Applied via 5793 // `background:` so either renders. Add a single-line text field 5794 // named `css_background` to the metaobject to enable gradients. 5795 var val = f.css_background || f.css_color; 5796 if (f.name && val) acc[key(f.name)] = val; 5797 }); 5798 if (mo.pageInfo && mo.pageInfo.hasNextPage) return walk(mo.pageInfo.endCursor); 5799 }); 5800 } 5801 walk(null).then(function () { 5802 if (!Object.keys(acc).length) return; // empty response â keep cache/fallback 5803 map = acc; 5804 ready = true; 5805 try { sessionStorage.setItem(CACHE_KEY, JSON.stringify(acc)); } catch (_) {} 5806 try { window.dispatchEvent(new CustomEvent('bp:color-swatches-ready')); } catch (_) {} 5807 }).catch(function () { /* keep cache; fall back to raw CSS colour names */ }); 5808 } 5809 load(); 5810 5811 return { get: get, key: key, isReady: function () { return ready; } }; 5812 })(); 5813</script>
vendor: 1 bytes, line 5813
5813
5814<script type="application/ld+json"> 5815{ 5816 "@context": "https://schema.org", 5817 "@graph": [ 5818 { 5819 "@type": "Organization", 5820 "@id": "https:\/\/www.bostonproper.com\/#organization", 5821 "name": "Boston Proper", 5822 "url": "https:\/\/www.bostonproper.com\/", 5823 "logo": "https:\/\/www.bostonproper.com\/cdn\/shop\/files\/BOS-Logotype-Charcoal_1.png?v=1785187523\u0026width=500", 5824 "description": "Shop women's dresses, statement tops, polished pants, shoes, accessories, and travel-ready styles designed to fit beautifully and own every room. ", 5825 "parentOrganization": { 5826 "@type": "Organization", 5827 "name": "Brentwood Associates" 5828 }, 5829 "founder": { "@type": "Person", "name": "Sheryl Clark" }, 5830 "foundingDate": "1992", 5831 "sameAs": [ 5832 "https:\/\/www.facebook.com\/bostonproper", 5833 "https:\/\/www.instagram.com\/bostonproperofficial\/", 5834 "https:\/\/www.pinterest.com\/bostonproper\/", 5835 "https:\/\/www.youtube.com\/@bostonproper", 5836 "https:\/\/x.com\/bostonproper", 5837 "https:\/\/www.tiktok.com\/@bostonproperofficial", 5838 "https:\/\/www.bostonproper.com\/pages\/about-us" 5839 ], 5840 "contactPoint": [ 5841 { 5842 "@type": "ContactPoint", 5843 "telephone": "+1-800-243-4300", 5844 "contactType": "customer service", 5845 "contactOption": "TollFree", 5846 "areaServed": "US", 5847 "availableLanguage": "en" 5848 } 5849 ], 5850 "hasMerchantReturnPolicy": { 5851 "@type": "MerchantReturnPolicy", 5852 "applicableCountry": "US", 5853 "returnPolicyCategory": "https://schema.org/MerchantReturnFiniteReturnWindow", 5854 "merchantReturnDays": 30, 5855 "returnMethod": "https://schema.org/ReturnByMail", 5856 "returnFees": "https://schema.org/ReturnShippingFees", 5857 "returnShippingFeesAmount": { 5858 "@type": "MonetaryAmount", 5859 "value": "6.95", 5860 "currency": "USD" 5861 } 5862 } 5863 }, 5864 { 5865 "@type": "WebSite", 5866 "@id": "https:\/\/www.bostonproper.com\/#website", 5867 "url": "https:\/\/www.bostonproper.com\/", 5868 "name": "Boston Proper", 5869 "publisher": { "@id": "https:\/\/www.bostonproper.com\/#organization" }, 5870 "inLanguage": "en-US", 5871 "potentialAction": { 5872 "@type": "SearchAction", 5873 "target": { 5874 "@type": "EntryPoint", 5875 "urlTemplate": "https://www.bostonproper.com/search?q={search_term_string}" 5876 }, 5877 "query-input": "required name=search_term_string" 5878 } 5879 } 5880 ] 5881} 5882</script>
vendor: 1 bytes, line 5882
5882
5883<script> 5884 (function () { 5885 // ----- Customer + order state (Liquid-emitted, no extra fetch) ----- 5886 window.__customer_id__ = ""; 5887 window.__customer_type__ = "new"; 5888 window.__orders_count__ = 0; 5889 5890 // ----- Plain-text PII (Enhanced Conversions / vendor pixels) ----- 5891 // Nested-object shape + key names match Atlas's ga4_globals.js.liquid 5892 // (email / phone_number / first_name / last_name / street / city / 5893 // state / zip_code / country) so the GTM-NS3X79G container variables 5894 // resolve. 5895 // Absent fields are emitted as `undefined` (not ''), matching Atlas's 5896 // ga4_globals.js.liquid and __user_hashed_data__ below: a field that 5897 // does not exist should read undefined, never an empty string. 5898 window.__enhanced_conversion_data__ = { 5899 email: undefined, 5900 phone_number: undefined, 5901 first_name: undefined, 5902 last_name: undefined, 5903 street: undefined, 5904 city: undefined, 5905 state: undefined, 5906 zip_code: undefined, 5907 country: undefined 5908 }; 5909 5910 // ----- SHA-256 hashed PII for GA4 Enhanced Conversions ----- 5911 // Hashed SERVER-SIDE via Liquid `| sha256` (exactly like Atlas), so 5912 // it's available SYNCHRONOUSLY at the first event push. The previous 5913 // client-side SubtleCrypto version resolved async, so early events 5914 // (view_item / page_loaded) shipped empty hashes. undefined when 5915 // absent (Atlas parity â never ''). 5916 window.__user_hashed_data__ = { 5917 email: undefined, 5918 phone_number: undefined, 5919 first_name: undefined, 5920 last_name: undefined, 5921 street: undefined, 5922 city: undefined, 5923 state: undefined, 5924 zip_code: undefined, 5925 country: undefined 5926 }; 5927 5928 // ----- user_details (spec: localStorage gtm_user_data backup) ----- 5929 // The GA4/GTM analytics team's spec: every event's user_details is 5930 // built from the logged-in customer (Liquid globals above) with the 5931 // checkout-captured localStorage `gtm_user_data` key as a BACKUP for 5932 // guests. Plain text â enhanced_conversion_data; SHA-256(plain) â 5933 // user_hashed_data (hashed client-side here, since the localStorage 5934 // backup has no server-side hashes). Built ONCE and cached in 5935 // window.__bp_user_details__ so per-event pushes stay synchronous; 5936 // bpUserDetailsSync() is the interim shape used before the async 5937 // hashing settles (page_loaded fires at DOMContentLoaded). 5938 // 5939 // Shape follows the spec exactly: user_id is the (future) Peak id, 5940 // null until that integration lands; the Shopify customer id now 5941 // lives on customer_id. NOTE: the GTM-NS3X79G container must be 5942 // remapped to read customer_id for GA4 User-ID (it previously read 5943 // user_id). 5944 function bpGtmUserData() { 5945 try { return JSON.parse(localStorage.getItem('gtm_user_data')) || null; } 5946 catch (_) { return null; } 5947 } 5948 5949 // First non-empty of the args (null/undefined/'' are "empty"). 5950 function bpCoalesce() { 5951 for (var i = 0; i < arguments.length; i++) { 5952 var v = arguments[i]; 5953 if (v != null && v !== '') return v; 5954 } 5955 return null; 5956 } 5957 5958 function bpUserDetailsSync() { 5959 var ls = bpGtmUserData() || {}; 5960 var ecd = window.__enhanced_conversion_data__ || {}; 5961 // gtm_user_data field names â enhanced_conversion_data key names. 5962 var lsm = { 5963 email: ls.em, phone_number: ls.ph, first_name: ls.fn, 5964 last_name: ls.ln, street: ls.street, city: ls.ct, 5965 state: ls.st, zip_code: ls.zp, country: ls.country 5966 }; 5967 var loggedIn = !!(window.__customer_id__ && String(window.__customer_id__) !== ''); 5968 // Logged-in â Liquid customer is authoritative, localStorage backfills. 5969 // Guest â localStorage (checkout capture) is authoritative. 5970 var primary = loggedIn ? ecd : lsm; 5971 var backup = loggedIn ? lsm : ecd; 5972 function P(k) { return bpCoalesce(primary[k], backup[k]); } 5973 5974 var ordersCount = loggedIn 5975 ? bpCoalesce(window.__orders_count__, ls.ordersCount) 5976 : bpCoalesce(ls.ordersCount, window.__orders_count__); 5977 if (ordersCount != null) ordersCount = Number(ordersCount); 5978 var customerId = bpCoalesce(window.__customer_id__, ls.userId); 5979 5980 return { 5981 log_state: customerId ? 'Logged In' : 'Logged Out', 5982 user_id: null, // Peak/Shopify user id â wired later 5983 customer_id: customerId, 5984 customer_type: (ordersCount != null && ordersCount >= 1) ? 'returning' : 'new', 5985 orders_count: ordersCount, 5986 is_first_order: ordersCount != null ? (ordersCount < 1) : null, 5987 enhanced_conversion_data: { 5988 email: P('email'), 5989 phone_number: P('phone_number'), 5990 first_name: P('first_name'), 5991 last_name: P('last_name'), 5992 street: P('street'), 5993 city: P('city'), 5994 state: P('state'), 5995 zip_code: P('zip_code'), 5996 country: P('country') 5997 }, 5998 // Interim: Liquid server-side hashes (logged-in) until the async 5999 // client-side hash of the merged plain text overwrites this. 6000 user_hashed_data: window.__user_hashed_data__ || {} 6001 }; 6002 } 6003 6004 // SHA-256 â lowercase hex (spec: trim + lowercase normalization). 6005 function bpSha256(value) { 6006 if (value == null || value === '') return Promise.resolve(null);
6007 try { 6008 var norm = String(value).trim().toLowerCase(); 6009 var data = new TextEncoder().encode(norm); 6010 return crypto.subtle.digest('SHA-256', data).then(function (buf) { 6011 var arr = Array.prototype.slice.call(new Uint8Array(buf)); 6012 return arr.map(function (b) { return ('0' + b.toString(16)).slice(-2); }).join(''); 6013 }); 6014 } catch (_) { return Promise.resolve(null); } 6015 } 6016 6017 function bpBuildUserDetails() { 6018 var ud = bpUserDetailsSync(); 6019 var e = ud.enhanced_conversion_data; 6020 var keys = ['email','phone_number','first_name','last_name','street','city','state','zip_code','country']; 6021 return Promise.all(keys.map(function (k) { return bpSha256(e[k]); })) 6022 .then(function (hashes) { 6023 var hashed = {}; 6024 keys.forEach(function (k, i) { hashed[k] = hashes[i]; }); 6025 ud.user_hashed_data = hashed; 6026 return ud; 6027 }); 6028 } 6029 6030 window.bpUserDetailsSync = bpUserDetailsSync; 6031 window.__bp_user_details__ = null; // populated async, read synchronously by events 6032 try { 6033 if (window.crypto && window.crypto.subtle && window.TextEncoder) { 6034 bpBuildUserDetails().then(function (ud) { window.__bp_user_details__ = ud; }); 6035 } 6036 } catch (_) { /* insecure context / no subtle â sync fallback covers it */ } 6037 6038 // ----- First-touch attribution (session-scoped) ----- 6039 // Persists the FIRST page of the user's session so every later 6040 // event can attribute back to landing context. Mirrors Atlas's 6041 // sessionStorage usage in ga4.js.liquid. 6042 try { 6043 var STORAGE_KEY = 'bp_first_touch'; 6044 var stored = sessionStorage.getItem(STORAGE_KEY); 6045 if (stored) { 6046 window.__first_touch__ = JSON.parse(stored); 6047 } else { 6048 window.__first_touch__ = { 6049 page: window.location.pathname, 6050 full_url: window.location.href, 6051 template: "product", 6052 referrer: document.referrer || '', 6053 timestamp: Date.now() 6054 }; 6055 sessionStorage.setItem(STORAGE_KEY, JSON.stringify(window.__first_touch__)); 6056 } 6057 } catch (_) { 6058 window.__first_touch__ = { 6059 page: window.location.pathname, 6060 template: "product" 6061 }; 6062 } 6063 6064 // ----- Global DataLayer config â debug flag + shared shape ----- 6065 window.__DL__ = window.__DL__ || { 6066 debug: false, 6067 cart: null, // populated by view_cart / cart change events 6068 lastEvent: null // last event name pushed (for dedupe / tests) 6069 }; 6070 6071 // ----- Ensure dataLayer exists even if GTM hasn't booted yet ----- 6072 window.dataLayer = window.dataLayer || []; 6073 })(); 6074</script>
vendor: 1 bytes, line 6074
6074
6075<script>window.__pageType__ = "product";</script>
6075<script src="//www.bostonproper.com/cdn/shop/t/1521/assets/bp-track-gtm.js?v=183060181947548063931790713714" defer></script>
6075 6076
6076<script src="//www.bostonproper.com/cdn/shop/t/1521/assets/bp-track-page.js?v=89286407019962503341790713714" defer></script>
6076 6077
6077<script src="//www.bostonproper.com/cdn/shop/t/1521/assets/bp-track-cards.js?v=119320205335911998891790713714" defer></script>
6077<script src="//www.bostonproper.com/cdn/shop/t/1521/assets/bp-badges.js?v=45136502598786020341790713714" defer></script>
6077<script src="//www.bostonproper.com/cdn/shop/t/1521/assets/bp-card-hydrate.js?v=169242559010489351881790713714" defer></script>
6077<script src="//www.bostonproper.com/cdn/shop/t/1521/assets/bp-line-item-props.js?v=113126442776160060221790713714" defer></script>
6077<script> 6078 (function () { 6079 'use strict'; 6080 if (window.__bpTrackCartWired) return; 6081 window.__bpTrackCartWired = true; 6082 6083 var lastCartSnapshot = null; // for remove diff detection 6084 6085 function snapshotCart(cart) { 6086 if (!cart || !cart.items) return null; 6087 var map = {}; 6088 cart.items.forEach(function (line) { 6089 map[String(line.id || line.key)] = { 6090 key: line.key, 6091 id: line.id, 6092 variant_id: line.variant_id || line.id, // â item_shopify_variant 6093 product_id: line.product_id, 6094 product_title: line.product_title || line.title, 6095 vendor: line.vendor, 6096 product_type: line.product_type, 6097 sku: line.sku, 6098 price: line.price, 6099 quantity: line.quantity, 6100 handle: line.handle, 6101 url: line.url, 6102 featured_image: line.featured_image, 6103 image: line.image, 6104 options_with_values: line.options_with_values || [] 6105 }; 6106 }); 6107 return map; 6108 } 6109 6110 function detectRemovedLines(prev, next) { 6111 if (!prev) return []; 6112 var removed = []; 6113 Object.keys(prev).forEach(function (k) { 6114 if (!next || !next[k]) { 6115 removed.push(prev[k]); 6116 } else if (next[k].quantity < prev[k].quantity) { 6117 var line = Object.assign({}, prev[k]); 6118 line.quantity = prev[k].quantity - next[k].quantity; 6119 removed.push(line); 6120 } 6121 }); 6122 return removed; 6123 } 6124 6125 function detectAddedLines(prev, next) { 6126 if (!next) return []; 6127 var added = []; 6128 Object.keys(next).forEach(function (k) { 6129 if (!prev || !prev[k]) { 6130 added.push(next[k]); 6131 } else if (next[k].quantity > prev[k].quantity) { 6132 var line = Object.assign({}, next[k]); 6133 line.quantity = next[k].quantity - prev[k].quantity; 6134 added.push(line); 6135 } 6136 }); 6137 return added; 6138 } 6139 6140 // Where a cart mutation happened: the /cart page ("Cart") vs the 6141 // slide-out flyout drawer ("Cart Flyout"). Used for in-cart qty 6142 // changes + removals. 6143 function cartMutationLocation() { 6144 // The visible SURFACE wins over the path: if the flyout drawer is open, 6145 // a qty change / removal is "Cart Flyout" even on the /cart page (QA â 6146 // a flyout remove was reporting "Cart" because the path check beat the 6147 // actual surface). 6148 var dlg = document.querySelector('[data-bp-cart-dialog]'); 6149 if (dlg && dlg.open) return 'Cart Flyout'; 6150 var p = window.location.pathname; 6151 if (p === '/cart' || p.indexOf('/cart?') === 0) return 'Cart'; 6152 return 'Cart Flyout'; 6153 } 6154 6155 // Where an INITIAL add-to-cart happened. When a Quickview drawer is the 6156 // source, attribute to the SAME surface the view_item used: "Quickview" 6157 // for the PLP quickview, "Cart Quickview" for the cart flyout / cart-page 6158 // upsell (complete-the-look) modal. window.__bpQuickviewLoc is set on open 6159 // by bp-track-cards.js (fireQuickview / bpTrackCartQuickview); reading it 6160 // here â instead of hardcoding "Quickview" â makes the add_to_cart 6161 // event_location/item_list_id/location_id match the view_item, so GA4 can 6162 // compute an Add-to-Cart Rate for Cart Quickview and tell it apart from the 6163 // PLP Quickview (GA4 QA). Falls back to "Quickview" if the surface is 6164 // somehow unset. Otherwise let add_to_cart resolve from the path (PDP). 6165 function initialAddLocation() { 6166 return window.__bpQuickviewActive ? (window.__bpQuickviewLoc || 'Quickview') : undefined; 6167 } 6168 6169 // Normalize a cart-line snapshot into the (variant, product) tuple 6170 // bpTrack.add_to_cart expects, then fire it with the given location. 6171 function fireAddLine(line, location) { 6172 if (!window.bpTrack || !window.bpTrack.add_to_cart) return; 6173 var product = { 6174 id: line.product_id, 6175 title: line.product_title, 6176 vendor: line.vendor, 6177 product_type: line.product_type, 6178 handle: line.handle, 6179 url: line.url, 6180 // cart.js gives featured_image as an object; fall back to the
6181 // legacy line.image string. Feeds the Klaviyo Added to Cart 6182 // ImageURL / URL / Categories (GA4 items[] don't read these). 6183 featured_image: (line.featured_image && line.featured_image.url) || line.image || '' 6184 }; 6185 var variant = { 6186 id: line.variant_id || line.id, // â item_shopify_variant 6187 sku: line.sku, 6188 price: line.price, 6189 compare_at_price: null, 6190 // A line in the cart is purchasable â "In Stock". Without this, 6191 // _itemFromProduct defaults undefined-available to "Out of Stock". 6192 available: true, 6193 option1: line.options_with_values && line.options_with_values[0] && line.options_with_values[0].value, 6194 option2: line.options_with_values && line.options_with_values[1] && line.options_with_values[1].value, 6195 product_id: line.product_id 6196 }; 6197 window.bpTrack.add_to_cart(variant, product, true, location, line.product_id); 6198 } 6199 6200 // Initial add-to-cart (product:added / cart:update) â diff for ADDED 6201 // lines only and fire add_to_cart with the source-surface location. 6202 function fireAddedToCart(cart, location) { 6203 var next = snapshotCart(cart); 6204 if (lastCartSnapshot) { 6205 detectAddedLines(lastCartSnapshot, next).forEach(function (line) { 6206 fireAddLine(line, location); 6207 }); 6208 } 6209 lastCartSnapshot = next; 6210 } 6211 6212 // In-cart mutation (cart:updated from the qty stepper / remove) â diff 6213 // BOTH directions against the SAME prior snapshot so a qty increase 6214 // fires add_to_cart ("Cart Flyout"/"Cart") and a decrease/remove fires 6215 // remove_from_cart. Computed before the snapshot is replaced so the two 6216 // diffs don't race each other. 6217 function fireCartMutation(cart) { 6218 var next = snapshotCart(cart); 6219 var loc = cartMutationLocation(); 6220 if (lastCartSnapshot) { 6221 detectRemovedLines(lastCartSnapshot, next).forEach(function (line) { 6222 if (window.bpTrack && window.bpTrack.remove_from_cart) { 6223 window.bpTrack.remove_from_cart(line, loc); 6224 } 6225 }); 6226 detectAddedLines(lastCartSnapshot, next).forEach(function (line) { 6227 fireAddLine(line, loc); 6228 }); 6229 } 6230 lastCartSnapshot = next; 6231 } 6232 6233 function fireViewCart(location) { 6234 if (window.bpTrack && window.bpTrack.view_cart) { 6235 window.bpTrack.view_cart(location); 6236 } 6237 } 6238 6239 document.addEventListener('DOMContentLoaded', function () { 6240 // Seed snapshot from the initial /cart.js (so the first remove 6241 // detected by the diff has a baseline to compare against). 6242 fetch('/cart.js', { credentials: 'same-origin' }) 6243 .then(function (r) { return r.json(); }) 6244 .then(function (cart) { lastCartSnapshot = snapshotCart(cart); }) 6245 .catch(function () { /* silent */ }); 6246 6247 // bp:cart-drawer:opened â view_cart from the flyout ("Cart Flyout"). 6248 // NOTE: do NOT clear __bpQuickviewActive here. A Quickview add opens the 6249 // cart drawer, so clearing on drawer-open wiped the "Quickview" 6250 // attribution before the add_to_cart (from product:added) could read it 6251 // â the QA "Quickview ATC reports Site Browse" bug. The quick-add modal's 6252 // own `close` event + the 30s safety timeout (bp-track-cards.js) clear it, 6253 // and later in-cart qty edits use cartMutationLocation(), not this flag. 6254 document.addEventListener('bp:cart-drawer:opened', function () { 6255 fireViewCart('Cart Flyout'); 6256 }); 6257 6258 // cart:updated comes from bpCartChange (the qty stepper / remove) â 6259 // diff BOTH directions: qty up â add_to_cart, qty down/remove â 6260 // remove_from_cart, both located "Cart"/"Cart Flyout". 6261 document.addEventListener('cart:updated', function (e) { 6262 if (e && e.detail && e.detail.cart) { 6263 fireCartMutation(e.detail.cart); 6264 } 6265 }); 6266 6267 // product:added (Horizon / bp-cart-drawer / bp-swym) + 6268 // cart:update (Horizon stock event) â fired on PDP, quick-add, 6269 // wishlistâcart, and any other ATC path. Diff the cart against 6270 // the last snapshot to detect ADDED lines and fire add_to_cart 6271 // for each one. Universal coverage; per-source helpers can fire 6272 // add_to_cart ahead of this if they have richer attribution 6273 // (the GA4 events will deduplicate on event_id at the GTM side 6274 // if that ever becomes an issue). 6275 document.addEventListener('product:added', function (e) { 6276 var loc = initialAddLocation(); 6277 if (e && e.detail && e.detail.cart) { 6278 fireAddedToCart(e.detail.cart, loc); 6279 } else { 6280 fetch('/cart.js', { credentials: 'same-origin' }) 6281 .then(function (r) { return r.json(); }) 6282 .then(function (cart) { fireAddedToCart(cart, loc); }) 6283 .catch(function () {}); 6284 } 6285 }); 6286 document.addEventListener('cart:update', function () { 6287 var loc = initialAddLocation(); 6288 fetch('/cart.js', { credentials: 'same-origin' }) 6289 .then(function (r) { return r.json(); }) 6290 .then(function (cart) { fireAddedToCart(cart, loc); }) 6291 .catch(function () {}); 6292 }); 6293 6294 // On the /cart page load, fire view_cart immediately too ("Cart"). 6295 if (window.location.pathname === '/cart' || window.location.pathname.indexOf('/cart?') === 0) { 6296 fireViewCart('Cart'); 6297 } 6298 }); 6299 })(); 6300</script>
vendor: 1 bytes, line 6300
6300
6301<script> 6302 (function () { 6303 'use strict'; 6304 window.__bpPdpProduct = { 6305 id: 15286962618534, 6306 title: "Boston Proper Printed Scarf With Love BP", 6307 vendor: "Boston Proper", 6308 handle: "boston-proper-printed-scarf-with-love-bp", 6309 url: "\/products\/boston-proper-printed-scarf-with-love-bp", 6310 image: "\/\/www.bostonproper.com\/cdn\/shop\/files\/2CT8071_SET_e6f3e3ce-5a5f-451b-bc3b-ea1f8f71d236.jpg?v=1791009198\u0026width=720", 6311 product_type: null, 6312 metafields: { BP_ProductAttributes: { bp_department: "Promotions" } }, 6313 variants: [56389031657638] 6314 }; 6315 window.__bpPdpVariant = { 6316 id: 56389031657638, 6317 sku: "2CT8071000", 6318 price: 50.0, 6319 compare_at_price: 50.0, 6320 option1: "With Love BP", 6321 option2: "One Size", 6322 product_id: 15286962618534, 6323 available: true, 6324 inventory_quantity: 2617 6325 }; 6326 6327 function fireViewItem(variant) { 6328 if (!window.bpTrack || !window.bpTrack.view_item) { 6329 // bpTrack not loaded yet â retry shortly. Defer-loaded JS 6330 // tends to finish parsing on the next macrotask. 6331 setTimeout(function () { fireViewItem(variant); }, 100); 6332 return; 6333 } 6334 window.bpTrack.view_item(variant || window.__bpPdpVariant, window.__bpPdpProduct, 'Product Detail'); 6335 } 6336 6337 // Initial PDP view. 6338 if (document.readyState === 'loading') { 6339 document.addEventListener('DOMContentLoaded', function () { fireViewItem(); }, { once: true }); 6340 } else { 6341 fireViewItem(); 6342 } 6343 6344 // Re-fire on variant change. Horizon dispatches `variant:update` 6345 // on the document with detail.variant when the user picks a size 6346 // or color; size_change / color_change are fired off the same hook. 6347 document.addEventListener('variant:update', function (e) { 6348 var v = (e && e.detail && e.detail.variant) || null; 6349 if (!v) return; 6350 var nextVariant = { 6351 id: v.id, 6352 sku: v.sku || '', 6353 price: (v.price || 0) / 100, 6354 compare_at_price: v.compare_at_price ? v.compare_at_price / 100 : null, 6355 option1: v.option1 || '', 6356 option2: v.option2 || '', 6357 product_id: window.__bpPdpProduct.id, 6358 available: v.available, 6359 inventory_quantity: (v.inventory_quantity != null ? v.inventory_quantity : null) 6360 }; 6361 // Fire micro-events for option change. 6362 var colorChanged = nextVariant.option1 && nextVariant.option1 !== window.__bpPdpVariant.option1; 6363 if (window.bpTrack) { 6364 if (colorChanged) { 6365 window.bpTrack.color_change && window.bpTrack.color_change(nextVariant.option1, 'Product Detail'); 6366 } 6367 // NOTE: size_change is deliberately NOT fired from this hook. It 6368 // assumed Size == option2 (fragile â some products have Size as 6369 // option1), and in current Horizon the variant rides on 6370 // detail.resource, not detail.variant. Size is tracked by the 6371 // dedicated, option-NAME based `change` listener in the GLOBAL 6372 // snippets/bp-track-size.liquid (so it also covers the flyout). 6373 } 6374 window.__bpPdpVariant = nextVariant; 6375 // Spec: re-fire view_item ONLY on color-swatch change, not on size. 6376 if (colorChanged) fireViewItem(nextVariant); 6377 }); 6378 6379 6380 // BP colour swatches are CROSS-PRODUCT â each colourway is a separate 6381 // Shopify product, so picking a colour is an in-place product swap 6382 // handled by bp-pdp-swap.liquid, NOT a Horizon `variant:update` (that 6383 // hook only fires for size within one product). The swap dispatches 6384 // `bp:pdp:swapped` with the new handle; this listener is why the 6385 // colour-change view_item was never firing (QA â "not implemented"). 6386 // Refresh the PDP globals from the new product and re-fire view_item. 6387 // Spec: colour change re-fires view_item; size change does not. 6388 document.addEventListener('bp:pdp:swapped', function (e) { 6389 var handle = e && e.detail && e.detail.handle; 6390 if (!handle) return; 6391 fetch('/products/' + handle + '.js', { headers: { Accept: 'application/json' } }) 6392 .then(function (r) { return r.ok ? r.json() : null; }) 6393 .then(function (pj) { 6394 if (!pj) return; 6395 var variants = pj.variants || []; 6396 // Prefer the variant matching the size the shopper had selected 6397 // (the swap re-applies it); else the first available variant. 6398 var prevSize = window.__bpPdpVariant && window.__bpPdpVariant.option2; 6399 var v = null, i; 6400 if (prevSize) { 6401 for (i = 0; i < variants.length; i += 1) { 6402 if (variants[i].option2 === prevSize && variants[i].available) { v = variants[i]; break; } 6403 } 6404 } 6405 if (!v) { for (i = 0; i < variants.length; i += 1) { if (variants[i].available) { v = variants[i]; break; } } } 6406 if (!v) v = variants[0] || {}; 6407 // Colourways share a style â same bp_department; carry the 6408 // category metafield forward (not in /products/{handle}.js). 6409 var mf = (window.__bpPdpProduct && window.__bpPdpProduct.metafields) || 6410 { BP_ProductAttributes: { bp_department: '' } }; 6411 window.__bpPdpProduct = { 6412 id: pj.id, 6413 title: pj.title, 6414 vendor: pj.vendor || 'Boston Proper', 6415 handle: pj.handle, 6416 url: '/products/' + pj.handle, 6417 // Keep ImageURL populated on the colour-swap Viewed Product event. 6418 image: (v.featured_image && v.featured_image.src) || pj.featured_image || (pj.images && pj.images[0]) || '', 6419 product_type: pj.type || '', 6420 metafields: mf, 6421 variants: variants.map(function (x) { return x.id; }) 6422 }; 6423 window.__bpPdpVariant = { 6424 id: v.id, 6425 sku: v.sku || '', 6426 price: (v.price || 0) / 100, // .js prices are cents 6427 compare_at_price: v.compare_at_price ? v.compare_at_price / 100 : null, 6428 option1: v.option1 || '', 6429 option2: v.option2 || '', 6430 product_id: pj.id, 6431 available: v.available, 6432 // /products/{handle}.js omits inventory_quantity â item_stock_qty 6433 // may be blank on the colour-swap event (accepted limitation). 6434 inventory_quantity: (v.inventory_quantity != null ? v.inventory_quantity : null) 6435 }; 6436 fireViewItem(window.__bpPdpVariant); 6437 }) 6438 .catch(function () { /* best-effort â never break the colour swap */ }); 6439 }); 6440 })(); 6441 </script>
6441<script> 6442 (function () { 6443 var suppressUntil = 0; // colour-swap synthetic re-select window (ms epoch) 6444 6445 // Resolve the option's display name for a picker control. 6446 function sizeOptionName(el) { 6447 var fs = el.closest ? el.closest('fieldset') : null; 6448 if (fs) { 6449 var pfx = fs.querySelector('.variant-option__swatch-prefix') || fs.querySelector('legend'); 6450 if (pfx) return (pfx.textContent || '').trim(); 6451 } 6452 var nm = el.getAttribute ? el.getAttribute('name') : ''; 6453 var m = nm && nm.match(/options\[(.+?)\]/); 6454 if (m) return m[1]; 6455 if (el.id) { 6456 var lbl = document.querySelector('label[for="' + el.id + '"]'); 6457 if (lbl) return (lbl.textContent || '').trim(); 6458 } 6459 return ''; 6460 } 6461 6462 document.addEventListener('change', function (e) { 6463 var el = e.target; 6464 if (!el || !el.closest) return; 6465 var picker = el.closest('variant-picker'); 6466 if (!picker) return; // only variant pickers 6467 if (el.tagName !== 'SELECT' && el.type !== 'radio') return; 6468 if (el.type === 'radio' && !el.checked) return; 6469 if (!/size/i.test(sizeOptionName(el))) return; // SIZE option only 6470 var val = el.value; 6471 if (!val) return; 6472 // Colour-swap synthetic re-select â baseline it, don't report it. 6473 if (Date.now() < suppressUntil) { picker.__bpSize = val; return; } 6474 if (val === picker.__bpSize) return; // per-picker dedup / no-op 6475 picker.__bpSize = val; 6476 if (window.bpTrack && window.bpTrack.size_change) { 6477 window.bpTrack.size_change(val); // location auto (page type) 6478 } 6479 }, true); 6480 6481 // A colour swap re-selects the shopper's size on the swapped-in product via 6482 // SYNTHETIC change events â suppress briefly so it isn't a "size change". 6483 document.addEventListener('bp:pdp:swapped', function () { 6484 suppressUntil = Date.now() + 500; 6485 }); 6486 })(); 6487</script>
vendor: 1 bytes, line 6487
6487
6488<script> 6489 (function () { 6490 'use strict'; 6491 if (window.__bpTrackWishlistWired) return; 6492 window.__bpTrackWishlistWired = true; 6493 6494 function fire(eventName, line) { 6495 if (!window.bpTrack || !window.bpTrack[eventName] || !line) return; 6496 6497 // `line` is the `cartItemData` object dispatched by every save path 6498 // (bp-swym-helpers handleSaveForLater + the two PDP heart blocks). Its 6499 // field names are NOT the cart-line names the old code read 6500 // (product_id / sku / price / compare_at_price / options_with_values â 6501 // none of which exist here, which is why item_id / value / color / size / 6502 // stock / both Shopify ids all came back empty). The real shape is: 6503 // id â Shopify PARENT (product) id 6504 // variant_id â Shopify VARIANT id 6505 // variant_sku â full SKU (drives item_id inv7 / item_variant / item_parent_id inv5) 6506 // variant_price â selling price (cents; _itemFromProduct /100s it) 6507 // variant_compare_at_price â original / compare-at price 6508 // variant_title â "Color / Size" (option1 / option2) 6509 var price = parseFloat(line.variant_price); 6510 var compare = parseFloat(line.variant_compare_at_price); 6511 // variant_title is "<Color> / <Size>" (BP option1 / option2) â split so 6512 // item_category4 (Color) + item_category5 (Size) populate. 6513 var opts = String(line.variant_title || '').split(' / '); 6514 var color = (opts[0] || '').trim(); 6515 var size = (opts[1] || '').trim(); 6516 6517 var product = { 6518 id: line.id, // â item_shopify_id (parent product id) 6519 title: line.title, 6520 handle: line.handle, 6521 url: line.handle ? '/products/' + line.handle : '', 6522 metafields: { BP_ProductAttributes: { bp_department: '' } }, 6523 selected_variant: { 6524 id: line.variant_id, // â item_shopify_variant (variant id) 6525 sku: line.variant_sku || '', // â item_id (inv7) / item_variant / item_parent_id (inv5) 6526 price: isNaN(price) ? 0 : price, // â price + event_details.value (selling price) 6527 compare_at_price: isNaN(compare) ? null : compare, // â item_original_price when > selling price 6528 option1: color || null, // â item_category4 (Color) 6529 option2: size || null, // â item_category5 (Size) 6530 // A saved variant is a real, purchasable variant â "In Stock" (same 6531 // as cart-sourced events); _pushWithInventory fills item_stock_qty 6532 // from the live Storefront lookup. 6533 available: true, 6534 product_id: line.id 6535 } 6536 }; 6537 // eventLocation omitted on purpose â the helper resolves the Page Type 6538 // from the path (Product Detail / Site Browse / Homepage / Cart / â¦), 6539 // the same event_location scheme every other BP event uses. 6540 window.bpTrack[eventName](product); 6541 } 6542 6543 document.addEventListener('wishlist:item-saved', function (e) { 6544 var line = e && e.detail && e.detail.item; 6545 fire('add_to_wishlist', line); 6546 }); 6547 6548 document.addEventListener('wishlist:item-removed', function (e) { 6549 var line = e && e.detail && e.detail.item; 6550 fire('remove_from_wishlist', line); 6551 }); 6552 })(); 6553</script>
vendor: 1 bytes, line 6553
6553
6554<script> 6555 (function () { 6556 'use strict'; 6557 if (window.__bpTrackAccountWired) return; 6558 window.__bpTrackAccountWired = true; 6559 6560 function isLoginForm(form) { 6561 var action = form.getAttribute('action') || ''; 6562 var inner = form.querySelector('input[name="customer[email]"]'); 6563 var pwd = form.querySelector('input[name="customer[password]"]'); 6564 return /\/account\/login/.test(action) && inner && pwd; 6565 } 6566 6567 function isRegisterForm(form) { 6568 var action = form.getAttribute('action') || ''; 6569 var first = form.querySelector('input[name="customer[first_name]"]'); 6570 var email = form.querySelector('input[name="customer[email]"]'); 6571 var pwd = form.querySelector('input[name="customer[password]"]'); 6572 return /\/account/.test(action) && !!first && !!email && !!pwd; 6573 } 6574 6575 function isNewsletterForm(form) { 6576 var action = form.getAttribute('action') || ''; 6577 var typeAttr = form.getAttribute('id') || ''; 6578 var contact = form.querySelector('input[name="form_type"][value="customer"], input[name="contact[tags]"]'); 6579 return /\/contact/.test(action) && !!contact; 6580 } 6581 6582 document.addEventListener('submit', function (e) { 6583 var form = e.target; 6584 if (!(form instanceof HTMLFormElement)) return; 6585 6586 try { 6587 if (isLoginForm(form)) { 6588 // Atlas's flag pattern â bp-track-page.js reads + fires 6589 // bpTrack.login() on the next pageview. 6590 localStorage.setItem('login_GA4', 'true'); 6591 } 6592 if (isRegisterForm(form)) { 6593 // Fire sign_up immediately â Shopify-customer flow 6594 // typically posts and stays on the same response page 6595 // when registration succeeds (no GET redirect). 6596 if (window.bpTrack && window.bpTrack.sign_up) { 6597 window.bpTrack.sign_up('email'); 6598 } 6599 } 6600 if (isNewsletterForm(form)) { 6601 // Legacy Atlas-shape event (no GA4 standard counterpart) 6602 // for marketing's existing GTM tags. 6603 window.dataLayer = window.dataLayer || []; 6604 window.dataLayer.push({ event: 'Newsletter Subscription' }); 6605 // Also fire GA4 sign_up with method='newsletter' for the 6606 // standard ecommerce stream. 6607 if (window.bpTrack && window.bpTrack.sign_up) { 6608 window.bpTrack.sign_up('newsletter'); 6609 } 6610 } 6611 } catch (_) { /* analytics never blocks form submit */ } 6612 }, true); 6613 })(); 6614</script>
vendor: 1 bytes, line 6614
6614
6615<script> 6616 (function () { 6617 'use strict'; 6618 6619 const LOCAL_STORAGE_KEY = 'save-for-later-items'; 6620 const AMPLIENCE_BASE = 6621 (window.shop && window.shop.themeSettings && window.shop.themeSettings.amplience_cdn_base_url) || 6622 "https:\/\/media.bostonproper.com"; 6623 6624 // ---------- Customer / wishlist id ---------- 6625 function getCustomerId() { 6626 try { 6627 return (window.ShopifyAnalytics && 6628 window.ShopifyAnalytics.meta && 6629 window.ShopifyAnalytics.meta.page && 6630 window.ShopifyAnalytics.meta.page.customerId) || 'guest'; 6631 } catch (_) { 6632 return 'guest'; 6633 } 6634 } 6635 6636 // ---------- Storage layer: in-memory cache + localStorage mirror ---------- 6637 // 6638 // The wishlist needs to keep working even when localStorage is full 6639 // (third-party apps on this domain â Klaviyo, Yotpo cache, 6640 // BlackCrow, GTM dataLayer â routinely fill the 5MB per-origin 6641 // budget). The in-memory cache becomes the source of truth for the 6642 // session; localStorage is a write-through mirror so the state 6643 // survives a reload when it can fit. 6644 // 6645 // The Swym server-side wishlist is the cross-session source of 6646 // truth â checkWishlistSync() pulls from there on `wishlistloaded` 6647 // and seeds both layers, so an in-memory-only session is recovered 6648 // on the next visit. 6649 6650 let memoryState = null; // { [customerId]: { items, count } } 6651 let lsAvailable = true; // false after a quota failure this session 6652 const MAX_ITEMS_PER_CUSTOMER = 50; 6653 6654 function readLocalStorageRaw() { 6655 try { 6656 const raw = window.localStorage.getItem(LOCAL_STORAGE_KEY); 6657 return raw ? JSON.parse(raw) : null; 6658 } catch (_) { return null; } 6659 } 6660 6661 function isQuotaError(err) { 6662 if (!err) return false; 6663 return err.name === 'QuotaExceededError' || 6664 err.code === 22 || 6665 err.code === 1014 || 6666 /quota/i.test(String(err.message || err)); 6667 } 6668 6669 function tryWrite(data) { 6670 try { 6671 window.localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(data)); 6672 return null; 6673 } catch (e) { return e; } 6674 } 6675 6676 // Diagnostic â when localStorage is full, name the offender so we 6677 // can target a fix in a follow-up (clear the right vendor cache, 6678 // ask the app to roll over, etc.). Logged once per session. 6679 let quotaDiagLogged = false; 6680 function logQuotaDiagnostics() { 6681 if (quotaDiagLogged) return; 6682 quotaDiagLogged = true; 6683 try { 6684 const sizes = []; 6685 for (let i = 0; i < window.localStorage.length; i++) { 6686 const k = window.localStorage.key(i); 6687 const v = window.localStorage.getItem(k) || ''; 6688 sizes.push({ key: k, kb: Math.round((k.length + v.length) / 1024) }); 6689 } 6690 sizes.sort((a, b) => b.kb - a.kb); 6691 const top = sizes.slice(0, 5).map((s) => `${s.key} (${s.kb}KB)`).join(', '); 6692 console.warn('[bp-swym] localStorage full. Top 5 keys: ' + top); 6693 } catch (_) {} 6694 } 6695 6696 function getSavedItemsLocalStorage() { 6697 // Memory wins when present (set by any successful seed / write). 6698 if (memoryState) return JSON.parse(JSON.stringify(memoryState)); 6699 const fromLs = readLocalStorageRaw(); 6700 if (fromLs) memoryState = JSON.parse(JSON.stringify(fromLs)); 6701 return fromLs; 6702 } 6703 6704 // Cascade of size-reduction strategies. Each step mutates `data` 6705 // in-place and returns the most recent write error (or null on success). 6706 // 1. Try as-is. 6707 // 2. Drop `image` from every saved item (largest field â a CDN 6708 // URL adds 100-300 bytes per row; the saved drawer can 6709 // re-derive the image from the product handle on demand). 6710 // 3. Cap each customer's items at MAX_ITEMS_PER_CUSTOMER, oldest 6711 // first. 6712 // 4. Drop the orphan "guest" bucket when a logged-in customerId 6713 // bucket also exists (after-login carryover). 6714 function storeSavedItemsLocalStorage(data) { 6715 if (!data) return; 6716 6717 // Memory state ALWAYS updates â that's the in-session source of 6718 // truth so the drawer, bubble, and PDP heart always reflect the 6719 // latest save, even when localStorage rejects the write. 6720 memoryState = JSON.parse(JSON.stringify(data)); 6721 6722 let err = null; 6723 if (lsAvailable) {
6724 err = tryWrite(data); 6725 6726 if (err && isQuotaError(err)) { 6727 // Strategy 2 â strip images. 6728 Object.keys(data).forEach((cid) => { 6729 const items = (data[cid] && data[cid].items) || []; 6730 items.forEach((it) => { delete it.image; }); 6731 }); 6732 err = tryWrite(data); 6733 } 6734 6735 if (err && isQuotaError(err)) { 6736 // Strategy 3 â cap item count. 6737 Object.keys(data).forEach((cid) => { 6738 const bucket = data[cid] || {}; 6739 const items = bucket.items || []; 6740 if (items.length > MAX_ITEMS_PER_CUSTOMER) { 6741 bucket.items = items.slice(items.length - MAX_ITEMS_PER_CUSTOMER); 6742 bucket.count = bucket.items.length; 6743 data[cid] = bucket; 6744 } 6745 }); 6746 err = tryWrite(data); 6747 } 6748 6749 if (err && isQuotaError(err)) { 6750 // Strategy 4 â drop the orphan "guest" bucket if a real 6751 // customerId bucket exists. 6752 const keys = Object.keys(data); 6753 if (keys.length > 1 && data.guest) { 6754 delete data.guest; 6755 err = tryWrite(data); 6756 } 6757 } 6758 } 6759 6760 if (err) { 6761 if (isQuotaError(err)) { 6762 lsAvailable = false; 6763 logQuotaDiagnostics(); 6764 } else { 6765 console.warn('[bp-swym] storage write failed', err); 6766 } 6767 // Fall through â fire the change events anyway so UI updates 6768 // from the memory state. 6769 } 6770 6771 // ALWAYS dispatch â memory has the new state. Without this, 6772 // saving an item when localStorage is full leaves the drawer + 6773 // bubble stale (the user thinks the click didn't register). 6774 document.dispatchEvent(new CustomEvent('wishlist:updated', { detail: { wishlist: memoryState } })); 6775 document.dispatchEvent(new CustomEvent('bp:saved:updated')); 6776 } 6777 function getLocalWishlist() { 6778 const all = getSavedItemsLocalStorage(); 6779 const customerId = getCustomerId(); 6780 return (all && all[customerId]) || { items: [], count: 0 }; 6781 } 6782 6783 // ---------- Cart helpers (Atlas's updateCart + updateCartElements) ---------- 6784 async function updateCart(payload, action) { 6785 const map = { UPDATE: 'cart/update.js', CHANGE: 'cart/change.js', ADD: 'cart/add.js', REMOVE: 'cart/remove.js' }; 6786 const endpoint = map[action]; 6787 if (!endpoint) throw new Error('updateCart: unknown action ' + action); 6788 const root = (window.Shopify && window.Shopify.routes && window.Shopify.routes.root) || '/'; 6789 const res = await fetch(root + endpoint, { 6790 method: 'POST', 6791 headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, 6792 body: JSON.stringify(payload), 6793 credentials: 'same-origin' 6794 }); 6795 if (!res.ok) { 6796 // Shopify returns a JSON body on 4xx with the actual reason 6797 // ("You can only add N of that item...", "The product is sold 6798 // out", etc.). Surface it â a bare "HTTP 422" tells the shopper 6799 // and the next dev nothing, and Move-to-Bag now shows this. 6800 let description = ''; 6801 try { 6802 const body = await res.json(); 6803 description = body.description || body.message || ''; 6804 } catch (_) { /* non-JSON error body */ } 6805 const err = new Error(action + ' HTTP ' + res.status + (description ? ': ' + description : '')); 6806 err.status = res.status; 6807 err.description = description; 6808 throw err; 6809 } 6810 return res.json(); 6811 } 6812 function updateCartElements(cart) { 6813 document.dispatchEvent(new CustomEvent('cart:change', { detail: { cart } })); 6814 document.dispatchEvent(new CustomEvent('cart:updated', { detail: { cart } })); // BP drawer listens here 6815 document.dispatchEvent(new CustomEvent('product:added', { detail: { cart } })); 6816 } 6817 6818 // ---------- Swym proxy fetch ---------- 6819 async function getWishlist() { 6820 if (typeof window.SWGetExpressWishlistId !== 'function') return null; 6821 const wishlistId = window.SWGetExpressWishlistId(); 6822 if (wishlistId === 0 || wishlistId == null) return null; 6823 const customerId = getCustomerId(); 6824 const path = customerId && customerId !== 'guest' 6825 ? `/a/wishlist?type=api&customerid=${encodeURIComponent(customerId)}&version=1` 6826 : `/a/wishlist?type=api&wishlistid=${encodeURIComponent(wishlistId)}&version=1`;
6827 try { 6828 const res = await fetch(path, { credentials: 'same-origin' }); 6829 if (!res.ok) return null; 6830 return await res.json(); 6831 } catch (e) { 6832 console.warn('[bp-swym] proxy fetch failed', e); 6833 return null; 6834 } 6835 } 6836 6837 // ---------- Save the cart line to wishlist ---------- 6838 // Mirror of Atlas's handleSaveForLater(e, product) â `product` is 6839 // the cart line item (BP cart drawer passes a structured object). 6840 async function handleSaveForLater(e, product) { 6841 if (e && typeof e.preventDefault === 'function') e.preventDefault(); 6842 if (!product) return; 6843 6844 const customerId = getCustomerId(); 6845 const isCollectiveProduct = 6846 Array.isArray(product.tags) && 6847 (product.tags.indexOf('Shopify Collective') >= 0 || product.tags.indexOf('Collective') >= 0); 6848 6849 // Snapshot matches Atlas's cartItemData shape verbatim. 6850 const sku = (product.sku || product.variant_sku || '').toString(); 6851 const skuPrefix = sku.slice(0, 7); 6852 const cartItemData = { 6853 id: (product.product_id != null ? product.product_id : product.id) + '', 6854 handle: product.handle || '', 6855 variant_price: (product.variant_price != null 6856 ? product.variant_price 6857 : (product.variant && product.variant.price)) + '', 6858 variant_compare_at_price: (product.variant_compare_at_price != null 6859 ? product.variant_compare_at_price 6860 : (product.variant && product.variant.compareAtPrice)) + '', 6861 variant_title: (product.variant_title 6862 || (product.variant && product.variant.title) 6863 || '') + '', 6864 variant_id: (product.variant_id != null 6865 ? product.variant_id 6866 : (product.variant && product.variant.id)) + '', 6867 title: product.title || '', 6868 image: 6869 isCollectiveProduct || !skuPrefix 6870 ? (product.image || '') 6871 : `${AMPLIENCE_BASE}/s/bostonproper/${skuPrefix}_SET/1?fmt=auto&w=484`, 6872 quantity: ((product.quantity != null ? product.quantity : 1) + ''), 6873 variant_sku: sku, 6874 // Client save timestamp â the saved drawer sorts newest-first by this, 6875 // so a fresh save stays on top regardless of the mirror/Swym array 6876 // order (QA 7/27, Dragana: item landed at the bottom in-session). 6877 savedAt: Date.now() 6878 }; 6879 6880 const lineKey = product.line_key || product.key || (e && e.target && (function () { 6881 const li = e.target.closest('[data-bp-cart-line], [data-line-key], .cart-item'); 6882 return li && (li.dataset.lineKey || li.getAttribute('data-line-key')); 6883 })()); 6884 6885 // 1) Add to Swym (silent if app not loaded). 6886 try { 6887 if (typeof window.AddToSmartWishlist === 'function') { 6888 window.AddToSmartWishlist(cartItemData.id, cartItemData.variant_id); 6889 } 6890 } catch (err) { console.warn('[bp-swym] AddToSmartWishlist failed', err); } 6891 6892 // 2) Remove from cart. 6893 let cart = null; 6894 if (lineKey) { 6895 try { 6896 cart = await updateCart({ id: lineKey, quantity: 0 }, 'CHANGE'); 6897 updateCartElements(cart); 6898 } catch (err) { console.warn('[bp-swym] cart change failed', err); } 6899 } 6900 6901 // 3) Update local mirror. 6902 const all = getSavedItemsLocalStorage() || {}; 6903 const current = all[customerId] || { items: [], count: 0 }; 6904 // Prepend (newest first) so the optimistic in-session order matches the 6905 // order after reload (the saved list renders newest-first). Appending put 6906 // a fresh save at the BOTTOM in-session but the TOP after refresh 6907 // (QA 7/22, Dragana). 6908 const nextItems = [cartItemData].concat( 6909 current.items.filter((it) => it.variant_id !== cartItemData.variant_id) 6910 ); 6911 all[customerId] = { ...current, items: nextItems, count: nextItems.length }; 6912 storeSavedItemsLocalStorage(all); 6913 // Record the authoritative newest-first order (Swym-proof). 6914 stampSavedOrder(cartItemData.variant_id); 6915 6916 document.dispatchEvent(new CustomEvent('wishlist:item-saved', { detail: { item: cartItemData } })); 6917 return cart; 6918 } 6919 6920 // ---------- Remove from Swym + local mirror ---------- 6921 function removeWishlistItem(productId, variantId) { 6922 const customerId = getCustomerId(); 6923 const all = getSavedItemsLocalStorage() || {}; 6924 const current = all[customerId] || { items: [], count: 0 }; 6925 const removed = current.items.find((it) => String(it.variant_id) === String(variantId)) || null; 6926 const nextItems = current.items.filter((it) => String(it.variant_id) !== String(variantId)); 6927 all[customerId] = { ...current, items: nextItems, count: nextItems.length }; 6928 storeSavedItemsLocalStorage(all); 6929 clearSavedOrder(variantId); 6930
6931 try { 6932 if (typeof window.RemoveFromSmartWishlist === 'function') { 6933 window.RemoveFromSmartWishlist(productId, variantId); 6934 } 6935 } catch (err) { console.warn('[bp-swym] RemoveFromSmartWishlist failed', err); } 6936 6937 // Dispatched so analytics (bp-track-wishlist) can fire GA4 6938 // remove_from_wishlist. Detail carries the removed cart-line- 6939 // shaped object so the listener has enough to build the 6940 // GA4 items[] entry. 6941 if (removed) { 6942 document.dispatchEvent(new CustomEvent('wishlist:item-removed', { detail: { item: removed } })); 6943 } 6944 } 6945 6946 // ---------- Build BP Line Item Properties for Move-to-Bag ---------- 6947 // Port of Atlas's updateLineItemProperties() â preserves the LIPs 6948 // BP uses on every cart line so they survive the save/restore. 6949 function updateLineItemProperties(variant, inventoryAvailable) { 6950 const props = {}; 6951 try { 6952 const attrs = (variant && variant.BP_ProductAttributes) || {}; 6953 // Atlas reads both spellings (updateLineItemProperties.js:41-45); 6954 // existing ERP values aren't being converted, so a product 6955 // written at either spelling has to keep working. 6956 const catalogId = attrs.BP_CurrentEventID || attrs.bp_currenteventid; 6957 if (catalogId) props._catalog_id = catalogId; 6958 const priceStatus = attrs.BP_PriceStatus || attrs.bp_pricestatus; 6959 if (priceStatus) props._price_status = priceStatus; 6960 6961 const hasFinalSale = attrs.has_final_sale === 'yes'; 6962 if (hasFinalSale) props._final_sale = 'yes'; 6963 6964 // Pre-order date. Match the value union Atlas accepts rather 6965 // than case-sensitive 'T' â Atlas's React paths lower-case 6966 // first (ProductMain.js:67), so variants stored as "t"/"true"/ 6967 // "yes"/"1" are live pre-orders today and must stay that way. 6968 const rawPreorder = variant && variant.bp_preorder 6969 ? (variant.bp_preorder.value !== undefined ? variant.bp_preorder.value : variant.bp_preorder) 6970 : ''; 6971 const isPreorder = rawPreorder === true || 6972 ['t', 'true', 'yes', '1'].includes(String(rawPreorder).toLowerCase().trim()); 6973 const rawShipDate = variant && variant.expectedShipDate_Inbound && variant.expectedShipDate_Inbound[0] 6974 ? variant.expectedShipDate_Inbound[0].value 6975 : (variant && variant.expectedShipDate && variant.expectedShipDate.value); 6976 if (isPreorder && rawShipDate) { 6977 // No +13 offset â Atlas shows the raw DC-arrival date with only 6978 // weekend/holiday skipping applied (dateHelpers.skipWeekendsHol); 6979 // the earlier +13 here made a restored-from-save item's LIP ~2 6980 // weeks later than the cart's live recompute. Prefer the shared 6981 // window.bpPreorder helper (the Atlas-verbatim port used by the 6982 // PDP + cart) so all surfaces agree; fall back to a local 6983 // weekend-skip if it hasn't loaded. 6984 try { 6985 let formatted = null; 6986 if (window.bpPreorder && window.bpPreorder.formatDate) { 6987 formatted = window.bpPreorder.formatDate(rawShipDate); 6988 } else { 6989 const d = new Date(rawShipDate); 6990 if (!isNaN(d.getTime()) && d >= new Date()) { 6991 const day = d.getDay(); 6992 if (day === 6) d.setDate(d.getDate() + 2); 6993 else if (day === 0) d.setDate(d.getDate() + 1); 6994 formatted = d.toLocaleDateString('en-US', { year: 'numeric', month: 'long', day: '2-digit' }); 6995 } 6996 } 6997 if (formatted) props._expected_ship_date = formatted; 6998 } catch (_) {} 6999 } 7000 } catch (e) { console.warn('[bp-swym] LIP build failed', e); } 7001 return props; 7002 } 7003 7004 // ---------- Move saved item back to the cart ---------- 7005 // Throws if the line could not be added. Callers MUST let that 7006 // propagate to the UI: the saved entry is the only record of the 7007 // item, so a swallowed failure loses it (see the ordering note 7008 // below). 7009 async function handleMoveToBag(id, variantId, quantity, isWishlistItemProductLevel, item, variant) { 7010 // Fetch master product to validate inventory + carry BP attrs. 7011 let inventoryAvailable = 0; 7012 let resolvedVariant = variant || {}; 7013 // The id we actually post to /cart/add.js. Starts as the saved 7014 // variant_id but is replaced by the resolved variant below â 7015 // a product-level save (isWishlistItemProductLevel: variant_id 7016 // === product_id, no variant ever chosen) stores a PRODUCT id 7017 // here, and /cart/add.js 422s on those every time. 7018 let addVariantId = variantId;
7019 try { 7020 if (item && item.handle) { 7021 const res = await fetch(`/products/${item.handle}.js`, { credentials: 'same-origin' }); 7022 if (res.ok) { 7023 const product = await res.json(); 7024 const variants = product.variants || []; 7025 // Exact variant first; otherwise fall back to a sellable 7026 // one (product-level saves, or a variant retired since it 7027 // was saved). Prefer `available` over variants[0] so we 7028 // don't resolve straight onto a sold-out variant. 7029 let v = variants.find((vv) => String(vv.id) === String(variantId)); 7030 if (!v) v = variants.find((vv) => vv.available) || variants[0]; 7031 if (v) { 7032 addVariantId = v.id; 7033 inventoryAvailable = (v.inventory_quantity != null) ? v.inventory_quantity : 0; 7034 resolvedVariant = { ...v, ...resolvedVariant }; 7035 // BP_ProductAttributes (Atlas pulls from getMasterProduct's colorProduct.BP_ProductAttributes) 7036 const tags = product.tags || []; 7037 const hasFinalSale = tags.some((t) => String(t).toLowerCase().trim() === 'final sale'); 7038 resolvedVariant.BP_ProductAttributes = Object.assign( 7039 {}, 7040 resolvedVariant.BP_ProductAttributes || {}, 7041 { has_final_sale: hasFinalSale ? 'yes' : 'no' } 7042 ); 7043 } 7044 } 7045 } 7046 } catch (err) { /* non-fatal â push to cart with the saved id */ } 7047 7048 let properties = updateLineItemProperties(resolvedVariant, inventoryAvailable) || {}; 7049 // The metafield-derived LIPs (_catalog_id, _price_status, _final_sale, 7050 // _compare_at_price, _price) can't be computed client-side â they aren't 7051 // on the /products/<handle>.js variant JSON. Pull them from the canonical 7052 // ?view=lips endpoint so Move-to-Bag carries the SAME LIPs as add-to-cart 7053 // (QA 7/16 â they were missing). Then the saved line's own stored 7054 // properties win, preserving whatever LIP state it had when saved. 7055 if (window.bpLips && item && item.handle) { 7056 try { 7057 const canonical = await window.bpLips.forVariant(item.handle, addVariantId); 7058 if (canonical) properties = Object.assign({}, canonical, properties); 7059 } catch (_) { /* keep the computed set */ } 7060 } 7061 if (item && item.properties && Object.keys(item.properties).length) { 7062 properties = Object.assign(properties, item.properties); 7063 } 7064 const itemToAdd = { 7065 id: addVariantId, 7066 quantity: quantity != null ? quantity : 1 7067 }; 7068 if (Object.keys(properties).length) itemToAdd.properties = properties; 7069 7070 // Add FIRST, and let a failure throw. 7071 // 7072 // This used to remove from the wishlist optimistically and then 7073 // swallow any add error in a console.warn. That combination is 7074 // unrecoverable data loss rather than a cosmetic bug: the saved 7075 // entry is the item's only record, so any 422 (sold out, retired 7076 // variant, product-level id) silently destroyed it â the item 7077 // vanished from Saved for Later and never reached the cart. There 7078 // is nothing to be optimistic about here; the wishlist row costs 7079 // nothing to keep for the extra round trip. 7080 const cart = await updateCart({ items: [itemToAdd] }, 'ADD'); 7081 updateCartElements(cart); 7082 7083 // Only now that the line is really in the cart is it safe to drop 7084 // the save. Remove by the SAVED variantId (what the local mirror 7085 // and Swym are keyed on), not the resolved addVariantId â those 7086 // differ for product-level saves. 7087 if (isWishlistItemProductLevel) removeWishlistItem(id, id); 7088 else removeWishlistItem(id, variantId); 7089 7090 document.dispatchEvent(new CustomEvent('wishlist:item-saved')); 7091 return cart; 7092 } 7093 7094 // ---------- Sync local mirror with Swym ---------- 7095 // Readiness signal for consumers (the saved drawer): flips true the first 7096 // time checkWishlistSync reconciles against a real Swym response. Lets the 7097 // drawer WAIT for Swym and show a loading state instead of rendering 7098 // localStorage first and flashing when Swym replaces it. If Swym never 7099 // responds the consumer falls back to localStorage on its own timeout. 7100 let swymSynced = false;
7101 function isSynced() { return swymSynced; } 7102 function markSynced() { 7103 if (swymSynced) return; 7104 swymSynced = true; 7105 document.dispatchEvent(new CustomEvent('bp:saved:synced')); 7106 } 7107 7108 async function checkWishlistSync() { 7109 const local = getLocalWishlist(); 7110 let remote = null; 7111 try { remote = await getWishlist(); } catch (_) { remote = null; } 7112 if (!remote) return; // Swym not ready yet â a later `wishlistloaded` retries. 7113 const customerId = getCustomerId(); 7114 const all = getSavedItemsLocalStorage() || {}; 7115 // If counts differ, remote is the source of truth â EXCEPT when the 7116 // remote reads empty while local has items. Swym's API returns count 0 7117 // before it finishes hydrating the customer's wishlist, and treating that 7118 // as truth overwrote the local mirror with nothing: the saved drawer 7119 // showed the items on open, then blanked to "empty" and stayed empty until 7120 // a reopen re-synced. Keep local in that case (a genuine cross-device 7121 // clear is far rarer than this hydration race, and local edits already 7122 // update the mirror directly). 7123 if ((remote.count || 0) === 0 && (local.count || 0) > 0) { 7124 all[customerId] = local; 7125 storeSavedItemsLocalStorage(all); 7126 markSynced(); 7127 return; 7128 } 7129 if ((remote.count || 0) !== (local.count || 0)) { 7130 // Carry the client save timestamps over from the local mirror so a 7131 // freshly-saved item keeps sorting to the top even after the server 7132 // (whose item order lags/varies) becomes source of truth â otherwise 7133 // the sort key is lost and the item drops down the list (QA 7/27). 7134 const localByVariant = {}; 7135 (local.items || []).forEach((it) => { if (it && it.savedAt) localByVariant[String(it.variant_id)] = it.savedAt; }); 7136 const remoteItems = (remote.items || []).map((it) => { 7137 const ts = localByVariant[String(it.variant_id)]; 7138 return (ts && !it.savedAt) ? Object.assign({}, it, { savedAt: ts }) : it; 7139 }); 7140 all[customerId] = Object.assign({}, remote, { items: remoteItems }); 7141 storeSavedItemsLocalStorage(all); 7142 } else { 7143 // Counts match â keep the merged shape so newly-saved local 7144 // items remain even if Swym has caught up. 7145 all[customerId] = local; 7146 storeSavedItemsLocalStorage(all); 7147 } 7148 markSynced(); 7149 } 7150 7151 // ---------- Swym ready bridge (Atlas: wishlist-load-event.js) ---------- 7152 const WishlistLoadedEvent = new Event('wishlistloaded'); 7153 window.SWCallbackWishlistButtonLoaded = function () { 7154 window.dispatchEvent(WishlistLoadedEvent); 7155 }; 7156 // When Swym is ready, sync. Also try once after DOM ready in case 7157 // Swym already loaded. 7158 window.addEventListener('wishlistloaded', checkWishlistSync); 7159 document.addEventListener('DOMContentLoaded', () => { 7160 // Defer a tick so other listeners attach first. 7161 setTimeout(checkWishlistSync, 50); 7162 }); 7163 7164 // ---------- Cross-context cache invalidation ---------- 7165 // getLocalWishlist prefers the in-memory `memoryState` over localStorage 7166 // for speed. When ANOTHER same-origin document writes the shared key â 7167 // e.g. a save made inside the Publitas digital-catalog quickview iframe â 7168 // THIS window's memoryState is stale, so the saved drawer + top-nav count 7169 // didn't update until a reload (QA 7/27, Dragana). Re-seed memoryState from 7170 // localStorage so consumers read the fresh state. 7171 function reloadFromStorage() { 7172 const fresh = readLocalStorageRaw(); 7173 if (fresh) memoryState = fresh; 7174 return fresh; 7175 } 7176 // A localStorage write in a different same-origin document (the quickview 7177 // iframe) fires `storage` HERE (never in the writing document). Re-seed and 7178 // notify the drawer / bubble to re-render live. 7179 window.addEventListener('storage', (e) => { 7180 if (!e || e.key !== LOCAL_STORAGE_KEY) return; 7181 reloadFromStorage(); 7182 document.dispatchEvent(new CustomEvent('bp:saved:updated')); 7183 document.dispatchEvent(new CustomEvent('wishlist:updated')); 7184 }); 7185
7186 // ---------- Save-order map (Swym-proof newest-first ordering) ---------- 7187 // Per-item `savedAt` isn't reliable for "newest on top": Swym's async sync 7188 // rewrites the item objects (dropping our timestamp) and reorders them, so 7189 // a fresh save kept landing at the bottom. Keep an authoritative 7190 // variant_id â timestamp map in a SEPARATE localStorage key Swym never 7191 // manages; the saved drawer sorts by it. Survives every Swym sync/reorder. 7192 const ORDER_KEY = 'bp.saved_order'; 7193 function getSavedOrder() { 7194 try { return JSON.parse(window.localStorage.getItem(ORDER_KEY) || '{}') || {}; } 7195 catch (_) { return {}; } 7196 } 7197 function stampSavedOrder(variantId) { 7198 if (variantId == null || variantId === '') return; 7199 try { 7200 const m = getSavedOrder(); 7201 m[String(variantId)] = Date.now(); 7202 window.localStorage.setItem(ORDER_KEY, JSON.stringify(m)); 7203 } catch (_) { /* storage full â ordering falls back to savedAt */ } 7204 } 7205 function clearSavedOrder(variantId) { 7206 try { 7207 const m = getSavedOrder(); 7208 if (delete m[String(variantId)]) window.localStorage.setItem(ORDER_KEY, JSON.stringify(m)); 7209 } catch (_) {} 7210 } 7211 7212 // ---------- Expose globals (Atlas parity) ---------- 7213 window.bpSavedForLater = { 7214 LOCAL_STORAGE_KEY: LOCAL_STORAGE_KEY, 7215 getSavedItemsLocalStorage: getSavedItemsLocalStorage, 7216 storeSavedItemsLocalStorage: storeSavedItemsLocalStorage, 7217 getLocalWishlist: getLocalWishlist, 7218 getWishlist: getWishlist, 7219 removeWishlistItem: removeWishlistItem, 7220 updateLineItemProperties: updateLineItemProperties, 7221 checkWishlistSync: checkWishlistSync, 7222 isSynced: isSynced, 7223 reloadFromStorage: reloadFromStorage, 7224 getCustomerId: getCustomerId, 7225 getSavedOrder: getSavedOrder, 7226 stampSavedOrder: stampSavedOrder, 7227 clearSavedOrder: clearSavedOrder 7228 }; 7229 window.handleSaveForLater = handleSaveForLater; 7230 window.handleMoveToBag = handleMoveToBag; 7231 })(); 7232</script>
vendor: 1 bytes, line 7232
7232
7233<script 7234 src="https://cdn-widgetsrepository.yotpo.com/v1/loader/l9OjGj5YqKVR9PFonEHJc4ms9HXq9RV8dOWnI8LL?languageCode=en" 7235 async 7236 ></script>
7236<script> 7237(function () { 7238 if (window.__bpYotpoStarsQvNav) return; 7239 window.__bpYotpoStarsQvNav = true; 7240 7241 var MODAL_SEL = 'dialog.quick-add-modal, .quick-add-modal, #quick-add-modal-content, .quick-add-modal__content'; 7242 var DEBUG = false; // flip via window.bpYotpoQvNavDebug = true 7243 function log() { 7244 if (!DEBUG && !window.bpYotpoQvNavDebug) return; 7245 var args = ['[bp-yotpo-qv-nav]'].concat(Array.prototype.slice.call(arguments)); 7246 console.log.apply(console, args); 7247 } 7248 7249 function navigateFromHost(host, e) { 7250 var url = host.getAttribute('data-bp-product-url'); 7251 if (!url) { 7252 log('no data-bp-product-url on host', host); 7253 return false; 7254 } 7255 if (e) { 7256 e.preventDefault(); 7257 e.stopPropagation(); 7258 if (e.stopImmediatePropagation) e.stopImmediatePropagation(); 7259 } 7260 log('navigating â', url + '#bp-pdp-reviews'); 7261 window.location.href = url + '#bp-pdp-reviews'; 7262 return true; 7263 } 7264 7265 // 1) Document-level delegated fallback (capture phase). Handles the 7266 // case where Yotpo's own handlers haven't yet stopped propagation. 7267 ['pointerdown', 'mousedown', 'click'].forEach(function (evt) { 7268 document.addEventListener(evt, function (e) { 7269 var host = e.target.closest && e.target.closest('.bp-yotpo-stars'); 7270 if (!host) return; 7271 if (!host.closest(MODAL_SEL)) return; 7272 if (evt === 'click') navigateFromHost(host, e); 7273 }, true); 7274 }); 7275 7276 // 2) Direct-attach listener on any `.bp-yotpo-stars` that shows up 7277 // inside the drawer â belt-and-suspenders in case Yotpo's Vue 7278 // stops propagation before it reaches document. A MutationObserver 7279 // watches the whole drawer subtree; when the widget hydrates it 7280 // also gets the same click handler. 7281 function wireDrawerStars(root) { 7282 root.querySelectorAll('.bp-yotpo-stars').forEach(function (host) { 7283 if (host.__bpYotpoQvWired) return; 7284 host.__bpYotpoQvWired = true; 7285 host.style.cursor = 'pointer'; 7286 host.addEventListener('click', function (e) { navigateFromHost(host, e); }, true); 7287 // Yotpo's own summary sometimes eats the click â also listen on 7288 // pointerdown/mousedown which fire earlier in the event sequence. 7289 host.addEventListener('mousedown', function () { log('mousedown on stars'); }, true); 7290 log('wired stars host', host); 7291 }); 7292 } 7293 7294 function observeAll() { 7295 document.querySelectorAll(MODAL_SEL).forEach(function (drawer) { 7296 if (drawer.__bpYotpoQvObserved) return; 7297 drawer.__bpYotpoQvObserved = true; 7298 wireDrawerStars(drawer); 7299 var mo = new MutationObserver(function () { wireDrawerStars(drawer); }); 7300 mo.observe(drawer, { childList: true, subtree: true }); 7301 log('observing drawer', drawer); 7302 }); 7303 } 7304 7305 if (document.readyState === 'loading') { 7306 document.addEventListener('DOMContentLoaded', observeAll); 7307 } else { 7308 observeAll(); 7309 } 7310 // Drawers might mount after initial load â watch the body for the 7311 // dialog itself appearing. 7312 new MutationObserver(observeAll).observe(document.documentElement, { childList: true, subtree: true }); 7313})(); 7314</script>
vendor: 1 bytes, line 7314
7314
7315<script> 7316 !function(){if(!window.klaviyo){window._klOnsite=window._klOnsite||[];try{window.klaviyo=new Proxy({},{get:function(n,i){return"push"===i?function(){var n;(n=window._klOnsite).push.apply(n,arguments)}:function(){for(var n=arguments.length,o=new Array(n),w=0;w<n;w++)o[w]=arguments[w];var t="function"==typeof o[o.length-1]?o.pop():void 0,e=new Promise((function(n){window._klOnsite.push([i].concat(o,[function(i){t&&t(i),n(i)}]))}));return e}}})}catch(n){window.klaviyo=window.klaviyo||[],window.klaviyo.push=function(){var n;(n=window._klOnsite).push.apply(n,arguments)}}}}(); 7317</script>
7317<script> 7318 (function () { 7319 'use strict'; 7320 var ACCOUNT = "\/account"; 7321 var HASH = '#k-hub'; 7322 7323 function hubHref(href) { 7324 href = String(href || ''); 7325 if (href.indexOf(HASH) !== -1) return href; 7326 return href.split('#')[0] + HASH; 7327 } 7328 7329 // Open the hub WITHOUT navigating. Klaviyo's onsite runtime opens 7330 // Customer Hub by intercepting clicks on `#k-hub` anchors (it calls 7331 // preventDefault, so the page never navigates). A programmatic 7332 // `location.assign('/account#k-hub')` is NOT a click, so Klaviyo 7333 // can't intercept it â the browser really does load /account (a 7334 // "not found" under the new customer-accounts setup) and only then 7335 // does the hub open on the hash. Instead, synthesize a real click 7336 // on a bare `#k-hub` anchor: Klaviyo's delegated handler catches it 7337 // and slides the hub in place. If the hub isn't Live, a bare 7338 // `#k-hub` just sets the current-page hash â no navigation, no 7339 // not-found page (unlike the old /account#k-hub assign). 7340 function openHub() { 7341 var a = document.createElement('a'); 7342 a.setAttribute('href', HASH); 7343 a.setAttribute('aria-hidden', 'true'); 7344 a.style.cssText = 'position:absolute;width:1px;height:1px;overflow:hidden;clip:rect(0 0 0 0);'; 7345 (document.body || document.documentElement).appendChild(a); 7346 a.click(); 7347 setTimeout(function () { if (a.parentNode) a.parentNode.removeChild(a); }, 0); 7348 } 7349 7350 // Rewrite standalone account anchors (mobile nav, footer, etc.) â 7351 // the same rewrite Klaviyo does automatically on standard themes. 7352 function rewriteAnchors() { 7353 var links = document.querySelectorAll( 7354 'a[href="' + ACCOUNT + '"], a[href^="' + ACCOUNT + '?"], a[href$="/account"], a[href*="/account?"]' 7355 ); 7356 for (var i = 0; i < links.length; i++) { 7357 var a = links[i]; 7358 if (a.__kHub || a.hasAttribute('data-k-hub-ignore')) continue; 7359 a.setAttribute('href', hubHref(a.getAttribute('href'))); 7360 a.__kHub = 1; 7361 } 7362 } 7363 7364 // The desktop header account control is a <shopify-account> web 7365 // component with no plain /account anchor for Klaviyo to rewrite. 7366 // Intercept clicks on it (capture phase, so we beat the component's 7367 // own navigation handler) and open the hub in place via openHub(). 7368 // When Customer Hub is Live, Klaviyo slides the hub in; otherwise 7369 // the bare `#k-hub` hash is a no-op â the icon simply does nothing 7370 // rather than navigating to a not-found account page. 7371 document.addEventListener('click', function (e) { 7372 var scope = e.target.closest && e.target.closest('shopify-account, .account-button'); 7373 if (!scope || scope.hasAttribute('data-k-hub-ignore')) return; 7374 // If the click is already on a #k-hub anchor, let it through. 7375 var a = e.target.closest('a[href]'); 7376 if (a && a.getAttribute('href').indexOf(HASH) !== -1) return; 7377 e.preventDefault(); 7378 e.stopPropagation(); 7379 openHub(); 7380 }, true); 7381 7382 // Rewrite now and again as Horizon hydrates / injects account links 7383 // (short-lived â the anchors settle early). 7384 rewriteAnchors(); 7385 var runs = 0; 7386 var timer = setInterval(function () { 7387 rewriteAnchors(); 7388 if (++runs >= 6) clearInterval(timer); // ~3s of catch-up 7389 }, 500); 7390 window.addEventListener('load', rewriteAnchors); 7391 })(); 7392 </script>
7392<script> 7393 /* BP Stylitics outfit provider â sources outfits from the Composer SDK's 7394 single /api/orchestrator call instead of a second /api/outfits request. 7395 Loaded on product templates only (see theme.liquid), so the fetch wrap 7396 below is PDP-scoped. Public API: 7397 window.bpStyliticsOutfits(style) -> Promise<Array> (always resolves) 7398 window.bpRegisterStyliticsTrigger(style, fn) (How-To-Wear-It) 7399 window.bpPublishStyliticsOutfits(style, outfits) (manual publish) */ 7400 (function () { 7401 if (window.bpStyliticsOutfits) return; 7402 var store = {}; // item_number -> outfits array (from the orchestrator response) 7403 var waiters = {}; // item_number -> [resolve, ...] 7404 var net = {}; // item_number -> fallback /api/outfits promise 7405 var triggers = {}; // item_number -> fn that forces the Composer to init 7406 7407 function norm(s) { return String(s == null ? '' : s).trim(); } 7408 7409 // Publish the outfit array captured from the Composer's orchestrator call. 7410 function publish(style, outfits) { 7411 style = norm(style); 7412 if (!style || !Array.isArray(outfits)) return; 7413 store[style] = outfits; 7414 var w = waiters[style] || []; 7415 waiters[style] = []; 7416 w.forEach(function (fn) { try { fn(outfits); } catch (e) {} }); 7417 } 7418 window.bpPublishStyliticsOutfits = publish; 7419 7420 // Let How-To-Wear-It register a way to force its (lazy) Composer to init, 7421 // so a consumer that needs outfits before the section scrolls into view 7422 // (Complete-the-look) still resolves from the single orchestrator call 7423 // instead of firing its own fetch. 7424 window.bpRegisterStyliticsTrigger = function (style, fn) { 7425 style = norm(style); 7426 if (style && typeof fn === 'function') triggers[style] = fn; 7427 }; 7428 7429 // Capture the Composer SDK's orchestrator response (its single outfits call) 7430 // and publish outfits per item_number. Wrapping fetch is the most robust 7431 // hook â it reads the actual API response (stable modules[].data.outfits 7432 // shape), not SDK internals; it only acts on orchestrator URLs and always 7433 // returns the original promise, so it composes with other fetch wrappers. 7434 if (!window.__bpStyliticsFetchHooked && window.fetch) { 7435 window.__bpStyliticsFetchHooked = true; 7436 var origFetch = window.fetch; 7437 window.fetch = function (input) { 7438 var url = ''; 7439 try { url = typeof input === 'string' ? input : (input && input.url) || ''; } catch (e) {} 7440 var p = origFetch.apply(this, arguments); 7441 if (url && url.indexOf('widget-api.stylitics.com/api/orchestrator') !== -1) { 7442 p.then(function (res) { return res && res.clone ? res.clone().json() : null; }) 7443 .then(function (data) { 7444 try { 7445 var u = new URL(url); 7446 var cfg = JSON.parse(u.searchParams.get('config') || '{}'); 7447 var mods = (data && data.modules) || []; 7448 (cfg.modules || []).forEach(function (m, i) { 7449 var style = norm(m && (m.item_number || (m.params && m.params.item_number))); 7450 var outfits = mods[i] && mods[i].data && mods[i].data.outfits; 7451 if (style && Array.isArray(outfits)) publish(style, outfits); 7452 }); 7453 } catch (e) {} 7454 }) 7455 .catch(function () {}); 7456 } 7457 return p; 7458 }; 7459 } 7460 7461 // Fallback: a direct /api/outfits fetch, used ONLY when the Composer never 7462 // produces data for this style (section absent / SDK failed). In that case 7463 // no orchestrator call was made either, so it is still a single request. 7464 function fallback(style) { 7465 if (net[style]) return net[style]; 7466 var url = 'https://widget-api.stylitics.com/api/outfits?username=bostonproper&item_number=' + 7467 encodeURIComponent(style); 7468 net[style] = fetch(url, { cache: 'force-cache' }) 7469 .then(function (r) { return r.ok ? r.json() : []; }) 7470 .then(function (d) { return Array.isArray(d) ? d : []; }) 7471 .catch(function () { delete net[style]; return []; }); 7472 return net[style]; 7473 } 7474
7475 // Resolve outfits for a style. Prefers the Composer's captured orchestrator 7476 // data (no extra request); forces the Composer to init when a trigger is 7477 // registered; falls back to a single /api/outfits fetch only if the Composer 7478 // never yields data within the wait window. 7479 window.bpStyliticsOutfits = function (style, opts) { 7480 style = norm(style); 7481 if (!style) return Promise.resolve([]); 7482 if (store[style]) return Promise.resolve(store[style]); 7483 var waitMs = (opts && opts.waitMs) || 6000; 7484 if (triggers[style]) { try { triggers[style](); } catch (e) {} } 7485 return new Promise(function (resolve) { 7486 var settled = false; 7487 var done = function (v) { if (!settled) { settled = true; resolve(v); } }; 7488 (waiters[style] = waiters[style] || []).push(done); 7489 setTimeout(function () { if (!settled) fallback(style).then(done); }, waitMs); 7490 }); 7491 }; 7492 })(); 7493</script>
7493 7494 7495<link 7496 rel="stylesheet" 7497 href="https://web-assets.stylitics.com/v3-classic/latest/classic.css" 7498 media="print" 7499 onload="this.media='all'" 7500>
7501<script 7502 src="//web-assets.stylitics.com/v3-classic/latest/classic.release.js" 7503 async 7504></script>
vendor: 1 bytes, line 7504
7504
7505<script 7506 src="https://web-assets.stylitics.com/loader/latest/composer.js" 7507 async 7508></script>
7508 7509 7510
7510<script> 7511 (function (r, e, o) { 7512 var w = window, 7513 d = document, 7514 t, 7515 s, 7516 x; 7517 w.tfcapi = t = 7518 w.tfcapi || 7519 function () { 7520 t.q = t.q || []; 7521 t.q.push(arguments); 7522 }; 7523 t('init', { storeKey: r, environment: e, ...o }); 7524 x = d.getElementsByTagName('script')[0]; 7525 s = d.createElement('script'); 7526 s.type = 'text/javascript'; 7527 s.async = true; 7528 s.crossorigin = 'anonymous'; 7529 s.src = 'https://cdn.truefitcorp.com/fitrec/global/js/tf-integration.js?storeKey=' + r; 7530 x.parentNode.insertBefore(s, x); 7531 // Don't change anything above this line 7532 })("bog", "prod"); 7533 7534 if (window.Shopify && Shopify.designMode) { 7535 document.addEventListener('DOMContentLoaded', () => { 7536 new MutationObserver(() => tfcapi('calculate')).observe(document.body, { 7537 childList: true, 7538 subtree: true, 7539 }); 7540 }); 7541 } 7542 7543 // Stamp `attributes[_tfcToken]` on every cart-add for True Fit 7544 // purchase attribution. Verbatim port of Atlas's 7545 // observeCartChanges() block (layout/theme.liquid:185-224). 7546 // Without this, True Fit can't match purchases back to size 7547 // recommendations â their merchant dashboard shows incomplete 7548 // accuracy + ROI data. 7549 function observeCartChanges() { 7550 const cartObserver = new PerformanceObserver((list) => { 7551 list.getEntries().forEach((entry) => { 7552 const isValidRequestType = ['xmlhttprequest', 'fetch'].includes(entry.initiatorType); 7553 const isCartChangeRequest = /\/cart\/add/.test(entry.name); 7554 if (isValidRequestType && isCartChangeRequest) { 7555 tfcapi('getToken', async (token) => { 7556 if (!token) { 7557 // Atlas also fires an "emptyTfcToken" diagnostic ping 7558 // here to setup.truefit.com. Skipped in the BP port â 7559 // the metric is internal to True Fit and adds a 7560 // network call on every cart-add. Re-add if True Fit 7561 // support flags missing data. 7562 return; 7563 } 7564 const formData = new FormData(); 7565 formData.append('attributes[_tfcToken]', token); 7566 await fetch(window.Shopify.routes.root + 'cart/update.js', { 7567 method: 'POST', 7568 keepalive: true, 7569 body: formData, 7570 }); 7571 }); 7572 } 7573 }); 7574 }); 7575 cartObserver.observe({ entryTypes: ['resource'] }); 7576 } 7577 observeCartChanges(); 7578 </script>
7578<style> 7579 /* ---- Wrapper polish + typography baseline ------------------------ */ 7580 body[data-template="product"] { 7581 --bp-pdp-text: var(--bp-color-text, #171609); 7582 --bp-pdp-body: var(--bp-color-body-copy, #74736B); 7583 --bp-pdp-border: rgba(23, 22, 9, 0.18); 7584 --bp-pdp-border-soft: rgba(23, 22, 9, 0.10); 7585 } 7586 7587 /* ---- Force light background on the PDP wrapper ------------------- 7588 Horizon's default color scheme (scheme-1) is dark on this store â 7589 and inherit_color_scheme would propagate it through every nested 7590 block. We force a light bg + dark text on the entire PDP section 7591 so the variant buttons, prices, accordions, etc. read correctly. 7592 The product.json color_scheme is also cleared. */ 7593 body[data-template="product"], 7594 body[data-template="product"] .product-information, 7595 body[data-template="product"] .product-information .product-information, 7596 body[data-template="product"] .product-information .product-information__main, 7597 body[data-template="product"] .product-information .product-information__details { 7598 background: var(--bp-color-bg-cream, #F7F6EA); 7599 color: var(--bp-pdp-text); 7600 --color-background: var(--bp-color-bg-cream, #F7F6EA); 7601 --color-foreground: var(--bp-pdp-text); 7602 } 7603 body[data-template="product"] main#MainContent { 7604 background: var(--bp-color-bg-cream, #F7F6EA); 7605 } 7606 7607 /* ---- Product title â Heading/H4 ----------------------------------- 7608 #171609 · Signifier · 24 / 300 / 120% / -0.48. 7609 7610 Horizon's actual rule that styles the H1 reads from CSS variables: 7611 7612 .text-block.h3 :is(h1, h2, h3, h4, h5, h6) { 7613 font-family: var(--font-h3--family); 7614 font-size: var(--font-h3--size); 7615 ⦠7616 } 7617 7618 LAYER 1 â Redefine the variables Horizon's rule reads from. This 7619 makes the existing Horizon rule resolve to BP values without 7620 having to win the specificity fight at the rule level. */ 7621 html body[data-template="product"], 7622 html body[data-template="product"] .product-information, 7623 html body[data-template="product"] .product-information__details, 7624 html body[data-template="product"] .text-block, 7625 html body[data-template="product"] .text-block.h3, 7626 html body[data-template="product"] [class*="text-block"] { 7627 --font-h3--family: 'Signifier', 'Times New Roman', serif !important; 7628 --font-h3--size: 24px !important; 7629 --font-h3--weight: 300 !important; 7630 --font-h3--style: normal !important; 7631 --font-h3--line-height: 120% !important; 7632 --font-h3--letter-spacing: -0.48px !important; 7633 --font-h3--case: none !important; 7634 --font-h3-color: var(--bp-color-text, #171609) !important; 7635 } 7636 7637 /* LAYER 2 â Direct property override. Selectors target the actual 7638 Horizon class structure (.product-information, not the 7639 .product-information I had wrong earlier) AND mirror 7640 Horizon's `.text-block.h3 :is(h1,...)` selector with an extra 7641 `.product-information` ancestor to outrank it. */ 7642 html body .product-information h1, 7643 html body .product-information__details h1, 7644 html body .product-information .text-block h1, 7645 html body .product-information .text-block.h3 :is(h1, h2, h3, h4, h5, h6), 7646 html body .product-information [class*="text-block"] h1, 7647 html body[data-template="product"] h1.h1, 7648 html body[data-template="product"] h1.h2, 7649 html body[data-template="product"] h1.h3, 7650 html body[data-template="product"] h1.h4 { 7651 margin: 0 !important; 7652 color: var(--bp-color-text, #171609) !important; 7653 font-family: 'Signifier', 'Times New Roman', serif !important; 7654 font-size: 24px !important; 7655 font-style: normal !important; 7656 font-weight: 300 !important; 7657 line-height: 120% !important; 7658 letter-spacing: -0.48px !important; 7659 text-transform: none !important; 7660 } 7661 /* ---- COLLECTION / BRAND eyebrow â Body/Regular/Eyebrow ---------- 7662 #74736B · Messina · 14 / 500 / 110% / -0.14 · UPPERCASE. 7663 Targets Horizon's product-vendor surfaces. Breadcrumbs are 7664 handled separately by blocks/bp-pdp-media-gallery.liquid's own 7665 stylesheet (Body/Tiny spec, smaller and not uppercase) â they 7666 used to be in this selector list and were getting the wrong 7667 typography when moved to the gallery overlay. */ 7668 .product-information .product-vendor, 7669 .product-information [data-product-vendor], 7670 .product-information [data-bp-eyebrow] { 7671 color: var(--bp-color-body-copy, #74736B) !important; 7672 font-family: var(--bp-font-body), 'Messina Sans VF', system-ui, sans-serif !important; 7673 font-size: 14px !important; 7674 font-style: normal !important; 7675 font-weight: 500 !important; 7676 line-height: 110% !important; 7677 letter-spacing: -0.14px !important; 7678 text-transform: uppercase !important; 7679 text-decoration: none !important; 7680 } 7681 7682 /* ---- Yotpo bottomline "X Reviews" â Body/Tiny ------------------- 7683 #74736B · Messina · 10 / 400 / 145% / -0.1. 7684 7685 Yotpo's widget injects `font-family: Futura-PT-Regular` (+ a 7686 bunch of other inline style) at runtime, so this override pairs 7687 `html body` to outrank the inline declarations and uses `!important` 7688 on every property. Targets both the bp-yotpo-stars wrapper 7689 (top-of-PDP rating row) AND raw Yotpo containers anywhere on 7690 the page (reviews list, badge, etc.) â Yotpo renders this label 7691 in multiple places. */ 7692 /* Yotpo ships an upstream rule with FIVE chained classes â specificity 7693 (0,5,0) â for `.yotpo-sr-bottom-line-text` inside the bottom-line 7694 widget that pins 13px / Futura PT. To beat it we mirror their 7695 5-class chain AND add `html body` to push specificity to (0,5,2), 7696 plus !important on every prop. Three selectors below cover the 7697 widget's three rendering paths (right-panel, summary-only, and 7698 bare bottom-line). The short selector list at the bottom keeps 7699 the broader-but-lower-specificity safety net for any other Yotpo
7700 surface (reviews list, badge, etc.) where the upstream override 7701 isn't applied. */ 7702 html body .yotpo-reviews-star-ratings-widget .yotpo-bottom-line-scroll-panel .yotpo-sr-bottom-line-summary .yotpo-sr-bottom-line-right-panel .yotpo-sr-bottom-line-text, 7703 html body .yotpo-reviews-star-ratings-widget .yotpo-bottom-line-scroll-panel .yotpo-sr-bottom-line-summary .yotpo-sr-bottom-line-text, 7704 html body .yotpo-reviews-star-ratings-widget .yotpo-bottom-line-scroll-panel .yotpo-sr-bottom-line-text, 7705 html body .yotpo-sr-bottom-line-text, 7706 html body .yotpo-sr-bottom-line-text--right-panel, 7707 html body .yotpo-sr-bottom-line-summary [class*="text"], 7708 html body [class*="reviews-count"], 7709 html body [class*="bottom-line-text"] { 7710 color: var(--bp-color-body-copy, #74736B) !important; 7711 font-family: var(--bp-font-body), 'Messina Sans VF', system-ui, sans-serif !important; 7712 font-size: 10px !important; 7713 font-style: normal !important; 7714 font-weight: 400 !important; 7715 line-height: 145% !important; 7716 letter-spacing: -0.1px !important; 7717 text-transform: none !important; 7718 } 7719 7720 /* ---- Price ------------------------------------------------------ 7721 Markup (snippets/price.liquid): a `.price__regular` block holds the 7722 default price; a `.price__sale` block holds `.price-item--sale` (the 7723 NEW price) + `.price-item--regular.compare-at-price` (the struck OLD 7724 price). `.price__hidden` toggles which block shows. Note the `price` 7725 class is reused on the container AND the leaf spans, so we key colors 7726 off the specific leaf classes, not bare `.price`. */ 7727 7728 /* Default (non-sale) price â Body/Regular/Medium: #171609 · 17/500/-0.17. */ 7729 .product-information .price, 7730 .product-information [data-block-type="price"], 7731 .product-information .price__regular, 7732 .product-information .price__regular .price { 7733 color: var(--bp-color-text, #171609) !important; 7734 font-family: var(--bp-font-body) !important; 7735 font-size: 17px !important; 7736 font-style: normal !important; 7737 font-weight: 500 !important; 7738 line-height: 145% !important; 7739 letter-spacing: -0.17px !important; 7740 } 7741 /* NEW / sale price â Sales-Red · Body/Regular/Small (14 / 500 / -0.14). */ 7742 .product-information .price__sale .price-item--sale, 7743 .product-information .price__sale .price-item--sale.price { 7744 color: var(--bp-color-sales-red, #CA3D3D) !important; 7745 font-family: var(--bp-font-body) !important; 7746 font-size: 14px !important; 7747 font-style: normal !important; 7748 font-weight: 500 !important; 7749 line-height: 145% !important; 7750 letter-spacing: -0.14px !important; 7751 } 7752 /* OLD / compare price â Body-Copy grey (#74736B) · Body/Body Small 7753 (14 / 500 / -0.14) · strikethrough. Per Figma the struck price is 7754 grey, NOT Sales-Red. */ 7755 .product-information .compare-at-price, 7756 .product-information .price__sale .price-item--regular { 7757 color: #74736B !important; 7758 font-family: var(--bp-font-body) !important; 7759 font-size: 14px !important; 7760 font-style: normal !important; 7761 font-weight: 500 !important; 7762 line-height: 145% !important; 7763 letter-spacing: -0.14px !important; 7764 text-decoration: line-through; 7765 margin-left: 6px; 7766 } 7767 /* Right-of-price promo text â Sales-Red · Body/Book/Small 7768 (14 / 400 / -0.14). Lighter (400) than the sale price (500). */
7769 .product-information .bp-promo-line { 7770 color: var(--bp-color-sales-red, #CA3D3D) !important; 7771 font-family: var(--bp-font-body) !important; 7772 font-size: 14px !important; 7773 font-style: normal !important; 7774 font-weight: 400 !important; 7775 line-height: 145% !important; 7776 letter-spacing: -0.14px !important; 7777 } 7778 7779 /* ---- Tag pills below pricing â Heading/H6 (Signifier) ----------- 7780 #171609 · Signifier · 14 / 300 / 120% / -0.28. The pills already 7781 get their cream background from bp-tag-pills.liquid; here we 7782 just lock typography on the label text inside each pill. */ 7783 .product-information .bp-pills__item { 7784 color: var(--bp-color-text, #171609) !important; 7785 font-family: var(--bp-font-display) !important; 7786 font-size: 14px !important; 7787 font-style: normal !important; 7788 font-weight: 300 !important; 7789 line-height: 120% !important; 7790 letter-spacing: -0.28px !important; 7791 text-transform: none !important; 7792 } 7793 7794 /* ---- Variant picker label "Color" / "Size" â Body/XSmall Book ---- 7795 #171609 · Messina · 12 / 400 / 145% / -0.12. The selected-value 7796 ("Olive Night" / "XXS") shares the same spec but in body-copy 7797 grey. Targets Horizon's variant-picker legend/label + the 7798 bp-color-siblings inline label. 7799 7800 `.variant-option__swatch-prefix` is the wrapper around the option 7801 name in the legend ("Size:"); see the BP edit in 7802 snippets/variant-main-picker.liquid that makes Size match the 7803 bp-colors__label structure. */ 7804 .product-information variant-picker .option__name, 7805 .product-information variant-picker .option-label, 7806 .product-information variant-picker label[for], 7807 .product-information variant-picker legend, 7808 .product-information variant-picker .variant-option__swatch-prefix, 7809 .product-information variant-picker .product-form__option-name, 7810 .product-information .bp-colors__label, 7811 .product-information .bp-colors__label-prefix { 7812 color: var(--bp-color-text, #171609) !important; 7813 font-family: var(--bp-font-body) !important; 7814 font-size: 12px !important; 7815 font-style: normal !important; 7816 font-weight: 600 !important; 7817 line-height: 145% !important; 7818 letter-spacing: -0.12px !important; 7819 text-transform: none !important; 7820 margin-bottom: 0 !important; 7821 } 7822 /* Selected-value text (Olive Night / XXS) â same size, body-copy grey */ 7823 .product-information variant-picker .selected-value, 7824 .product-information variant-picker [class*="selected-value"], 7825 .product-information variant-picker .variant-option__swatch-value, 7826 .product-information variant-picker .product-form__selected-value, 7827 .product-information .bp-colors__label-name { 7828 color: var(--bp-color-body-copy, #74736B) !important; 7829 font-family: var(--bp-font-body) !important; 7830 font-size: 12px !important; 7831 font-style: normal !important; 7832 font-weight: 400 !important; 7833 line-height: 145% !important; 7834 letter-spacing: -0.12px !important; 7835 text-transform: none !important; 7836 margin-left: 8px !important; 7837 } 7838 /* Legend = "Size XXS" row. Render the label + value side-by-side. */ 7839 .product-information variant-picker fieldset.variant-option > legend { 7840 display: inline-flex !important; 7841 align-items: baseline !important; 7842 gap: 8px !important; 7843 padding: 0 !important; 7844 margin-bottom: 8px !important; 7845 } 7846 7847 /* Hide Horizon's Color option row â bp-color-siblings owns the 7848 colour swatch UI, so the variant-picker Color row would render a 7849 duplicate "Color: Tomato Red" stacked below the BP swatches. 7850 Horizon renders this two different ways depending on how many 7851 values the option has: 7852 1. Multi-value: <fieldset class="variant-option"> with radio inputs. 7853 2. Single-value (one color attached to the product): 7854 <div class="variant-option" data-testid="variant-option-single"> 7855 Color <span class="variant-option__swatch-value">Tomato Red</span> 7856 </div> 7857 Hide both â unscoped selectors so they work even if the markup 7858 isn't inside .product-information for some reason. */ 7859 html body[data-template="product"] [data-testid="variant-option-single"], 7860 html body[data-template="product"] div.variant-option[data-testid="variant-option-single"], 7861 html body[data-template="product"] variant-picker [data-testid="variant-option-single"], 7862 html body[data-template="product"] .variant-picker__form > [data-testid="variant-option-single"], 7863 .product-information variant-picker fieldset.variant-option:has(input[name^="Color-"]), 7864 .product-information variant-picker fieldset.variant-option:has(input[name^="color-"]), 7865 .product-information variant-picker [data-testid="variant-option-single"], 7866 .product-information [data-testid="variant-option-single"], 7867 [data-testid="variant-option-single"] { 7868 display: none !important; 7869 visibility: hidden !important; 7870 height: 0 !important; 7871 overflow: hidden !important; 7872 margin: 0 !important; 7873 padding: 0 !important; 7874 } 7875 7876 /* The theme setting variant_button_width = "equal-width-buttons" makes 7877 the fieldset a GRID of equal 1fr tracks (base.css:2179), which stretches 7878 every size chip to the same width and ignores the labels' flex. Figma 7879 wants padding-hugged chips, so lay the row out as wrapping flex instead 7880 â this is what actually kills the equal width (label flex alone can't, 7881 because grid items don't honor flex). */ 7882 .product-information variant-picker .variant-option--equal-width-buttons { 7883 display: flex !important; 7884 flex-wrap: wrap !important; 7885 grid-template-columns: none !important; 7886 gap: 8px !important; 7887 } 7888 7889 /* ---- Variant picker: button row ---------------------------------- 7890 Per Figma: size chips are padding-sized (13px x / 16px y), NOT 7891 fixed-width â the chip hugs its label. White bg, black CTA/CTA 7892 Small text (#171609 · Messina · 12 / 500 / 145% / -0.12). Squared 7893 corners, 1px outline border. */ 7894 .product-information variant-picker .variant-option__button-label, 7895 .product-information variant-picker label[role="radio"], 7896 .product-information variant-picker input[type="radio"] + label, 7897 .product-information .variant-option__button, 7898 .product-information .variant-picker__option-value { 7899 /* Kill Horizon's fixed flex-basis (base.css: flex 0 0 3.25em) + 7900 equal-width-buttons min-width so each chip hugs its label 7901 instead of all rendering the same width. */ 7902 flex: 0 0 auto !important; 7903 min-width: 0 !important; 7904 width: auto !important; 7905 min-height: 0 !important; 7906 padding: 16px 13px !important; 7907 display: inline-flex !important;
7908 align-items: center !important; 7909 justify-content: center !important; 7910 border: 1px solid #E1E0D4 !important; 7911 background: #FDFFFA !important; 7912 color: var(--bp-color-text, #171609) !important; 7913 font-family: var(--bp-font-body) !important; 7914 font-size: 12px !important; 7915 font-style: normal !important; 7916 font-weight: 500 !important; 7917 line-height: 145% !important; 7918 letter-spacing: -0.12px !important; 7919 text-transform: none !important; 7920 border-radius: 0 !important; 7921 cursor: pointer; 7922 transition: background-color 0.12s ease, color 0.12s ease, border-color 0.12s ease; 7923 } 7924 /* Hover (available, non-selected) â invert to the selected look: 7925 black bg, white text. Per Figma hover === selected. Disabled chips 7926 are protected by the higher-specificity :has(input[...]) rule below. */ 7927 .product-information variant-picker .variant-option__button-label:hover, 7928 .product-information variant-picker label[role="radio"]:hover, 7929 .product-information .variant-option__button:hover, 7930 .product-information .variant-picker__option-value:hover { 7931 background: var(--bp-color-text, #171609) !important; 7932 color: var(--bp-color-cta-text, #FDFFFA) !important; 7933 border-color: var(--bp-color-text, #171609) !important; 7934 } 7935 .product-information variant-picker .variant-option__button-label:hover .variant-option__button-label__text { 7936 color: var(--bp-color-cta-text, #FDFFFA) !important; 7937 } 7938 /* Selected â black background, white text. 7939 Uses data-current-checked as the sole source of truth. morph.js's 7940 onBeforeUpdate hook preserves this attribute through every async 7941 variant fetch, so the dark highlight never snaps back to the default 7942 variant during the ~1 s network round-trip. 7943 input:checked is intentionally excluded here: morph resets it from 7944 the server HTML (always the default variant) before the request 7945 resolves, which would cause two buttons to appear dark simultaneously 7946 if we used both selectors. */ 7947 .product-information variant-picker .variant-option__button-label:has(input[data-current-checked="true"]) { 7948 background: var(--bp-color-text, #171609) !important; 7949 color: var(--bp-color-cta-text, #FDFFFA) !important; 7950 border-color: var(--bp-color-text, #171609) !important; 7951 } 7952 /* Selected text span â force white regardless of inherited color. */ 7953 .product-information variant-picker .variant-option__button-label:has(input[data-current-checked="true"]) .variant-option__button-label__text { 7954 color: var(--bp-color-cta-text, #FDFFFA) !important; 7955 } 7956 7957 /* ---- Pill animation position â use data-current-checked, not :checked --- 7958 Horizon's base.css positions the sliding pill using :checked-based selectors. 7959 morph.js resets input.checked from the server HTML after each fetch, which 7960 causes the pill to snap to the server-default variant (XXS) for ~1 frame 7961 â visible to the user as a square "jumping back". data-current-checked is 7962 preserved by morph.js so these overrides keep the pill on the user's actual 7963 selection throughout the morph cycle. 7964 7965 Pattern mirrors Horizon's base.css lines ~2095â2160 with :checked â data-current-checked. */ 7966 7967 /* Reset any pill offset inherited from Horizon's ~ label rule on the 7968 selected label itself (the rule below would otherwise assign a -100%-1px 7969 offset when the checked input is to the left of another checked label). */ 7970 .product-information variant-picker .variant-option__button-label:has(input[data-current-checked="true"]) { 7971 --pill-offset: 0; 7972 } 7973 /* Labels BEFORE the selected: shift their pill RIGHT (off-screen). */ 7974 .product-information variant-picker .variant-option__button-label:not(:has(input[data-current-checked="true"])):has(~ label > input[data-current-checked="true"]) { 7975 --pill-offset: calc(100% + 1px); 7976 } 7977 /* Labels AFTER the selected: shift their pill LEFT (off-screen). */ 7978 .product-information variant-picker .variant-option__button-label:has(input[data-current-checked="true"]) ~ label { 7979 --pill-offset: calc(-100% - 1px); 7980 } 7981 /* Anchor the pill to the right edge in labels that precede the selection. */ 7982 .product-information variant-picker .variant-option__button-label:not(:has(input[data-current-checked="true"])):h
7982as(~ label > input[data-current-checked="true"]) .variant-option__button-label__pill { 7983 right: 0; 7984 left: unset; 7985 } 7986 /* Anchor the pill to the left edge in labels that follow the selection. */ 7987 .product-information variant-picker .variant-option__button-label:has(input[data-current-checked="true"]) ~ label .variant-option__button-label__pill { 7988 left: 0; 7989 right: unset; 7990 } 7991 /* Wrap-around case (3+ variants): firstâlast direction. */ 7992 .product-information variant-picker .variant-option--buttons:has(:nth-of-type(3)) .variant-option__button-label:not(:has(input[data-current-checked="true"])):first-of-type:has(~ label:last-of-type > input[data-current-checked="true"]) { 7993 --pill-offset: calc(-100% - 1px); 7994 } 7995 /* No size pre-selected (fix/pdp-no-default-size): Horizon's pill defaults 7996 to visible (offset 0) and relies on ONE selected sibling to push the 7997 others off-screen. With zero selection nothing pushes them, so every 7998 pill showed and painted all buttons black. Hide all pills while the 7999 fieldset has no selection; the data-current-checked rules above re-reveal 8000 the chosen one once a size is picked. */ 8001 .product-information variant-picker .variant-option--buttons:not(:has(input[data-current-checked="true"])) .variant-option__button-label__pill { 8002 opacity: 0 !important; 8003 } 8004 /* Disabled (unavailable / OOS) variants â Figma CTA/CTA Small spec: 8005 border 1px #E1E0D4 · bg #F7F6EA · color #74736B 8006 Messina Sans 12px / 500 / 145% / -0.12px 8007 Horizon paints a diagonal SVG strikethrough 8008 (.variant-option__strikethrough) for sold-out items, so no 8009 text-decoration: line-through here â that would double-stroke. */ 8010 .product-information .variant-option__button[disabled], 8011 .product-information .variant-option__button[aria-disabled="true"], 8012 .product-information variant-picker .variant-option__button-label:has(input[disabled]), 8013 .product-information variant-picker .variant-option__button-label:has(input[aria-disabled="true"]), 8014 .product-information variant-picker .variant-option__button-label:has(input[data-option-available="false"]), 8015 .product-information input[type="radio"]:disabled + label { 8016 border: 1px solid #E1E0D4 !important; 8017 background: #F7F6EA !important; 8018 /* #74736B on #F7F6EA is 4.38:1 (just under AA). #666559 = 5.5:1. These 8019 unavailable sizes are interactive (they route to Notify Me), so they are 8020 NOT WCAG-exempt disabled controls â must pass AA (QA 7/27, contrast). */ 8021 color: #666559 !important; 8022 font-family: var(--bp-font-body) !important; 8023 font-size: 12px !important; 8024 font-weight: 500 !important; 8025 line-height: 145% !important; 8026 letter-spacing: -0.12px !important; 8027 cursor: not-allowed; 8028 opacity: 1 !important; 8029 text-decoration: none !important; 8030 } 8031 /* Force the inner text element to inherit the OOS color so it 8032 doesn't fall back to the selected/available text color. */ 8033 .product-information variant-picker .variant-option__button-label:has(input[disabled]) .variant-option__button-label__text, 8034 .product-information variant-picker .variant-option__button-label:has(input[aria-disabled="true"]) .variant-option__button-label__text, 8035 .product-information variant-picker .variant-option__button-label:has(input[data-option-available="false"]) .variant-option__button-label__text { 8036 /* Match the button rule above â #666559 for AA on the cream chip. */ 8037 color: #666559 !important; 8038 } 8039 8040 /* ---- Add to Bag button â CTA/CTA Regular -------------------------- 8041 #FDFFFA on #171609 · Messina · 17 / 500 / normal / -0.17 · 8042 normal-case. No icon â text only. 20px gap above so the button 8043 sits clearly below the labels/links row. */ 8044 .product-information buy-buttons-component, 8045 .product-information .buy-buttons { 8046 display: flex; 8047 flex-direction: column; 8048 gap: 12px; 8049 margin-top: 20px !important; 8050 } 8051 .product-information add-to-cart-component button, 8052 .product-information button[name="add"], 8053 .product-information .buy-buttons button[type="submit"] { 8054 width: 100%; 8055 background: var(--bp-color-text, #171609) !important; 8056 color: var(--bp-color-cta-text, #FDFFFA) !important; 8057 border: 1px solid var(--bp-color-text, #171609) !important; 8058 border-radius: 0 !important; 8059 padding: 16px 24px !important; 8060 font-family: var(--bp-font-body) !important; 8061 font-size: 17px !important; 8062 font-style: normal !important; 8063 font-weight: 500 !important; 8064 line-height: normal !important; 8065 letter-spacing: -0.17px !important; 8066 text-transform: none !important; 8067 cursor: pointer; 8068 display: inline-flex !important;
8069 align-items: center !important; 8070 justify-content: center !important; 8071 gap: 10px !important; 8072 transition: background-color 0.12s ease, color 0.12s ease; 8073 } 8074 /* Hide Horizon's default cart icon (the .svg-wrapper.add-to-cart-icon 8075 span baked into add-to-cart-component) so only the BP bag SVG 8076 from the ::after pseudo-element renders. */ 8077 .product-information add-to-cart-component button .svg-wrapper.add-to-cart-icon, 8078 .product-information add-to-cart-component button .add-to-cart-icon, 8079 .product-information button[name="add"] .svg-wrapper.add-to-cart-icon, 8080 .product-information button[name="add"] .add-to-cart-icon { 8081 display: none !important; 8082 } 8083 8084 /* BP bag icon appended via ::after â the SVG path from the design 8085 spec, fill=#FDFFFA so it reads as a white outline on the 8086 filled-black button. URL-encoded data URI. */ 8087 .product-information add-to-cart-component button::after, 8088 .product-information button[name="add"]::after, 8089 .product-information .buy-buttons button[type="submit"]::after { 8090 content: '' !important; 8091 display: inline-block !important; 8092 flex: 0 0 20px !important; 8093 width: 20px !important; 8094 height: 20px !important; 8095 background-repeat: no-repeat; 8096 background-position: center; 8097 background-size: contain; 8098 background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20' fill='none'><path d='M4.18929 5.74443H16.5343H16.6292L16.6729 5.74559C16.9007 5.75108 17.1328 5.87813 17.2448 6.08099C17.3145 6.20166 17.3382 6.30382 17.3547 6.43057C17.6354 9.18815 17.9008 11.9961 18.3051 14.7357C18.3771 15.2468 18.4466 15.7581 18.5137 16.2698C18.5163 16.2918 18.5195 16.336 18.5207 16.3583C18.5378 16.6194 18.4156 16.9264 18.1508 17.0688C17.9651 17.1753 17.7807 17.1764 17.5845 17.1756C16.1978 17.1756 4.69428 17.1756 3.34023 17.1756C3.20422 17.1727 3.01496 17.1863 2.86505 17.1452C2.45036 17.0407 2.25213 16.6561 2.31666 16.2446C2.41071 15.5272 2.58579 14.249 2.68621 13.5186C2.95476 11.54 3.15849 9.46683 3.35962 7.47468C3.37582 7.35719 3.44296 6.63748 3.45714 6.52896L3.46698 6.43433L3.46814 6.42246C3.46814 6.41957 3.46901 6.41349 3.47045 6.40568C3.5156 6.02427 3.79254 5.74038 4.18929 5.74443ZM4.18929 6.61259C4.2136 6.61259 4.24804 6.5961 4.27872 6.57353C4.30302 6.555 4.3201 6.53099 4.32704 6.51854C4.33457 6.5061 4.33486 6.50147 4.33399 6.50436C4.33341 6.50812 4.33225 6.51362 4.33138 6.51767C4.33138 6.51767 4.33081 6.51941 4.33052 6.52433L4.32936 6.53619L4.31923 6.63053C4.132 8.41519 3.9054 10.3182 3.74624 12.1017C3.63685 13.4482 3.56103 15.0257 3.46727 16.3754L3.46582 16.3916V16.3962C3.46582 16.3962 3.46611 16.3974 3.4664 16.3916C3.4664 16.3873 3.4664 16.3751 3.46351 16.3589C3.45396 16.2909 3.41721 16.2162 3.3602 16.1508C3.29306 16.0799 3.20422 16.0247 3.12319 16.0163C3.11856 16.0163 3.13361 16.0174 3.15647 16.0177C4.67199 16.0183 16.0117 16.0177 17.5848 16.0177H17.6719C17.6823 16.0171 17.6916 16.0168 17.6962 16.0166C17.7014 16.0163 17.7034 16.016 17.6991 16.0163C17.5752 16.0287 17.4268 16.1592 17.3857 16.2796C17.3576 16.3557 17.3617 16.3789 17.362 16.3942C17.362 16.3922 17.362 16.3931 17.362 16.3887L17.3605 16.3725C17.254 14.8544 17.1872 13.3293 17.0509 11.8149C16.8906 10.0896 16.6752 8.23172 16.4883 6.50465C16.495 6.52028 16.5161 6.55443 16.5468 6.57584C16.5714 6.59494 16.5989 6.60594 16.6145 6.6097L16.6243 6.61201H16.6284C16.6284 6.61201 16.6298 6.61288 16.6304 6.61288C16.6318 6.61288 16.6336 6.61288 16.633 6.61288C16.6324 6.61288 16.635 6.61288 16.6292 6.61288C16.5783 6.6123 16.4 6.61346 16.3445 6.61288H4.18929V6.61259Z' fill='%23FDFFFA'/><path d='M6.08276 8.7807C6.17855 7.51897 6.20633 6.21411 6.58514 4.98913C7.15408 3.11013 8.44186 2.11637 10.4138 2.12679C13.7999 2.13345 14.5028 4.88003 14.6718 7.71401C14.6961 8.07459 14.7155 8.43459 14.7447 8.78041L13.5872 8.77491C13.617 8.46064 13.6572 8.02655 13.6752 7.71864C13.8008 5.83357 13.5973 3.34135 11.3985 2.81611C10.7573 2.66766 10.07 2.66766 9.42867 2.81611C7.61479 3.24817 7.15119 5.03746 7.12659 6.68726C7.11096 7.36935 7.17521 8.09138 7.24032 8.77491L6.08276 8.78041V8.7807Z' fill='%23FDFFFA'/></svg>"); 8099 } 8100 .product-information add-to-cart-component button:hover, 8101 .product-information button[name="add"]:hover, 8102 .product-information .buy-buttons button[type="submit"]:hover { 8103 background: transparent !important; 8104 color: var(--bp-color-text, #171609) !important; 8105 } 8106 /* Hover state â re-encode the SVG with fill='#171609' so the icon 8107 inverts to black on the now-transparent button. */ 8108 .product-information add-to-cart-component button:hover::after, 8109 .product-information button[name="add"]:hover::after, 8110 .product-information .buy-buttons button[type="submit"]:hover::after { 8111 background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='20' height='20' viewBox='0 0 20 20' fill='none'><path d='M4.18929 5.74443H16.5343H16.6292L16.6729 5.74559C16.9007 5.75108 17.1328 5.87813 17.2448 6.08099C17.3145 6.20166 17.3382 6.30382 17.3547 6.43057C17.6354 9.18815 17.9008 11.9961 18.3051 14.7357C18.3771 15.2468 18.4466 15.7581 18.5137 16.2698C18.5163 16.2918 18.5195 16.336 18.5207 16.3583C18.5378 16.6194
811118.4156 16.9264 18.1508 17.0688C17.9651 17.1753 17.7807 17.1764 17.5845 17.1756C16.1978 17.1756 4.69428 17.1756 3.34023 17.1756C3.20422 17.1727 3.01496 17.1863 2.86505 17.1452C2.45036 17.0407 2.25213 16.6561 2.31666 16.2446C2.41071 15.5272 2.58579 14.249 2.68621 13.5186C2.95476 11.54 3.15849 9.46683 3.35962 7.47468C3.37582 7.35719 3.44296 6.63748 3.45714 6.52896L3.46698 6.43433L3.46814 6.42246C3.46814 6.41957 3.46901 6.41349 3.47045 6.40568C3.5156 6.02427 3.79254 5.74038 4.18929 5.74443ZM4.18929 6.61259C4.2136 6.61259 4.24804 6.5961 4.27872 6.57353C4.30302 6.555 4.3201 6.53099 4.32704 6.51854C4.33457 6.5061 4.33486 6.50147 4.33399 6.50436C4.33341 6.50812 4.33225 6.51362 4.33138 6.51767C4.33138 6.51767 4.33081 6.51941 4.33052 6.52433L4.32936 6.53619L4.31923 6.63053C4.132 8.41519 3.9054 10.3182 3.74624 12.1017C3.63685 13.4482 3.56103 15.0257 3.46727 16.3754L3.46582 16.3916V16.3962C3.46582 16.3962 3.46611 16.3974 3.4664 16.3916C3.4664 16.3873 3.4664 16.3751 3.46351 16.3589C3.45396 16.2909 3.41721 16.2162 3.3602 16.1508C3.29306 16.0799 3.20422 16.0247 3.12319 16.0163C3.11856 16.0163 3.13361 16.0174 3.15647 16.0177C4.67199 16.0183 16.0117 16.0177 17.5848 16.0177H17.6719C17.6823 16.0171 17.6916 16.0168 17.6962 16.0166C17.7014 16.0163 17.7034 16.016 17.6991 16.0163C17.5752 16.0287 17.4268 16.1592 17.3857 16.2796C17.3576 16.3557 17.3617 16.3789 17.362 16.3942C17.362 16.3922 17.362 16.3931 17.362 16.3887L17.3605 16.3725C17.254 14.8544 17.1872 13.3293 17.0509 11.8149C16.8906 10.0896 16.6752 8.23172 16.4883 6.50465C16.495 6.52028 16.5161 6.55443 16.5468 6.57584C16.5714 6.59494 16.5989 6.60594 16.6145 6.6097L16.6243 6.61201H16.6284C16.6284 6.61201 16.6298 6.61288 16.6304 6.61288C16.6318 6.61288 16.6336 6.61288 16.633 6.61288C16.6324 6.61288 16.635 6.61288 16.6292 6.61288C16.5783 6.6123 16.4 6.61346 16.3445 6.61288H4.18929V6.61259Z' fill='%23171609'/><path d='M6.08276 8.7807C6.17855 7.51897 6.20633 6.21411 6.58514 4.98913C7.15408 3.11013 8.44186 2.11637 10.4138 2.12679C13.7999 2.13345 14.5028 4.88003 14.6718 7.71401C14.6961 8.07459 14.7155 8.43459 14.7447 8.78041L13.5872 8.77491C13.617 8.46064 13.6572 8.02655 13.6752 7.71864C13.8008 5.83357 13.5973 3.34135 11.3985 2.81611C10.7573 2.66766 10.07 2.66766 9.42867 2.81611C7.61479 3.24817 7.15119 5.03746 7.12659 6.68726C7.11096 7.36935 7.17521 8.09138 7.24032 8.77491L6.08276 8.78041V8.7807Z' fill='%23171609'/></svg>"); 8112 } 8113 /* Hide the ATC button ONLY when the BP Notify Me block replaces it as 8114 the primary CTA â i.e. the variant is sold out AND restock-eligible 8115 (full price, no on-hand, no inbound). The inline script below sets 8116 `[data-bp-sold-out]` on that same gate; a sold-out-but-NOT-eligible 8117 variant keeps Horizon's disabled "Sold out" button so the buy box is 8118 never left empty. 8119 8120 Gating on the attribute (not `[disabled]`) also avoids the transient 8121 submit-disable that product-form.js:56 applies mid-add-to-cart, which 8122 would otherwise flash-hide the button on every successful click. */ 8123 .product-information add-to-cart-component[data-bp-sold-out] button[data-testid="standalone-add-to-cart"] { 8124 display: none !important; 8125 } 8126 8127 /* ---- Rise.ai GiftWizard "Send as a Gift" button ---------------- 8128 Rise's app injects its own <button> INSIDE <add-to-cart-component> 8129 as a sibling of the real Add to Bag button on gift-card PDPs. 8130 Both then match the ATC selector above and pick up the dark 8131 filled treatment. 8132 8133 Override just Rise's button (it always carries `.gwbutton` and 8134 `.GiftWizard-gift-button`) to render as the BP secondary CTA: 8135 stacked full-width below Add to Bag, white bg, black border, 8136 text-only. Specificity is (0,3,3), cleanly beats the ATC 8137 rule's (0,1,3) so Horizon's !important doesn't win. */ 8138 .product-information add-to-cart-component button.gwbutton.GiftWizard-gift-button, 8139 .product-information add-to-cart-component button.gwbutton, 8140 .product-information add-to-cart-component button.GiftWizard-gift-button { 8141 display: flex !important; 8142 width: 100% !important; 8143 max-height: 44px !important; 8144 min-height: 44px !important; 8145 padding: 16px !important; 8146 margin: 0 !important; 8147 justify-content: center !important;
8148 align-items: center !important; 8149 gap: 4px !important; 8150 background: var(--bp-color-bg-page) !important; 8151 background-color: var(--bp-color-bg-page) !important; 8152 color: var(--bp-color-text, #171609) !important; 8153 border: 1px solid var(--bp-color-text, #171609) !important; 8154 border-radius: 0 !important; 8155 box-shadow: none !important; 8156 font-family: var(--bp-font-body) !important; 8157 font-size: 17px !important; 8158 font-weight: 500 !important; 8159 letter-spacing: -0.17px !important; 8160 line-height: normal !important; 8161 text-transform: none !important; 8162 cursor: pointer !important; 8163 transition: background-color 0.15s ease, color 0.15s ease !important; 8164 } 8165 .product-information add-to-cart-component button.gwbutton::after, 8166 .product-information add-to-cart-component button.GiftWizard-gift-button::after { 8167 display: none !important; 8168 content: none !important; 8169 } 8170 .product-information add-to-cart-component button.gwbutton:hover, 8171 .product-information add-to-cart-component button.GiftWizard-gift-button:hover { 8172 background: var(--bp-color-text, #171609) !important; 8173 background-color: var(--bp-color-text, #171609) !important; 8174 color: var(--bp-color-cta-text, #FDFFFA) !important; 8175 } 8176 /* Text-only â hide any icon Rise's app may inject inside its button. */ 8177 .product-information add-to-cart-component button.gwbutton svg, 8178 .product-information add-to-cart-component button.gwbutton img, 8179 .product-information add-to-cart-component button.gwbutton i, 8180 .product-information add-to-cart-component button.GiftWizard-gift-button svg, 8181 .product-information add-to-cart-component button.GiftWizard-gift-button img, 8182 .product-information add-to-cart-component button.GiftWizard-gift-button i { 8183 display: none !important; 8184 } 8185 /* Rise's app stamps `style="margin-right: 15px; margin-bottom: 15px;"` 8186 on the sibling Add to Bag button â zero those out so the two 8187 buttons stack flush per the design. */ 8188 .product-information add-to-cart-component:has(button.gwbutton) button:not(.gwbutton):not(.GiftWizard-gift-button) { 8189 margin: 0 0 8px 0 !important; 8190 } 8191 8192 /* ---- Quantity selector â keep compact, BP border ---------------- */ 8193 .product-information quantity-input, 8194 .product-information .quantity-selector, 8195 .product-information .quantity { 8196 border: 1px solid var(--bp-pdp-border); 8197 border-radius: 0; 8198 height: 40px; 8199 } 8200 .product-information quantity-input input, 8201 .product-information .quantity-selector input { 8202 font-family: var(--bp-font-body); 8203 color: var(--bp-pdp-text); 8204 } 8205 8206 /* ---- Accelerated checkout (Shop Pay) ---------------------------- 8207 Hidden on the PDP per Figma â the bp-pay-in-4 block renders a 8208 static "Pay in full or in 4 interest-free installments with 8209 Shop Pay" line in its place. The block lives after buy-buttons 8210 in templates/product.json's block_order. */ 8211 .product-information accelerated-checkout-component, 8212 .product-information .accelerated-checkout-block, 8213 .product-information shopify-buy-it-now-button, 8214 .product-information shop-pay-button, 8215 .product-information .shopify-payment-button, 8216 .product-information [data-shop-pay-button] { 8217 display: none !important; 8218 } 8219 8220 /* Tighten the gap between the Add-to-bag button and the Pay-in-4 line so it 8221 sits right below the button (QA: Amber â was ~36px, want ~10px). PDP only 8222 (.product-information scope); the quick-add flyout hides this block and 8223 uses its own footer line. */
8224 .product-information .bp-pay-in-4 { 8225 margin-top: -1rem; 8226 } 8227 8228 /* ---- BP product accordion â Body/XSmall Book typography ---------- 8229 Label (summary + inner span): #171609 · Messina · 12 / 400 / 145% / -0.12 8230 Content (body): #74736B · Messina · 12 / 400 / 145% / -0.12 8231 8232 The accordion block's own CSS sets the summary to 14 / 660 / uppercase. 8233 Override at every common selector for the summary itself AND the inner 8234 <span> that carries the label text. */ 8235 .product-information .bp-acc__summary, 8236 .product-information .bp-acc__summary span, 8237 .product-information .bp-acc summary, 8238 .product-information .bp-acc summary span, 8239 .product-information .bp-acc__item summary, 8240 .product-information .bp-acc__item summary span, 8241 html body .bp-acc__summary, 8242 html body .bp-acc__summary span, 8243 html body .bp-acc summary span { 8244 color: var(--bp-color-text, #171609) !important; 8245 font-family: var(--bp-font-body), 'Messina Sans VF', system-ui, sans-serif !important; 8246 font-size: 12px !important; 8247 font-style: normal !important; 8248 font-weight: 400 !important; 8249 line-height: 145% !important; 8250 letter-spacing: -0.12px !important; 8251 text-transform: none !important; 8252 } 8253 .product-information .bp-acc__body, 8254 .product-information .bp-acc__body *, 8255 .product-information .bp-acc__list, 8256 .product-information .bp-acc__list li, 8257 .product-information .bp-acc__item > div, 8258 .product-information .bp-acc__item > div * { 8259 color: var(--bp-color-body-copy, #74736B) !important; 8260 font-family: var(--bp-font-body), 'Messina Sans VF', system-ui, sans-serif !important; 8261 font-size: 12px !important; 8262 font-style: normal !important; 8263 font-weight: 400 !important; 8264 line-height: 145% !important; 8265 letter-spacing: -0.12px !important; 8266 text-transform: none !important; 8267 } 8268 8269 /* ---- Inline BP CTAs (Size Chart, Find My Size) line up ---------- */ 8270 .bp-size-chart-link, 8271 .bp-fma { 8272 margin-top: 4px; 8273 } 8274 8275 /* ---- Description text ------------------------------------------- */ 8276 .product-information [data-block-type="text"] p, 8277 .product-information .product-description, 8278 .product-information .rte { 8279 font-family: var(--bp-font-body); 8280 font-size: 14px; 8281 line-height: 145%; 8282 color: var(--bp-pdp-body); 8283 } 8284 8285 /* ---- 8px gap between right-column sections ---------------------- 8286 The right column lays out as a flex column: breadcrumbs, stars, 8287 title, price, promo, pills, divider, colors, size, etc. Force 8288 8px between every section regardless of the section schema's 8289 gap setting (which is also set to 8 via product.json). */ 8290 .product-information .product-information__details, 8291 .product-information .product-information__details > * { 8292 --gap: 8px; 8293 } 8294 .product-information .product-information__details { 8295 gap: 8px !important; 8296 } 8297 /* Inside the title/price group block (group_icgrde) â keep 8px too. */ 8298 .product-information .product-information__details .group, 8299 .product-information .product-information__details [class*="group"] { 8300 gap: 8px !important; 8301 } 8302 8303 /* ---- Divider line between price and variant picker -------------- */ 8304 .product-information .divider, 8305 .product-information hr { 8306 border: 0; 8307 border-top: 1px solid var(--bp-pdp-border-soft); 8308 margin: 16px 0; 8309 } 8310 8311 /* ---- Yotpo star widget alignment in title group ----------------- */ 8312 .product-information .bp-yotpo-stars { 8313 margin: 6px 0 2px; 8314 } 8315 .yotpo-bottomline .yotpo-stars { display: inline-block; } 8316 8317 /* ---- Hide "Write a Review" link in the top bottomline widget ---- 8318 Scoped to .bp-yotpo-stars so the write-review form in the full 8319 reviews section below is unaffected. */ 8320 .bp-yotpo-stars [class*="write-review"], 8321 .bp-yotpo-stars [class*="writeReview"], 8322 .bp-yotpo-stars .yotpo-bottomline-link, 8323 .bp-yotpo-stars .yotpo-sr-bottom-line-link, 8324 .bp-yotpo-stars .yotpo-sr-bottom-line-new-review { 8325 display: none !important; 8326 } 8327 8328 /* ---- Reviews section spacing ------------------------------------ */ 8329 .bp-pdp-extras .bp-pdp-extras__reviews { padding: 0 20px; } 8330 8331 /* ---- Media gallery polish â compact thumbs, no rounded ---------- */ 8332 .product-information media-gallery img, 8333 .product-information .media-gallery img { 8334 border-radius: 0; 8335 } 8336
8337 /* ---- Responsive layout â gallery left / buy-box right ----------- */ 8338 @media (min-width: 1024px) { 8339 .product-information { gap: 40px; } 8340 } 8341 8342 /* ================================================================= 8343 Figma-spec'd PDP layout (frame 309:164601): 8344 - Container 1360 inside 1440 (40px gutters) 8345 - Gallery 900 wide, Right Column 440 wide 8346 - 2-col gallery grid, 446 Ã 558 cells, 8px gap 8347 - Swatch 40Ã40, size button 40 high, ATC 440Ã44 8348 - Accordion row 47 tall 8349 ================================================================= */ 8350 8351 @media screen and (min-width: 1024px) { 8352 /* Container â outer gutter 40px each side */ 8353 body[data-template="product"] .product-information, 8354 body[data-template="product"] .product-information > * { 8355 padding-left: 0; 8356 padding-right: 0; 8357 } 8358 body[data-template="product"] .product-information .page-width, 8359 body[data-template="product"] .product-information .section-padding, 8360 body[data-template="product"] .product-information .container { 8361 padding-inline: 40px !important; 8362 max-width: 1440px !important; 8363 } 8364 8365 /* Gallery â 900 wide, 2-column grid, 446Ã558 cells */ 8366 body[data-template="product"] .product-information media-gallery, 8367 body[data-template="product"] .product-information .media-gallery { 8368 flex: 0 0 900px; 8369 max-width: 900px; 8370 gap: 8px !important; 8371 } 8372 8373 /* Sticky media gallery (QA, Adrienne). CSS position:sticky never engaged 8374 reliably in this grid across environments, so the pin is done in JS 8375 (BpPdpGallery.initStickyPin) â but SMOOTHLY: the gallery is `position: 8376 fixed` with a CONSTANT top while in the sticky zone, so the browser keeps 8377 it fixed natively (no per-frame movement â no jank, unlike the old 8378 translateY pin that chased the scroll every frame). JS only runs at the 8379 enter/exit thresholds. 8380 8381 Keep `align-items: start` so the media column sits at the gallery's 8382 natural height and the grid ROW height is held by the taller details 8383 column â that way, when the gallery goes `fixed` (out of flow) the row 8384 doesn't collapse/jump (details still defines it). The JS references the 8385 details column for the travel distance, so pinning the gallery never 8386 destabilises the measurement. */ 8387 body[data-template="product"] .product-information__grid { 8388 align-items: start !important; 8389 } 8390 body[data-template="product"] .product-information .media-gallery__item, 8391 body[data-template="product"] .product-information .media-gallery > * { 8392 aspect-ratio: 446 / 558; 8393 } 8394 8395 /* Right column â 440 wide cap, with 20px gap from gallery */ 8396 body[data-template="product"] .product-information .product-information__details, 8397 body[data-template="product"] .product-information .product-information__main--right, 8398 body[data-template="product"] .product-information [data-block-type="product-details"] { 8399 flex: 0 0 440px !important; 8400 max-width: 440px !important; 8401 margin-left: 20px; 8402 } 8403 } 8404 8405 /* ---- Breadcrumb â 11px uppercase, 15px line-height -------------- */ 8406 .bp-crumbs__item { font-size: 11px; letter-spacing: 1px; line-height: 15px; } 8407 8408 /* ---- Ratings row spacing (Yotpo) -------------------------------- */ 8409 .product-information .bp-yotpo-stars { min-height: 17px; } 8410 8411 /* ---- Title â Heading/H4 (24px / 120% / -0.48px) ----------------- */ 8412 /* Already in earlier rule â no change */ 8413 8414 /* ---- Price row layout â sale price first ($70 then $75 strikethrough). 8415 Typography + colors are owned by the Figma-spec price block near the 8416 top of this file; here we only lay the row out. */ 8417 .product-information .price { 8418 display: inline-flex; 8419 flex-wrap: wrap; 8420 gap: 6px; 8421 align-items: baseline; 8422 } 8423 8424 /* ---- Promo line height aligned to price row --------------------- */ 8425 .bp-promo-line { line-height: 20px; } 8426 8427 /* ---- Badges row â Popular / Matching Set chips ------------------ */ 8428 .bp-pills { margin-top: 8px; } 8429 .bp-pills__item { 8430 height: 25px; 8431 padding: 0 12px; 8432 font-size: 12px; 8433 letter-spacing: 0.6px; 8434 } 8435 8436 /* ---- Color row â label 17px, swatches 40Ã40 ---------------------- */ 8437 .bp-colors__label { font-size: 13px; line-height: 17px; } 8438 .bp-colors__swatch { width: 40px; height: 40px; border-radius: 50%; } 8439 .bp-colors__item.is-active .bp-colors__swatch { 8440 border-width: 1px; 8441 border-color: var(--bp-color-text, #171609); 8442 box-shadow: inset 0 0 0 2px #fff; 8443 } 8444 .bp-colors__swatches { gap: 10px; } 8445 8446 /* ---- Quantity selector â hidden on PDP, visible in cart ---------- */ 8447 .product-information .quantity-selector-wrapper { display: none; } 8448 8449 /* ---- Size button row â wrap, gap 8 (chips are padding-sized) ----- */ 8450 .product-information variant-picker .variant-options, 8451 .product-information variant-picker .variant-option__buttons { 8452 display: flex; 8453 flex-wrap: wrap; 8454 gap: 8px; 8455 } 8456 8457 /* ---- Size-chart + What's my size links sit side-by-side -------- */ 8458 .product-information .bp-size-chart-link, 8459 .product-information .bp-fma { 8460 display: inline-flex; 8461 } 8462 .product-information .bp-size-chart-link + .bp-fma, 8463 .product-information .bp-fma + .bp-size-chart-link { 8464 margin-left: 24px; 8465 } 8466 .bp-size-chart-link__btn, 8467 .bp-fma__btn { font-size: 12px; height: 24px; } 8468 8469 /* ---- ATC â 440Ã44 ------------------------------------------------ */ 8470 .product-information add-to-cart-component button, 8471 .product-information button[name="add"], 8472 .product-information .buy-buttons button[type="submit"],
8473 .bp-notify-trigger__btn { 8474 height: 44px; 8475 padding: 0 24px; 8476 font-size: 13px; 8477 letter-spacing: 1.2px; 8478 } 8479 8480 /* ---- "Pay in full or in 4..." installment line ------------------ */ 8481 .product-information .shop-pay-installments, 8482 .product-information .additional-checkout-buttons + p { 8483 margin-top: 8px; 8484 font-family: var(--bp-font-body); 8485 font-size: 12px; 8486 line-height: 17px; 8487 color: var(--bp-pdp-body); 8488 } 8489 8490 /* ---- Save for Later (heart) overlay ----------------------------- 8491 The bp-pdp-save-for-later block renders below buy-buttons as a 8492 sibling in the section's flex column. Reserve 56px on the right 8493 side of the ATC + Shop Pay row so the heart never overlaps the 8494 button labels, then pull the heart block up so it sits flush with 8495 the right edge of the Add to Bag row. The block's own 8496 `align-self: flex-end` handles horizontal positioning inside the 8497 flex column. */ 8498 .product-information buy-buttons-component:has(+ .bp-pdp-save-later), 8499 .product-information .buy-buttons:has(+ .bp-pdp-save-later) { 8500 padding-right: 56px; 8501 } 8502 .product-information buy-buttons-component + .bp-pdp-save-later, 8503 .product-information .buy-buttons + .bp-pdp-save-later { 8504 /* Lift over the buy-buttons row. 8505 ATC is 44px tall + a 12px gap from the next item below it. */ 8506 margin-top: calc(-1 * (44px + 12px)); 8507 pointer-events: none; 8508 position: relative; 8509 z-index: 2; 8510 } 8511 .product-information buy-buttons-component + .bp-pdp-save-later .bp-pdp-save-later__btn, 8512 .product-information .buy-buttons + .bp-pdp-save-later .bp-pdp-save-later__btn { 8513 pointer-events: auto; 8514 } 8515 8516 /* ---- Final Sale + Popular meta lines ---------------------------- */ 8517 .bp-final-sale { font-size: 12px; line-height: 17px; } 8518 8519 /* ---- Editor's Note ---------------------------------------------- */ 8520 .bp-editors-note__heading { font-style: italic; font-weight: 700; letter-spacing: 0; text-transform: none; font-family: var(--bp-font-display); font-size: 14px; } 8521 8522 /* ---- Accordion â 47px row, 14px chevron. Typography is locked 8523 above (Body/XSmall Book); don't redefine font-size here. */ 8524 .bp-acc__summary { min-height: 47px !important; padding: 14px 0 !important; } 8525 .bp-acc__chevron { width: 14px; height: 14px; } 8526 8527 /* ---- Stylitics image cells â Neutral cream background ------------ 8528 Each outfit grid cell renders <div class="stylitics-grid-img-wrap"> 8529 wrapping the product photo. Per design, the photo sits on a 8530 #F7F6EA tile so the items read consistently regardless of the 8531 image's own background. Targets both inline Complete-the-Look 8532 and bp-pdp-extras (How to Wear It / You'll Also Love). */ 8533 .bp-stylitics .stylitics-grid-img-wrap, 8534 .bp-stylitics [class*="stylitics-grid-img-wrap"], 8535 .bp-pdp-extras .stylitics-grid-img-wrap, 8536 .bp-pdp-extras [class*="stylitics-grid-img-wrap"] { 8537 background: var(--bp-color-bg-cream, #F7F6EA) !important; 8538 } 8539 8540 /* ---- Stylitics counter heading "Outfit 1/4" ---------------------- 8541 Same Signifier display face as the section heading, scaled down a 8542 bit (16px) so it reads as a secondary label above the carousel. 8543 Targets both the plain class and the Svelte-hashed variant. */ 8544 .bp-stylitics .stylitics-grid-counter-heading, 8545 .bp-stylitics [class*="stylitics-grid-counter-heading"], 8546 .bp-pdp-extras .stylitics-grid-counter-heading, 8547 .bp-pdp-extras [class*="stylitics-grid-counter-heading"] { 8548 display: none !important; 8549 } 8550 /* "1" + "/4" share the same family but read as a single inline tag; 8551 a small gap between "Outfit" and the count keeps them legible. */ 8552 .bp-stylitics .stylitics-count-index, 8553 .bp-stylitics .stylitics-count-total, 8554 .bp-pdp-extras .stylitics-count-index, 8555 .bp-pdp-extras .stylitics-count-total { 8556 font-family: var(--bp-font-display) !important; 8557 font-weight: 300 !important; 8558 color: var(--bp-color-text, #171609) !important; 8559 margin-left: 2px !important; 8560 } 8561 8562 /* ---- Stylitics item hover panel ---------------------------------------- 8563 DOM (from widget inspection): 8564 <a data-testid="stylitics-grid-item-info" class="stylitics-grid-item-info â¦"> 8565 <div class="stylitics-item-info-texts â¦"> 8566 <p class="stylitics-grid-item-info-brand â¦">â¦</p> 8567 <p class="stylitics-grid-item-info-name â¦">â¦</p> 8568 <p class="stylitics-grid-item-info-style â¦">â¦</p> 8569 <svelte-css-wrapper> 8570 <p data-testid="stylitics-ui-price" â¦>
8571 <span data-testid="stylitics-ui-regular-price" â¦>$70</span> 8572 </p> 8573 </svelte-css-wrapper> 8574 <div data-testid="stylitics-grid-item-info-cta" class="stylitics-grid-item-info-cta â¦">Shop</div> 8575 </div> 8576 </a> 8577 8578 NOTE: Svelte-hashed classes (svelte-efkltz etc.) are intentionally 8579 avoided here â they change on every widget build. We target 8580 data-testid attributes and the stable semantic class names instead. */ 8581 8582 /* Panel = the <a> element itself */ 8583 [data-testid="stylitics-grid-item-info"] { 8584 background: var(--bp-color-bg-page, #FEFFFA) !important; 8585 } 8586 /* Brand, name, style labels */ 8587 .stylitics-grid-item-info-brand, 8588 .stylitics-grid-item-info-name, 8589 .stylitics-grid-item-info-style { 8590 color: var(--bp-color-text, #171609) !important; 8591 font-family: var(--bp-font-body, 'Messina Sans VF', system-ui, sans-serif) !important; 8592 } 8593 /* Price */ 8594 [data-testid="stylitics-ui-price"], 8595 [data-testid="stylitics-ui-regular-price"] { 8596 color: var(--bp-color-text, #171609) !important; 8597 font-family: var(--bp-font-body, 'Messina Sans VF', system-ui, sans-serif) !important; 8598 } 8599 /* CTA "Shop" div â BP primary button: dark fill, near-white text */ 8600 [data-testid="stylitics-grid-item-info-cta"] { 8601 background: var(--bp-color-text, #171609) !important; 8602 color: var(--bp-color-cta-text, #FDFFFA) !important; 8603 font-family: var(--bp-font-body, 'Messina Sans VF', system-ui, sans-serif) !important; 8604 } 8605 8606 /* ---- Complete the Look (inline PDP right column) â Figma spec ---- 8607 Container: display: flex; padding: 20px; flex-direction: column; 8608 justify-content: center; align-items: center; gap: 20px; 8609 align-self: stretch; background: #F7F6EA (Neutral). 8610 Heading: Signifier 20 / 300 / 120% / -0.4 / #171609, centered. */ 8611 .product-information .bp-stylitics { 8612 display: flex !important; 8613 flex-direction: column !important; 8614 justify-content: center !important; 8615 align-items: center !important; 8616 gap: 20px !important; 8617 align-self: stretch !important; 8618 width: 100% !important; 8619 padding: 20px !important; 8620 margin: 0 !important; 8621 background: var(--bp-color-bg-cream, #F7F6EA) !important; 8622 } 8623 .product-information .bp-stylitics__heading { 8624 margin: 0 !important; 8625 color: var(--bp-color-text, #171609) !important; 8626 text-align: center !important; 8627 font-family: var(--bp-font-display) !important; 8628 font-size: 20px !important; 8629 font-style: normal !important; 8630 font-weight: 300 !important; 8631 line-height: 120% !important; 8632 letter-spacing: -0.4px !important; 8633 align-self: stretch !important; 8634 } 8635 8636 /* ---- Reviews + How to Wear It + You'll Also Love â full-bleed 8637 viewport width with the home-section padding convention: 8638 display: flex; padding: 40px; gap: 20px; flex-direction: column; 8639 align-items: flex-start; align-self: stretch. 8640 Mobile drops to the standard BP gutter (16px). */ 8641 .bp-pdp-extras-section, 8642 .bp-pdp-extras { 8643 width: 100%; 8644 max-width: none; 8645 margin: 0; 8646 padding: 0; 8647 /* Stylitics's composer renders fixed-width carousel rails and grid 8648 cells that occasionally overshoot the section on narrow viewports 8649 (visible as image cells getting cut off on the right edge of the 8650 page on mobile). Clip the overflow at the section so the layout 8651 stays inside the viewport â the carousel rails inside still 8652 scroll horizontally via their own overflow-x: auto. */ 8653 overflow-x: hidden; 8654 } 8655 .bp-pdp-extras .bp-stylitics, 8656 .bp-pdp-extras .bp-stylitics__target { 8657 max-width: 100%; 8658 overflow-x: hidden; 8659 } 8660 /* htwi keeps its own scrolling rail â don't clip the host element, 8661 otherwise its rail (overflow-x: auto) can't paint the offscreen 8662 slides during a swipe. The outer .bp-pdp-extras (above) still 8663 contains any layout overshoot at the section edge. */ 8664 body[data-template="product"] .bp-pdp-extras > .bp-stylitics, 8665 .bp-pdp-extras-section .bp-pdp-extras > .bp-stylitics, 8666 /* How to Wear It renders as its OWN standalone section since #142 split 8667 the combined bp-pdp-extras â its <bp-htwi class="bp-stylitics"> root is 8668 no longer a child of .bp-pdp-extras, so the two selectors above stopped 8669 matching it and the base .bp-stylitics card style (the standalone 8670 Stylitics widget: centered + cream) bled through. Reset it here too s
8670o 8671 HTWI keeps its pre-split look (left-aligned, transparent, section 8672 padding). Complete the Look keeps its own cream card (bp-ctl) untouched. */ 8673 bp-htwi.bp-stylitics { 8674 display: flex !important; 8675 flex-direction: column !important; 8676 align-items: flex-start !important; 8677 align-self: stretch !important; 8678 gap: 20px !important; 8679 padding: 32px 16px !important; 8680 margin: 0 auto !important; 8681 width: 100% !important; 8682 max-width: 90rem !important; 8683 background: transparent !important; 8684 } 8685 /* Stylitics Svelte widget's inner shell tends to ship a 1107 / 1400 8686 max-width. Override so the carousel inside How to Wear It / 8687 You'll Also Love fills the section's 40px-padded container. */ 8688 .bp-pdp-extras .bp-stylitics .bp-stylitics__target, 8689 .bp-pdp-extras .bp-stylitics__target, 8690 .bp-pdp-extras .bp-stylitics__target > section, 8691 .bp-pdp-extras .bp-stylitics__target [class*="stylitics-grid-base"], 8692 .bp-pdp-extras .bp-stylitics__target [class*="stylitics-grid-layout"], 8693 .bp-pdp-extras .bp-stylitics__target [class*="stylitics-ui-carousel"], 8694 .bp-pdp-extras .bp-stylitics__target [class*="stylitics-ui-carousel-card-list"] { 8695 width: 100% !important; 8696 max-width: none !important; 8697 margin: 0 !important; 8698 } 8699 @media (min-width: 1024px) { 8700 body[data-template="product"] .bp-pdp-extras > .bp-stylitics, 8701 .bp-pdp-extras-section .bp-pdp-extras > .bp-stylitics, 8702 bp-htwi.bp-stylitics { 8703 padding: 40px !important; 8704 } 8705 } 8706 /* Reviews block â capped at the 90rem page width and centered (margin 8707 auto), with the same 40px (desktop) / 16px (mobile) horizontal padding 8708 as the surrounding bp-stylitics sections so the three home-style modules 8709 share the same edges and content width on ultra-wide screens. */ 8710 body[data-template="product"] .bp-pdp-extras .bp-pdp-extras__reviews, 8711 .bp-pdp-extras-section .bp-pdp-extras .bp-pdp-extras__reviews { 8712 display: flex !important; 8713 flex-direction: column !important; 8714 align-items: stretch !important; 8715 gap: 20px !important; 8716 width: 100% !important; 8717 max-width: 90rem !important; 8718 margin: 0 auto !important; 8719 padding: 32px 16px !important; 8720 } 8721 @media (min-width: 1024px) { 8722 body[data-template="product"] .bp-pdp-extras .bp-pdp-extras__reviews, 8723 .bp-pdp-extras-section .bp-pdp-extras .bp-pdp-extras__reviews { 8724 padding: 40px !important; 8725 } 8726 } 8727 /* Yotpo's main-widget layout caps itself at 1380px and adds its own 8728 16px gutter. Kill both so the widget fills our padded container 8729 (which already provides the 40px desktop / 16px mobile padding). */ 8730 .yotpo-reviews-main-widget .yotpo-main-widget-layout .yotpo-base-layout, 8731 .bp-pdp-extras__reviews .yotpo-base-layout, 8732 #yotpo-reviews-container .yotpo-base-layout { 8733 width: 100% !important; 8734 max-width: none !important; 8735 margin: 0 !important; 8736 padding: 0 !important; 8737 box-sizing: border-box !important; 8738 } 8739 /* ---- Stylitics composer carousel ------------------------------------ 8740 Atlas parity: let the composer render its OWN native carousel + arrows 8741 on every breakpoint. We intentionally do NOT convert it to a vertical 8742 stack or hide the `stylitics-ui-carousel-arrow / -nav / -pagination` 8743 chevrons here â the earlier mobile override did both, which broke the 8744 native navigation. Keep only the width/box-sizing normalisation above. */ 8745 8746 /* "How to Wear It" / "You'll Also Love" headings â Heading/H3 spec 8747 (Signifier 32 / 300 / 120% / -0.64 / #171609, left-aligned). 8748 Applies to every .bp-stylitics__heading (Stylitics modules) and 8749 the optional section-level .bp-pdp-extras__heading. 8750 Unscoped selector with html body for maximum specificity. */ 8751 html body .bp-stylitics__heading, 8752 html body .bp-pdp-extras__heading, 8753 body[data-template="product"] .bp-pdp-extras .bp-stylitics__heading, 8754 body[data-template="product"] .bp-pdp-extras .bp-pdp-extras__heading { 8755 color: var(--bp-color-text, #171609) !important; 8756 text-align: left !important; 8757 font-family: var(--bp-font-display), Signifier, 'Times New Roman', serif !important; 8758 font-size: 32px !important; 8759 font-style: normal !important; 8760 font-weight: 300 !important; 8761 line-height: 120% !important; 8762 letter-spacing: -0.64px !important;
8763 align-self: stretch !important; 8764 margin: 0 !important; 8765 text-transform: none !important; 8766 } 8767 body[data-template="product"] .bp-pdp-extras .bp-stylitics__target { 8768 width: 100%; 8769 } 8770 8771 /* ================================================================= 8772 Yotpo reviews â match ProjectAtlas exactly (no custom layout / no 8773 custom typography). Atlas renders the bare v3 widget into a plain 8774 anchor div and lets Yotpo own everything. The only deviation we 8775 keep is forcing the star color from Yotpo's default black to BP 8776 gold (#EBAE55). 8777 8778 Yotpo v3 renders each star as <svg class="yotpo-star-rating-icon" 8779 yotpo-sr-star-{full,half,empty}> whose <path fill="url(#â¦)"> 8780 references a linearGradient with rgba(0,0,0,1) stops. A CSS `fill` 8781 declaration on the path beats the fill="url(#â¦)" attribute, and 8782 forcing the gradient stops to transparent neutralizes the gradient 8783 entirely. Selectors anchor on `#yotpo-reviews-container` to win 8784 specificity over Yotpo's id-scoped rules. 8785 ================================================================= */ 8786 .bp-pdp-extras__reviews, 8787 .yotpo-widget-instance { 8788 --yotpo-star-color: #EBAE55; 8789 } 8790 8791 /* Repaint each Yotpo star with the BP custom Star 1 SVG. We hide 8792 Yotpo's inner <path> and paint our shape as a background-image on 8793 the <svg> element itself â works on every star occurrence (summary 8794 row, filter chips, per-review cards, write-review modal) without 8795 having to selector-match Yotpo's container hierarchy. 8796 8797 Yotpo wraps each star in <div class="star-container"> and the row 8798 in <div class="yotpo-star-rating">. The container has no intrinsic 8799 size, so we set width/height on it AND on the SVG. Default visible 8800 size is 28Ã27, generous enough for review rows; the inline bottom- 8801 line variant near the product title overrides down to 16Ã15. */ 8802 .yotpo-star-rating:not(.yotpo-selected-score), 8803 .yotpo-review-star-rating, 8804 .yotpo-sr-bottom-line-summary .yotpo-star-rating { 8805 display: inline-flex !important; 8806 align-items: center !important; 8807 gap: 2px !important; 8808 line-height: 1 !important; 8809 } 8810 /* The score-filter's "currently-selected stars" overlay defaults to 8811 display:none inline. Honor that â only un-hide when a score is 8812 actually selected (Yotpo strips the inline display when active). */ 8813 .yotpo-score-filter .yotpo-selected-score[style*="display: none"] { 8814 display: none !important; 8815 } 8816 .star-container { 8817 display: inline-flex !important; 8818 width: 16px !important; 8819 height: 16px !important; 8820 flex: 0 0 auto !important; 8821 align-items: center; 8822 justify-content: center; 8823 } 8824 svg.yotpo-star-rating-icon, 8825 svg[class*="yotpo-sr-star"], 8826 .star-container > svg { 8827 width: 16px !important; 8828 height: 16px !important; 8829 transform: none !important; 8830 background-repeat: no-repeat; 8831 background-position: center; 8832 background-size: contain; 8833 display: block; 8834 overflow: visible; 8835 } 8836 svg.yotpo-star-rating-icon > *, 8837 svg[class*="yotpo-sr-star"] > *, 8838 .star-container > svg > * { 8839 display: none !important; 8840 } 8841 8842 /* Reviews-list star rating â 22px + 4px spacing per Figma. 8843 Yotpo injects its OWN nine-class rules (specificity 0,9,0 8844 !important) for star size and spacing. A plain class rule ties on 8845 specificity and then loses on source order, because Yotpo's CSS is 8846 injected late â which is why the earlier gap/size rules didn't take. 8847 The reviews widget lives inside <section id="bp-pdp-reviews">, so we 8848 anchor on that ID: one ID (1,x,y) beats any all-class Yotpo selector 8849 regardless of chain length or load order. 8850 8851 Spacing is margin-based, not flex `gap`: Yotpo spaces stars via 8852 per-.star-container margins, so we zero the row gap and set the 8853 inter-star margin directly (matches the Yotpo pattern QA supplied). 8854 8855 Two star ratings live inside the widget and both need this: 8856 .yotpo-review-star-rating â the per-review row. 8857 .yotpo-bottom-line-stars â the widget's aggregate summary rating 8858 at the top of the list. 8859 (The top-of-PDP summary is a separate block, .bp-yotpo-stars, sized 8860 independently below.) */ 8861 #bp-pdp-reviews .yotpo-review-star-rating, 8862 #bp-pdp-reviews .yotpo-bottom-line-stars { 8863 gap: 0 !important; 8864 } 8865 #bp-pdp-reviews .yotpo-review-star-rating .star-container, 8866 #bp-pdp-reviews .yotpo-bottom-line-stars .star-container { 8867 width: 22px !important; 8868 height: 21px !important; 8869 flex: 0 0 22px !important; 8870 margin: 0 !important; 8871 } 8872 #bp-pdp-reviews .yotpo-review-star-rating .star-container:not(:last-child), 8873 #bp-pdp-reviews .yotpo-bottom-line-stars .star-container:not(:last-child) { 8874 margin-right: 4px !important; 8875 } 8876 #bp-pdp-reviews .yotpo-review-star-rating .star-container > svg, 8877 #bp-pdp-reviews .yotpo-review-star-rating svg.yotpo-star-rating-icon, 8878 #bp-pdp-reviews .yotpo-review-star-rating svg[class*="yotpo-sr-star"], 8879 #bp-pdp-reviews .yotpo-bottom-line-stars .star-container > svg, 8880 #bp-pdp-reviews .yotpo-bottom-line-stars svg.yotpo-star-rating-icon, 8881 #bp-pdp-reviews .yotpo-bottom-line-stars svg[class*="yotpo-sr-star"] { 8882 width: 22px !important; 8883 height: 21px !important; 8884 } 8885 8886 /* --- Reviews header cleanup (BP) --- 8887 Yotpo's default summary header ships a vertical divider bar between 8888 the score and the "See reviews summary" AI button. The divider reads 8889 as a stray line in the BP design, so hide it â but the AI summary 8890 button IS shown (skinned below). Scoped to #bp-pdp-reviews. */
8891 #bp-pdp-reviews .yotpo-vertical-border { display: none !important; } 8892 /* Yotpo also draws a divider as a :after pseudo on the header .yotpo-bottom-line 8893 (a 2px #CCCCCC bar). Hide it too â no divider in the BP design. The #id scope 8894 out-specifies Yotpo's long class chain; content:none also drops the pseudo. */ 8895 #bp-pdp-reviews .yotpo-bottom-line::after { 8896 content: none !important; 8897 display: none !important; 8898 } 8899 8900 /* "See reviews summary" AI button â shown, and Yotpo's dark fill pill 8901 re-skinned with BP tokens so it reads on-brand in the reviews header. */ 8902 #bp-pdp-reviews .yotpo-reviews-summary-button #yotpo-summary, 8903 #bp-pdp-reviews #yotpo-summary.yotpo-icon-btn { 8904 display: inline-flex !important; 8905 align-items: center !important; 8906 gap: 8px !important; 8907 background: var(--bp-color-text, #171609) !important; 8908 color: var(--bp-color-cta-text, #FDFFFA) !important; 8909 border: 0 !important; 8910 border-radius: 0 !important; 8911 padding: 10px 16px !important; 8912 font-family: var(--bp-font-body) !important; 8913 font-size: 14px !important; 8914 font-weight: 500 !important; 8915 line-height: 1 !important; 8916 text-transform: none !important; 8917 cursor: pointer !important; 8918 } 8919 #bp-pdp-reviews #yotpo-summary .yotpo-button-icon path { 8920 fill: var(--bp-color-cta-text, #FDFFFA) !important; 8921 } 8922 8923 /* Reviews-summary drawer. Yotpo portals #yotpo-summary-container to <body>, 8924 so these rules are GLOBAL (not scoped to #bp-pdp-reviews). Present it as a 8925 right-side, full-height flyout matching the BP cart drawer 8926 (bp-cart-drawer.liquid): pinned right, 1/3 viewport (min 480px) on desktop, 8927 full width on mobile, solid page-bg surface, above the sticky ATC, with the 8928 same slide-in. Internal header/body/footer stack in a column and the body 8929 scrolls within the fixed height. */ 8930 #yotpo-summary-container, 8931 .yotpo-reviews-summary-drawer-container { 8932 position: fixed !important; 8933 inset: 0 0 0 auto !important; /* pin to the right edge, full height */ 8934 width: calc(100vw / 3) !important; 8935 min-width: 480px !important; 8936 max-width: 100vw !important; 8937 height: 100dvh !important; 8938 max-height: 100dvh !important; 8939 margin: 0 !important; 8940 border-radius: 0 !important; 8941 display: flex !important; 8942 flex-direction: column !important; 8943 background: var(--bp-color-bg-page, #FEFFFA) !important; 8944 color: var(--bp-color-text, #171609) !important; 8945 font-family: var(--bp-font-body) !important; 8946 box-shadow: -8px 0 40px rgba(23, 22, 9, 0.18) !important; 8947 z-index: 9999 !important; 8948 overflow: hidden !important; /* the body scrolls, not the drawer */ 8949 animation: bpDrawerSlideIn 0.25s cubic-bezier(0.4, 0, 0.2, 1) forwards; 8950 } 8951 @media (max-width: 749px) { 8952 #yotpo-summary-container, 8953 .yotpo-reviews-summary-drawer-container { width: 100vw !important; min-width: 0 !important; } 8954 } 8955 /* Close (X) floats top-right over the header. */ 8956 #yotpo-summary-container .yotpo-summary-close, 8957 .yotpo-reviews-summary-drawer-container .yotpo-summary-close { 8958 position: absolute !important; 8959 top: 24px !important; 8960 right: 24px !important; 8961 width: 16px !important; 8962 height: 16px !important; 8963 color: var(--bp-color-text, #171609) !important; 8964 cursor: pointer !important; 8965 z-index: 1 !important; 8966 } 8967 /* Header + footer hold their size; the body takes the rest and scrolls. */ 8968 #yotpo-summary-container .yotpo-summary-header, 8969 #yotpo-summary-container .yotpo-summary-footer-container { flex: 0 0 auto !important; } 8970 #yotpo-summary-container .yotpo-summary-body { 8971 flex: 1 1 auto !important; 8972 min-height: 0 !important; 8973 overflow-y: auto !important; 8974 } 8975 /* Local copy of the cart drawer's slide-in so this doesn't depend on 8976 bp-cart-drawer's keyframes being present (identical name/values). */ 8977 @keyframes bpDrawerSlideIn { 8978 from { transform: translateX(100%); } 8979 to { transform: translateX(0); } 8980 } 8981 /* Topic sentiment icons default to Yotpo pink/green â recolor to the BP 8982 black theme. !important beats the SVG's inline fill presentation attr. */ 8983 #yotpo-summary-container .yotpo-summary-body .yotpo-review-summary .yotpo-summary-sentiment svg path { 8984 fill: var(--bp-color-text, #171609) !important; 8985 } 8986 8987 /* Full + (any non-explicit) star: solid gold filled star.
8988 Yotpo ships TWO markup shapes depending on which widget renders the 8989 stars (DEV-1587): 8990 - "vanilla" widget: class lives on the <svg> itself 8991 (<div class="star-container"><svg class="⦠yotpo-sr-star-half">). 8992 - Vue-rendered widget (has a trailing <!--v-if--> comment): class 8993 lives on .star-container and the <svg> carries NO class at all 8994 (<div class="star-container yotpo-sr-star-half"><svg (no class)>). 8995 A catch-all that excludes on only ONE of the two locations is wrong 8996 for the other shape â excluding on .star-container alone is always 8997 vacuously true for the svg-classed shape (container never has the 8998 class), and excluding on the svg alone is always vacuously true for 8999 the Vue shape (svg never has a class to exclude on). Either way the 9000 "always true" :not() lets this rule's higher specificity beat the 9001 dedicated empty/half rules below and paint every star gold. Exclude 9002 on BOTH the container's and the svg's own class so a real empty/half 9003 star fails the match under either shape. */ 9004 svg.yotpo-sr-star-full, 9005 svg.yotpo-star-rating-icon:not(.yotpo-sr-star-empty):not(.yotpo-sr-star-half), 9006 .star-container.yotpo-sr-star-full > svg, 9007 .star-container:not(.yotpo-sr-star-empty):not(.yotpo-sr-star-half) > svg:not(.yotpo-sr-star-empty):not(.yotpo-sr-star-half) { 9008 background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='22' height='21' viewBox='0 0 22 21' fill='none'><path d='M10.7344 0L12.881 8.04547L21.196 7.60081L14.2076 12.1285L17.2
900819.8992L10.7344 14.652L4.26874 19.8992L7.26112 12.1285L0.272754 7.60081L8.58778 8.04547L10.7344 0Z' fill='%23EBAE55' stroke='%23EBAE55' stroke-width='1' stroke-linejoin='bevel'/></svg>"); 9009 } 9010 /* Empty star: gold outline only. */ 9011 svg.yotpo-sr-star-empty, 9012 .star-container.yotpo-sr-star-empty > svg { 9013 background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='22' height='21' viewBox='0 0 22 21' fill='none'><path d='M10.7344 0L12.881 8.04547L21.196 7.60081L14.2076 12.1285L17.2 19.8992L10.7344 14.652L4.26874 19.8992L7.26112 12.1285L0.272754 7.60081L8.58778 8.04547L10.7344 0Z' fill='none' stroke='%23EBAE55' stroke-linejoin='bevel' stroke-width='1'/></svg>"); 9014 } 9015 /* Half star: gold-filled left half, gold outline right half. */ 9016 svg.yotpo-sr-star-half, 9017 .star-container.yotpo-sr-star-half > svg { 9018 background-image: url("data:image/svg+xml;utf8,<svg xmlns='http://www.w3.org/2000/svg' width='22' height='21' viewBox='0 0 22 21' fill='none'><defs><linearGradient id='g' x1='0' y1='0' x2='1' y2='0'><stop offset='50%25' stop-color='%23EBAE55'/><stop offset='50%25' stop-color='transparent'/></linearGradient></defs><path d='M10.7344 0L12.881 8.04547L21.196 7.60081L14.2076 12.1285L17.2
901819.8992L10.7344 14.652L4.26874 19.8992L7.26112 12.1285L0.272754 7.60081L8.58778 8.04547L10.7344 0Z' fill='url(%23g)' stroke='%23EBAE55' stroke-linejoin='bevel' stroke-width='1'/></svg>"); 9019 } 9020 9021 /* Bottom-line star row â Yotpo renders: 9022 .yotpo-sr-bottom-line-summary (inline-style column, center) 9023 > <div> (inline-style row, center) 9024 .star-container à 5 9025 .yotpo-sr-bottom-line-right-panel 9026 .yotpo-sr-bottom-line-text--right-panel "82 Reviews" 9027 Yotpo's inline styles default to column-stacked and Futura 9028 typography. Override both with !important so the row reads 9029 "â â â â â 82 Reviews" left-aligned with BP typography. */ 9030 .product-information .bp-yotpo-stars, 9031 .product-information .bp-yotpo-stars > *, 9032 .bp-yotpo-stars .yotpo-sr-bottom-line-summary, 9033 .bp-yotpo-stars .yotpo-sr-bottom-line-summary > div, 9034 .bp-yotpo-stars .yotpo-sr-bottom-line-right-panel { 9035 display: flex !important; 9036 flex-direction: row !important; 9037 align-items: center !important; 9038 justify-content: flex-start !important; 9039 gap: 8px !important; 9040 flex-wrap: nowrap !important; 9041 margin: 0 !important; 9042 } 9043 .bp-yotpo-stars .yotpo-star-rating, 9044 .bp-yotpo-stars .yotpo-sr-bottom-line, 9045 .bp-yotpo-stars .yotpo-sr-bottom-line-summary > div { 9046 display: inline-flex !important; 9047 align-items: center !important; 9048 gap: 4px !important; 9049 } 9050 /* Top-of-PDP summary stars are 16px (smaller than the reviews-widget 9051 ratings, which are 22px) â per QA. Same treatment as the others: 9052 gold glyph (from the global mask rules) + 4px inter-star spacing via 9053 margin. Was 22px with margin-right:0, so the stars were oversized 9054 and touching. */ 9055 .bp-yotpo-stars .star-container { 9056 width: 16px !important; 9057 height: 16px !important; 9058 margin: 0 !important; 9059 flex: 0 0 16px !important; 9060 } 9061 .bp-yotpo-stars .star-container:not(:last-child) { 9062 margin-right: 4px !important; 9063 } 9064 .bp-yotpo-stars svg.yotpo-star-rating-icon, 9065 .bp-yotpo-stars svg[class*="yotpo-sr-star"], 9066 .bp-yotpo-stars .star-container > svg { 9067 width: 16px !important; 9068 height: 16px !important; 9069 } 9070 /* The "82 Reviews" text â Yotpo applies inline 9071 `font-family: Futura-PT-Regular; font-size: 16px; color: rgb(0,0,0)` 9072 directly on the element. !important on external CSS still wins 9073 against inline styles that aren't themselves marked !important. */ 9074 .bp-yotpo-stars .yotpo-sr-bottom-line-text, 9075 .bp-yotpo-stars .yotpo-sr-bottom-line-text--right-panel, 9076 .bp-yotpo-stars .yotpo-sr-bottom-line-right-panel [class*="bottom-line-text"] { 9077 color: var(--bp-color-body-copy, #74736B) !important; 9078 font-family: var(--bp-font-body), 'Messina Sans VF', system-ui, sans-serif !important; 9079 font-size: 12px !important; 9080 font-style: normal !important; 9081 font-weight: 500 !important; 9082 line-height: 145% !important; 9083 letter-spacing: -0.12px !important; 9084 text-transform: none !important; 9085 white-space: nowrap !important; 9086 margin: 0 !important; 9087 } 9088 9089 /* ================================================================= 9090 Yotpo review filter row â laid out as a single flex grid of 9 9091 chips (Search / Rating / Media / Age / Usual Size / Size Purchased 9092 / Occasion / Fit / Sort by). Yotpo nests them under three sibling 9093 panels (.yotpo-filters-top-panel, .yotpo-custom-questions-filters, 9094 .yotpo-filters-right-panel), so we flatten those intermediate 9095 wrappers with display: contents and let the flex parent place the 9096 items as if they were direct children. 9097 9098 Each chip is flex: 0 1 calc(25% - gap), min-width 220px â so they 9099 show 4-up on desktop, 3-up on narrower, 2-up on tablet, 1-up on 9100 mobile, all automatically via flex-wrap. 9101 9102 Per Figma: #74736B / Messina Sans VF / 14px / 500 / 145% / -0.14px. 9103 Each chip is just a bottom hairline; no box border, no fill. 9104 ================================================================= */ 9105 9106 /* Outer container â wrapping flex. Flatten intermediate panels so 9107 filters become direct flex children. */ 9108 .yotpo-filters-container, 9109 #yotpo-reviews-container .yotpo-filters-container { 9110 display: flex !important; 9111 flex-wrap: wrap !important;
9112 align-items: stretch !important; 9113 gap: 16px 24px !important; 9114 padding: 24px 0 !important; 9115 margin: 0 !important; 9116 background: transparent !important; 9117 border: 0 !important; 9118 box-shadow: none !important; 9119 } 9120 .yotpo-filters-container-inner, 9121 .yotpo-filters-top-panel, 9122 .yotpo-custom-questions-filters, 9123 .yotpo-custom-questions-filters-wrapper, 9124 .yotpo-filters-right-panel, 9125 #yotpo-reviews-container .yotpo-filters-container-inner, 9126 #yotpo-reviews-container .yotpo-filters-top-panel, 9127 #yotpo-reviews-container .yotpo-custom-questions-filters, 9128 #yotpo-reviews-container .yotpo-custom-questions-filters-wrapper, 9129 #yotpo-reviews-container .yotpo-filters-right-panel { 9130 display: contents !important; 9131 } 9132 9133 /* Each chip â 25% width minus gap, min 220px, flat hairline only. */ 9134 .yotpo-search-filter, 9135 .yotpo-score-filter, 9136 .yotpo-media-filter, 9137 .yotpo-custom-questions-filter, 9138 .yotpo-sorting-filter-container, 9139 #yotpo-reviews-container .yotpo-search-filter, 9140 #yotpo-reviews-container .yotpo-score-filter, 9141 #yotpo-reviews-container .yotpo-media-filter, 9142 #yotpo-reviews-container .yotpo-custom-questions-filter, 9143 #yotpo-reviews-container .yotpo-sorting-filter-container { 9144 flex: 0 1 calc(25% - 18px) !important; 9145 min-width: 220px !important; 9146 max-width: 100% !important; 9147 margin: 0 !important; 9148 padding: 0 !important; 9149 background: transparent !important; 9150 border: 0 !important; 9151 border-bottom: 1px solid rgba(23, 22, 9, 0.16) !important; 9152 border-radius: 0 !important; 9153 box-shadow: none !important; 9154 box-sizing: border-box !important; 9155 } 9156 9157 /* The inner clickable element (label / button) â flexbox row that 9158 pins the visible label on the left and the chevron / icon on the 9159 right. Strip its own borders / bg / shadow. */ 9160 .yotpo-search-filter .yotpo-search-label, 9161 .yotpo-score-filter > .yotpo-dropdown-base, 9162 .yotpo-media-filter .yotpo-media-select, 9163 .yotpo-custom-questions-filter .yotpo-dropdown-closable, 9164 .yotpo-sorting-filter-container .yotpo-dropdown-closable { 9165 display: flex !important; 9166 align-items: center !important; 9167 justify-content: space-between !important; 9168 gap: 8px !important; 9169 width: 100% !important; 9170 min-height: 40px !important; 9171 padding: 6px 4px !important; 9172 margin: 0 !important; 9173 background: transparent !important; 9174 border: 0 !important; 9175 border-radius: 0 !important; 9176 box-shadow: none !important; 9177 text-align: left !important; 9178 cursor: pointer !important; 9179 position: relative !important; 9180 } 9181 /* The label inside Search + Sort sits flush-left of its content. */ 9182 .yotpo-search-filter .yotpo-search-label, 9183 .yotpo-sorting-filter-container .yotpo-dropdown-closable { 9184 justify-content: flex-start !important; 9185 } 9186 9187 /* Yotpo uses placeholder <input> elements inside each dropdown chip 9188 as the clickâfocus target. Previous attempts to hide them broke 9189 Yotpo's click-handler chain (display: none kills labelâinput 9190 forwarding; position: absolute over the chip covered the button). 9191 9192 Final approach: leave the input's layout entirely to Yotpo and 9193 only override its appearance so it blends with the BP styling. 9194 The input's white background, default size, and event wiring 9195 stay intact â only the text/border become invisible. */ 9196 .yotpo-score-filter .yotpo-filter-select, 9197 .yotpo-custom-questions-filter .yotpo-dropdown-placeholder-input, 9198 .yotpo-sorting-filter-container input[readonly] { 9199 background: transparent !important; 9200 background-color: transparent !important; 9201 color: transparent !important; 9202 caret-color: transparent !important; 9203 border: 0 !important; 9204 outline: 0 !important; 9205 box-shadow: none !important; 9206 } 9207 9208 /* Reorder filters so the row layout matches the Figma: 9209 Row 1: Age, Usual Size, Size Purchased, Fit (custom) 9210 Row 2: Search, Rating, With media, Sort by (top + sort) 9211 Yotpo doesn't expose a stable class per custom-question filter, so 9212 we use :has(.yotpo-selected-value[title="â¦"]) to pick each one by 9213 its label. Occasion (not in the comp) falls to the end. */ 9214 .yotpo-custom-questions-filter:has(.yotpo-selected-value[title="Age"]) { order: 1 !important; } 9215 .yotpo-custom-questions-filter:has(.yotpo-selected-value[title="Usual Size"]) { order: 2 !important; } 9216 .yotpo-custom-questions-filter:has(.yotpo-selected-value[title="Size Purchased"]) { order: 3 !important; } 9217 .yotpo-custom-questions-filter:has(.yotpo-selected-value[title="Fit"]) { order: 4 !important; } 9218 .yotpo-search-filter { order: 5 !important; } 9219 .yotpo-score-filter { order: 6 !important; } 9220 .yotpo-media-filter { order: 7 !important; } 9221 .yotpo-sorting-filter-container { order: 8 !important; } 9222 /* Anything not explicitly ordered (e.g. Occasion) sinks below. */ 9223 .yotpo-custom-questions-filter { order: 9 !important; } 9224 .yotpo-custom-questions-filter:has(.yotpo-selected-value[title="Age"]), 9225 .yotpo-custom-questions-filter:has(.yotpo-selected-value[title="Usual Size"]), 9226 .yotpo-custom-questions-filter:has(.yotpo-selected-value[title="Size Purchased"]), 9227 .yotpo-custom-questions-filter:has(.yotpo-selected-value[title="Fit"]) {
9228 /* re-apply specific orders to defeat the catch-all above */ 9229 } 9230 9231 /* BP typography on every visible label. */ 9232 .yotpo-search-filter input.yotpo-search-input, 9233 .yotpo-score-filter .yotpo-selected-value, 9234 .yotpo-media-filter .yotpo-selected-value, 9235 .yotpo-custom-questions-filter .yotpo-selected-value, 9236 .yotpo-sorting-filter-container .yotpo-dropdown-placeholder, 9237 .yotpo-sorting-filter-container .yotpo-selected-value { 9238 color: var(--bp-color-body-copy, #74736B) !important; 9239 font-family: var(--bp-font-body, "Messina Sans VF"), system-ui, sans-serif !important; 9240 font-size: 14px !important; 9241 font-style: normal !important; 9242 font-weight: 500 !important; 9243 line-height: 145% !important; 9244 letter-spacing: -0.14px !important; 9245 background: transparent !important; 9246 border: 0 !important; 9247 padding: 0 !important; 9248 margin: 0 !important; 9249 flex: 1 1 auto !important; 9250 text-align: left !important; 9251 white-space: nowrap !important; 9252 overflow: hidden !important; 9253 text-overflow: ellipsis !important; 9254 } 9255 9256 /* Search input internals â magnifier on left as a static flex item, 9257 input fills the rest. Yotpo's own CSS positions the SVG absolutely 9258 against the label which causes the placeholder to render under it; 9259 force position: static on both so flex governs layout. */ 9260 .yotpo-search-filter .yotpo-search-label { 9261 position: relative !important; 9262 padding: 6px 4px 6px 8px !important; 9263 } 9264 .yotpo-search-filter .yotpo-search-icon { 9265 position: static !important; 9266 flex: 0 0 16px !important; 9267 width: 16px !important; 9268 height: 16px !important; 9269 margin: 0 !important; 9270 transform: none !important; 9271 inset: auto !important; 9272 } 9273 .yotpo-search-filter .yotpo-search-icon path { 9274 fill: var(--bp-color-body-copy, #74736B) !important; 9275 } 9276 .yotpo-search-filter input.yotpo-search-input { 9277 position: static !important; 9278 flex: 1 1 0 !important; 9279 min-width: 0 !important; 9280 width: auto !important; 9281 padding: 0 0 0 8px !important; 9282 margin: 0 !important; 9283 min-height: 40px !important; 9284 border: 0 !important; 9285 background: transparent !important; 9286 resize: none !important; 9287 outline: none !important; 9288 box-shadow: none !important; 9289 } 9290 .yotpo-search-filter input.yotpo-search-input::placeholder { 9291 color: var(--bp-color-body-copy, #74736B) !important; 9292 font-family: var(--bp-font-body, "Messina Sans VF"), system-ui, sans-serif !important; 9293 font-size: 14px !important; 9294 font-weight: 500 !important; 9295 letter-spacing: -0.14px !important; 9296 opacity: 1 !important; 9297 } 9298 9299 /* Media filter's circle icon and order â put it on the right. */ 9300 .yotpo-media-filter .yotpo-radio-icon { 9301 flex: 0 0 auto !important; 9302 width: 16px !important; 9303 height: 16px !important; 9304 } 9305 .yotpo-media-filter .yotpo-radio-icon circle { 9306 stroke: var(--bp-color-body-copy, #74736B) !important; 9307 } 9308 9309 /* Score (Rating) chip â chevron right of label; if a rating is 9310 applied, the visible .yotpo-selected-score star row sits between 9311 the label and the chevron. The label container is already 9312 space-between, so the natural order works. */ 9313 .yotpo-score-filter .yotpo-selected-score { 9314 flex: 0 0 auto !important; 9315 margin: 0 0 0 8px !important; 9316 } 9317 9318 /* Chevron arrow â re-tint to BP body copy color. */ 9319 .yotpo-filters-container .yotpo-dropdown-arrow-icon path { 9320 stroke: var(--bp-color-body-copy, #74736B) !important; 9321 fill: none !important; 9322 } 9323 .yotpo-filters-container .yotpo-dropdown-arrow-icon { 9324 flex: 0 0 auto !important; 9325 width: 10px !important; 9326 height: 7px !important; 9327 } 9328 9329 /* Dropdown menus (open state) â match the chip typography + flat 9330 background, light hover state. */ 9331 .yotpo-filters-container .yotpo-dropdown-content, 9332 .yotpo-filters-container .yotpo-dropdown-content-wrapper { 9333 background: var(--bp-color-bg-page) !important; 9334 border: 1px solid rgba(23, 22, 9, 0.12) !important; 9335 border-radius: 0 !important; 9336 box-shadow: 0 8px 24px rgba(23, 22, 9, 0.08) !important; 9337 padding: 4px 0 !important; 9338 margin-top: 4px !important; 9339 } 9340 .yotpo-filters-container .yotpo-dropdown-option, 9341 .yotpo-filters-container .yotpo-score-option { 9342 color: var(--bp-color-text, #171609) !important; 9343 font-family: var(--bp-font-body, "Messina Sans VF"), system-ui, sans-serif !important; 9344 font-size: 14px !important; 9345 font-weight: 500 !important; 9346 line-height: 145% !important; 9347 letter-spacing: -0.14px !important; 9348 padding: 10px 16px !important; 9349 cursor: pointer !important; 9350 } 9351 .yotpo-filters-container .yotpo-dropdown-option .yotpo-selected-icon path, 9352 .yotpo-filters-container .yotpo-score-option .yotpo-selected-icon path { 9353 fill: var(--bp-color-text, #171609) !important; 9354 } 9355 9356 /* Tighten the filter row on narrow viewports. */ 9357 @media (max-width: 767px) { 9358 .yotpo-search-filter, 9359 .yotpo-score-filter, 9360 .yotpo-media-filter, 9361 .yotpo-custom-questions-filter, 9362 .yotpo-sorting-filter-container { 9363 flex: 0 1 calc(50% - 6px) !important; 9364 min-width: 0 !important; 9365 } 9366 .yotpo-filters-container, 9367 #yotpo-reviews-container .yotpo-filters-container { 9368 gap: 12px !important; 9369 } 9370 } 9371 9372 /* ================================================================= 9373 Yotpo review cards â individual review card layout + typography. 9374 Targets the 2-col card (reviewer left / content right) that Yotpo 9375 v3 renders for each review in the list. 9376 ================================================================= */ 9377 9378 /* Card row â 2-col grid. The card itself has no border anymore
9379 (Yotpo renders a separate .yotpo-review-border-smooth element 9380 between cards which we color below). */ 9381 .yotpo-review, 9382 .yotpo-reviews-main-widget .yotpo-review { 9383 display: grid !important; 9384 grid-template-columns: 200px 1fr !important; 9385 gap: 24px !important; 9386 border-top: 0 !important; 9387 border-top-width: 0 !important; 9388 border-bottom: none !important; 9389 padding: 24px 0 !important; 9390 background: transparent !important; 9391 } 9392 /* Separator that Yotpo paints between review cards â switch from 9393 the default #CCCCCC to BP's E1E0D4 hairline tone. */ 9394 .yotpo-review-border-smooth, 9395 .yotpo-review-border-smooth.top-margin, 9396 .yotpo-reviews-main-widget .yotpo-review-border-smooth, 9397 .yotpo-reviews-main-widget .yotpo-review-border-smooth.top-margin { 9398 background: #E1E0D4 !important; 9399 } 9400 @media (max-width: 767px) { 9401 .yotpo-review, 9402 .yotpo-reviews-main-widget .yotpo-review { 9403 grid-template-columns: 1fr !important; 9404 gap: 12px !important; 9405 } 9406 /* Mobile: the star row is inline-flex, so Yotpo's centered text-align 9407 on the rating/title wrapper centers the stars while the (block-level) 9408 title + body stay left â the stars end up out of line with the rest 9409 of the review. Force the star row (and its wrapper) hard-left so it 9410 aligns with the title, body, and size details. */ 9411 #bp-pdp-reviews .yotpo-review-rating-title { 9412 text-align: left !important; 9413 align-items: flex-start !important; 9414 } 9415 #bp-pdp-reviews .yotpo-review-star-rating { 9416 justify-content: flex-start !important; 9417 align-self: flex-start !important; 9418 margin-left: 0 !important; 9419 margin-right: auto !important; 9420 } 9421 } 9422 9423 /* ---- Left column: reviewer info ---- */ 9424 .yotpo-reviewer, 9425 .yotpo-review-reviewer { 9426 display: flex !important; 9427 flex-direction: column !important; 9428 align-items: flex-start !important; 9429 gap: 2px !important; 9430 font-family: var(--bp-font-body, "Messina Sans VF"), system-ui, sans-serif !important; 9431 } 9432 9433 /* Reviewer name â Body/Semi/Small */ 9434 .yotpo-reviewer-name, 9435 .yotpo-reviewer-full-name { 9436 font-family: var(--bp-font-body, "Messina Sans VF"), system-ui, sans-serif !important; 9437 font-size: 14px !important; 9438 font-weight: 700 !important; 9439 line-height: 145% !important; 9440 letter-spacing: -0.14px !important; 9441 color: var(--bp-color-text, #171609) !important; 9442 margin: 0 !important; 9443 } 9444 9445 /* Verified buyer badge â ONE element shown as a flex row (icon + text side by side). 9446 We target the badge CONTAINER (.yotpo-verified-buyer-badge) and the confirmed 9447 text class (.yotpo-reviewer-verified-buyer-text) as the same rule. 9448 The broad [class*="verified"] wildcard is intentionally removed â it caused 9449 multiple elements to receive display:flex and produced the double-check symptom. */ 9450 .yotpo-verified-buyer-badge, 9451 .yotpo-reviewer-verified-buyer, 9452 .yotpo-reviewer-verified-buyer-text { 9453 display: flex !important; 9454 flex-direction: row !important; 9455 align-items: center !important; 9456 gap: 4px !important; 9457 font-family: var(--bp-font-body, "Messina Sans VF"), system-ui, sans-serif !important; 9458 font-size: 12px !important; 9459 font-weight: 500 !important; 9460 line-height: 145% !important; 9461 letter-spacing: -0.12px !important; 9462 color: var(--bp-color-text, #171609) !important; 9463 margin: 2px 0 0 !important; 9464 } 9465 /* If Yotpo renders the text as a sibling of the badge container (double-rendering), 9466 hide the standalone text element â the badge container already shows both icon + text. */ 9467 .yotpo-verified-buyer-badge + .yotpo-reviewer-verified-buyer-text, 9468 .yotpo-reviewer-verified-buyer + .yotpo-reviewer-verified-buyer-text { 9469 display: none !important; 9470 } 9471 9472 /* Reviewer custom field rows â Size Purchased / Usual Size / Age / Location. 9473 Yotpo renders these when the widget's "show reviewer attributes" option is 9474 on (Yotpo Admin â Widget Builder). */ 9475 .yotpo-reviewer-tags, 9476 .yotpo-reviewer-custom-fields, 9477 .yotpo-reviewer [class*="tags"], 9478 .yotpo-reviewer [class*="custom-field"]:not([class*="label"]):not([class*="value"]) { 9479 display: flex !important; 9480 flex-direction: column !important; 9481 gap: 2px !important; 9482 margin-top: 10px !important; 9483 } 9484 .yotpo-reviewer-tag, 9485 .yotpo-reviewer-custom-field, 9486 .yotpo-reviewer [class*="-tag"]:not([class*="label"]):not([class*="value"]) { 9487 display: grid !important; 9488 grid-template-columns: auto 1fr !important; 9489 gap: 0 6px !important;
9490 align-items: baseline !important; 9491 font-family: var(--bp-font-body, "Messina Sans VF"), system-ui, sans-serif !important; 9492 line-height: 145% !important; 9493 } 9494 /* Field label: Size Purchased: / Usual Size: etc. â Body/Semi/Small */ 9495 .yotpo-reviewer-tag-label, 9496 .yotpo-reviewer-tag-title, 9497 .yotpo-reviewer-custom-field-label, 9498 .yotpo-reviewer-field-label, 9499 .yotpo-reviewer [class*="-label"] { 9500 font-size: 14px !important; 9501 font-weight: 700 !important; 9502 color: var(--bp-color-text, #171609) !important; 9503 letter-spacing: -0.14px !important; 9504 white-space: nowrap !important; 9505 } 9506 /* Field value: S (4-8) / 55-64 / Tennessee etc. â Body/XSmall Book */ 9507 .yotpo-reviewer-tag-value, 9508 .yotpo-reviewer-tag-content, 9509 .yotpo-reviewer-custom-field-value, 9510 .yotpo-reviewer-field-value, 9511 .yotpo-reviewer [class*="-value"], 9512 .yotpo-reviewer [class*="-content"] { 9513 font-size: 12px !important; 9514 font-weight: 400 !important; 9515 color: var(--bp-color-body-copy, #74736B) !important; 9516 letter-spacing: -0.12px !important; 9517 } 9518 9519 /* ---- Shared 2-column grid for all reviewer question types ---- 9520 Collapses every question wrapper to display:contents so label and value 9521 spans land directly in this grid, aligning ALL value columns regardless 9522 of label width differences (e.g. "Size Purchased" vs "Location"). 9523 .bp-pdp-extras__reviews prefix gives specificity (0,2,0) which beats 9524 Yotpo's async-loaded single-class rules even if they carry !important. */ 9525 .bp-pdp-extras__reviews .yotpo-review-customer-questions { 9526 display: grid !important; 9527 grid-template-columns: max-content 1fr !important; 9528 column-gap: 8px !important; 9529 row-gap: 4px !important; 9530 align-items: baseline !important; 9531 } 9532 /* Key-value + free-text wrappers: flatten into the shared grid */ 9533 .bp-pdp-extras__reviews .yotpo-review-question-wrapper, 9534 .bp-pdp-extras__reviews .yotpo-review-key-value-question, 9535 .bp-pdp-extras__reviews .yotpo-review-free-text-question, 9536 .bp-pdp-extras__reviews .yotpo-custom-questions-wrapper, 9537 .bp-pdp-extras__reviews .yotpo-custom-questions-free-text-wrapper, 9538 .bp-pdp-extras__reviews .yotpo-custom-questions-free-text-question { 9539 display: contents !important; 9540 } 9541 9542 /* Range question: its outer container has inline display:flex so display:contents 9543 is unreliable there. Instead keep it as a single grid item spanning both columns, 9544 and make the wrapper a flex row so title + value appear side by side. */ 9545 .bp-pdp-extras__reviews .yotpo-custom-questions-range-question, 9546 .bp-pdp-extras__reviews .yotpo-review-range-question-top { 9547 grid-column: 1 / -1 !important; 9548 } 9549 .bp-pdp-extras__reviews .yotpo-custom-questions-range-question-wrapper { 9550 display: flex !important; 9551 flex-direction: row !important; 9552 align-items: baseline !important; 9553 gap: 8px !important; 9554 width: auto !important; 9555 margin-bottom: 0 !important; 9556 } 9557 /* Hide progress bar â only label + text value are shown */ 9558 .bp-pdp-extras__reviews .yotpo-progress-bar-wrapper { 9559 display: none !important; 9560 } 9561 /* See More link spans both columns so it sits full-width below the fields */ 9562 .bp-pdp-extras__reviews .yotpo-custom-questions-see-more-label { 9563 grid-column: 1 / -1 !important; 9564 } 9565 9566 /* ---- Label typography (all question types) ---- */ 9567 .yotpo-custom-questions-title, 9568 .yotpo-custom-questions-range-question-title, 9569 .yotpo-custom-questions-free-text-title { 9570 font-family: var(--bp-font-body, "Messina Sans VF"), system-ui, sans-serif !important; 9571 font-size: 12px !important; 9572 font-weight: 700 !important; 9573 line-height: 145% !important; 9574 letter-spacing: -0.12px !important; 9575 color: var(--bp-color-text, #171609) !important; 9576 white-space: nowrap !important; 9577 } 9578 9579 /* ---- Value typography (all question types) ---- */ 9580 .yotpo-custom-questions-value, 9581 .yotpo-custom-questions-range-question-value, 9582 .yotpo-custom-questions-free-text-value { 9583 font-family: var(--bp-font-body, "Messina Sans VF"), system-ui, sans-serif !important; 9584 font-size: 12px !important; 9585 font-weight: 500 !important; 9586 line-height: 145% !important; 9587 letter-spacing: -0.12px !important; 9588 color: var(--bp-color-body-copy, #74736B) !important; 9589 } 9590 9591 /* High-specificity overrides for all question types. 9592 Yotpo's CDN stylesheet is async-injected (appears later in cascade), so 9593 at equal class-count !important rules Yotpo wins on cascade order. 9594 Strategy: add the `html` element selector â specificity (0,0,N,1
9594) beats 9595 Yotpo's (0,0,N,0) regardless of source order. 9596 Free-text value needs one extra class because its DOM has two extra 9597 wrapper elements; key-value and range skip those. */ 9598 9599 /* Free-text value: 14 classes (0,14,0) â already beats Yotpo's 13. */ 9600 .bp-pdp-extras__reviews .yotpo-reviews-main-widget .yotpo-main-widget-layout .yotpo-main-layout .yotpo-reviews-container .yotpo-reviews-list .yotpo-review .yotpo-review-left-panel .yotpo-review-customer-questions-container .yotpo-review-customer-questions .yotpo-review-question-wrapper .yotpo-review-free-text-question .yotpo-custom-questions-free-text-question .yotpo-custom-questions-free-text-value { 9601 font-family: var(--bp-font-body, "Messina Sans VF"), system-ui, sans-serif !important; 9602 font-size: 12px !important; 9603 font-weight: 500 !important; 9604 line-height: 145% !important; 9605 letter-spacing: -0.12px !important; 9606 color: #74736B !important; 9607 } 9608 9609 /* Key-value + range labels: html + 12 classes = (0,0,12,1). 9610 Even if Yotpo uses 12 classes (0,0,12,0), the element selector tips the win. */ 9611 /* Labels â html + 13 classes = (0,0,13,1), beats Yotpo's (0,0,13,0). */ 9612 html .bp-pdp-extras__reviews .yotpo-reviews-main-widget .yotpo-main-widget-layout .yotpo-main-layout .yotpo-reviews-container .yotpo-reviews-list .yotpo-review .yotpo-review-left-panel .yotpo-review-customer-questions-container .yotpo-review-customer-questions .yotpo-review-question-wrapper .yotpo-custom-questions-wrapper .yotpo-custom-questions-title, 9613 html .bp-pdp-extras__reviews .yotpo-reviews-main-widget .yotpo-main-widget-layout .yotpo-main-layout .yotpo-reviews-container .yotpo-reviews-list .yotpo-review .yotpo-review-left-panel .yotpo-review-customer-questions-container .yotpo-review-customer-questions .yotpo-review-question-wrapper .yotpo-custom-questions-range-question .yotpo-custom-questions-range-question-wrapper .yotpo-custom-questions-range-question-title, 9614 html .bp-pdp-extras__reviews .yotpo-reviews-main-widget .yotpo-main-widget-layout .yotpo-main-layout .yotpo-reviews-container .yotpo-reviews-list .yotpo-review .yotpo-review-left-panel .yotpo-review-customer-questions-container .yotpo-review-customer-questions .yotpo-review-question-wrapper .yotpo-review-free-text-question .yotpo-custom-questions-free-text-question .yotpo-custom-questions-free-text-title { 9615 font-family: var(--bp-font-body, "Messina Sans VF"), system-ui, sans-serif !important; 9616 font-size: 12px !important; 9617 font-weight: 700 !important; 9618 line-height: 145% !important; 9619 letter-spacing: -0.12px !important; 9620 color: var(--bp-color-text, #171609) !important; 9621 white-space: nowrap !important; 9622 } 9623 9624 /* Values â same (0,0,13,1) or (0,0,14,1) approach. */ 9625 html .bp-pdp-extras__reviews .yotpo-reviews-main-widget .yotpo-main-widget-layout .yotpo-main-layout .yotpo-reviews-container .yotpo-reviews-list .yotpo-review .yotpo-review-left-panel .yotpo-review-customer-questions-container .yotpo-review-customer-questions .yotpo-review-question-wrapper .yotpo-custom-questions-wrapper .yotpo-custom-questions-value, 9626 html .bp-pdp-extras__reviews .yotpo-reviews-main-widget .yotpo-main-widget-layout .yotpo-main-layout .yotpo-reviews-container .yotpo-reviews-list .yotpo-review .yotpo-review-left-panel .yotpo-review-customer-questions-container .yotpo-review-customer-questions .yotpo-review-question-wrapper .yotpo-custom-questions-range-question .yotpo-custom-questions-range-question-wrapper .yotpo-custom-questions-range-question-value { 9627 font-family: var(--bp-font-body, "Messina Sans VF"), system-ui, sans-serif !important; 9628 font-size: 12px !important; 9629 font-weight: 500 !important; 9630 line-height: 145% !important; 9631 letter-spacing: -0.12px !important; 9632 color: #74736B !important; 9633 } 9634 9635 /* "See More" reviewer info link â CTA/CTA Small. 9636 Yotpo renders this as <label class="yotpo-custom-questions-see-more-label"> 9637 with an inline display:flex. text-decoration applies to flex elements 9638 so no display override needed â keep it as-is to preserve left alignment. */ 9639 html body .yotpo-custom-questions-see-more-label, 9640 .yotpo-see-more-reviewers-info, 9641 [class*="yotpo-see-more"], 9642 [class*="see-more-reviewers"] { 9643 font-family: var(--bp-font-body, "Messina Sans VF"), system-ui, sans-serif !important; 9644 font-size: 12px !important; 9645 font-weight: 500 !important; 9646 line-height: 145% !important; 9647 letter-spacing: -0.12px !important; 9648 color: var(--bp-color-text, #171609) !important; 9649 text-decoration: underline !important; 9650 text-decoration-line: underline !important; 9651 text-underline-offset: 2px !important; 9652 background: none !important; 9653 border: none !important; 9654 padding: 0 !important; 9655 margin-top: 10px !important; 9656 cursor: pointer !important; 9657 } 9658 9659 /* ---- Right column: review content ---- */ 9660 .yotpo-review-content { 9661 display: flex !important; 9662 flex-direction: column !important; 9663 gap: 0 !important; 9664 font-family: var(--bp-font-body, "Messina Sans VF"), system-ui, sans-serif !important; 9665 } 9666 9667 /* Header: stars + title + date on one row */ 9668 .yotpo-review-header, 9669 .yotpo-review-content .yotpo-review-header { 9670 display: flex !important;
9671 align-items: center !important; 9672 gap: 8px !important; 9673 flex-wrap: nowrap !important; 9674 margin-bottom: 8px !important; 9675 } 9676 /* Figma Body/Semi/Small spec: Messina 14 / 660 / 145% / -0.14 / BP black. */ 9677 .yotpo-review-title, 9678 .yotpo-review-header-title { 9679 font-family: var(--bp-font-body, "Messina Sans VF"), system-ui, sans-serif !important; 9680 font-size: 14px !important; 9681 font-style: normal !important; 9682 font-weight: 700 !important; 9683 line-height: 145% !important; 9684 letter-spacing: -0.14px !important; 9685 color: var(--bp-color-text, #171609) !important; 9686 flex: 1 1 auto !important; 9687 margin: 0 !important; 9688 } 9689 /* Yotpo's app injects a NINE-class selector (0,9,0 !important) forcing 9690 the review title to Futura PT 18/500. An all-class override ties on 9691 specificity and loses on load order (Yotpo injects late), so the 9692 `html`-prefixed mirror wasn't reliably winning. The reviews widget 9693 lives inside <section id="bp-pdp-reviews">, so anchor on that ID: 9694 one ID beats any all-class Yotpo selector (1,1,0 > 0,9,0) no matter 9695 the chain length or load order, and it survives Yotpo renaming an 9696 internal class. */ 9697 #bp-pdp-reviews .yotpo-review-title { 9698 font-family: var(--bp-font-body, "Messina Sans VF"), system-ui, sans-serif !important; 9699 font-size: 14px !important; 9700 font-style: normal !important; 9701 font-weight: 700 !important; 9702 line-height: 145% !important; 9703 letter-spacing: -0.14px !important; 9704 color: var(--bp-color-text, #171609) !important; 9705 margin: 0 !important; 9706 } 9707 .yotpo-review-date { 9708 font-family: var(--bp-font-body, "Messina Sans VF"), system-ui, sans-serif !important; 9709 font-size: 12px !important; 9710 font-weight: 400 !important; 9711 line-height: 145% !important; 9712 letter-spacing: -0.12px !important; 9713 color: var(--bp-color-text, #171609) !important; 9714 margin-left: auto !important; 9715 white-space: nowrap !important; 9716 flex-shrink: 0 !important; 9717 } 9718 9719 /* Review body text */ 9720 .yotpo-review-body, 9721 .yotpo-review-body-text, 9722 .yotpo-review-content [class*="review-body"] { 9723 font-family: var(--bp-font-body, "Messina Sans VF"), system-ui, sans-serif !important; 9724 font-size: 14px !important; 9725 font-weight: 400 !important; 9726 line-height: 145% !important; 9727 letter-spacing: -0.14px !important; 9728 color: var(--bp-color-text, #171609) !important; 9729 margin: 0 0 8px !important; 9730 } 9731 9732 /* Inline custom question answers in the review body, e.g. "Fit: True to size" */ 9733 .yotpo-review-custom-fields, 9734 .yotpo-review-custom-questions-answers, 9735 .yotpo-review-content [class*="custom-questions-answers"], 9736 .yotpo-review-content [class*="custom-fields"] { 9737 display: flex !important; 9738 flex-direction: column !important; 9739 gap: 2px !important; 9740 margin-bottom: 8px !important; 9741 } 9742 .yotpo-review-custom-field, 9743 .yotpo-review-content [class*="custom-field"]:not([class*="label"]):not([class*="value"]) { 9744 display: flex !important; 9745 gap: 4px !important; 9746 align-items: baseline !important; 9747 font-family: var(--bp-font-body, "Messina Sans VF"), system-ui, sans-serif !important; 9748 font-size: 14px !important; 9749 line-height: 145% !important; 9750 color: var(--bp-color-text, #171609) !important; 9751 } 9752 .yotpo-review-custom-field-label, 9753 .yotpo-review-content [class*="custom-field-label"], 9754 .yotpo-review-content [class*="field-label"] { 9755 font-weight: 600 !important; 9756 letter-spacing: -0.14px !important; 9757 } 9758 .yotpo-review-custom-field-value, 9759 .yotpo-review-content [class*="custom-field-value"], 9760 .yotpo-review-content [class*="field-value"] { 9761 font-weight: 400 !important; 9762 letter-spacing: -0.14px !important; 9763 } 9764 9765 /* Brand / store response block â the "Boston Proper" reply box */ 9766 .yotpo-brand-response, 9767 .yotpo-review-response, 9768 .yotpo-review-content [class*="brand-response"], 9769 .yotpo-review-content [class*="review-response"] { 9770 background: rgba(23, 22, 9, 0.04) !important; 9771 border: none !important; 9772 border-radius: 0 !important; 9773 padding: 16px !important; 9774 margin: 12px 0 !important; 9775 } 9776 .yotpo-brand-response-header, 9777 .yotpo-brand-response-name, 9778 [class*="brand-response"] [class*="header"], 9779 [class*="brand-response"] [class*="name"], 9780 [class*="review-response"] [class*="header"], 9781 [class*="review-response"] [class*="name"] { 9782 font-family: var(--bp-font-body, "Messina Sans VF"), system-ui, sans-serif !important; 9783 font-size: 14px !important; 9784 font-weight: 600 !important; 9785 line-height: 145% !important; 9786 letter-spacing: -0.14px !important; 9787 color: var(--bp-color-text, #171609) !important; 9788 margin: 0 0 8px !important; 9789 } 9790 .yotpo-brand-response-body, 9791 [class*="brand-response"] [class*="body"], 9792 [class*="review-response"] [class*="body"] { 9793 font-family: var(--bp-font-body, "Messina Sans VF"), system-ui, sans-serif !important; 9794 font-size: 14px !important; 9795 font-weight: 400 !important; 9796 line-height: 145% !important; 9797 letter-spacing: -0.14px !important; 9798 color: var(--bp-color-text, #171609) !important; 9799 margin: 0 !important; 9800 } 9801 9802 /* "Was this review helpful?" row â right-aligned, Body/XSmall Book */ 9803 .yotpo-helpful, 9804 .yotpo-helpful-question, 9805 .yotpo-review-content [class*="helpful"]:not([class*="icon"]):not(svg):not(path) { 9806 display: flex !important;
9807 align-items: center !important; 9808 gap: 8px !important; 9809 justify-content: flex-end !important; 9810 margin-top: 12px !important; 9811 font-family: var(--bp-font-body, "Messina Sans VF"), system-ui, sans-serif !important; 9812 font-size: 12px !important; 9813 font-weight: 400 !important; 9814 line-height: 145% !important; 9815 letter-spacing: -0.12px !important; 9816 color: var(--bp-color-text, #171609) !important; 9817 } 9818 9819 /* ================================================================= 9820 Yotpo v3 confirmed class names (from live widget HTML inspection). 9821 These selectors fill the gap between the generic patterns above 9822 and the actual class names Yotpo ships in the rendered DOM. 9823 ================================================================= */ 9824 9825 /* ---- Tabs row (.ugc-storefront-widgets-tabs-container__tab) ---- */ 9826 .ugc-storefront-widgets-tabs-container { 9827 display: flex !important; 9828 border-bottom: 1px solid rgba(23, 22, 9, 0.12) !important; 9829 margin-bottom: 0 !important; 9830 padding: 0 !important; 9831 background: transparent !important; 9832 } 9833 /* Tab: Body/Bold/Medium â inactive uses 40% opacity to indicate unselected state. 9834 Yotpo's injected rule uses 4 chained classes (0,4,0) + !important and hardcodes 9835 'Futura PT' directly (not via a variable, so the --primary-font-family fix doesn't 9836 reach it). Doubling the tab class pushes our specificity to (0,5,0) to win. */ 9837 .ugc-storefront-widgets-tabs-container__tab, 9838 .yotpo-widget-override-css .yotpo-main-widget-layout .ugc-storefront-widgets-tabs-container .ugc-storefront-widgets-tabs-container__tab.ugc-storefront-widgets-tabs-container__tab { 9839 font-family: var(--bp-font-body, "Messina Sans VF"), system-ui, sans-serif !important; 9840 font-size: 17px !important; 9841 font-weight: 775 !important; 9842 line-height: 145% !important; 9843 letter-spacing: -0.17px !important; 9844 color: var(--bp-color-text, #171609) !important; 9845 background: none !important; 9846 border: none !important; 9847 border-bottom: 2px solid transparent !important; 9848 padding: 0 0 12px !important; 9849 margin-right: 24px !important; 9850 margin-bottom: -1px !important; 9851 cursor: pointer !important; 9852 text-transform: none !important; 9853 } 9854 .ugc-storefront-widgets-tabs-container__tab.selected, 9855 .ugc-storefront-widgets-tabs-container__tab[aria-selected="true"] { 9856 color: var(--bp-color-text, #171609) !important; 9857 border-bottom-color: var(--bp-color-text, #171609) !important; 9858 } 9859 9860 /* ---- Summary row container: centered flex with score group + divider + CTA ---- */ 9861 /* Yotpo v3 uses several possible wrapper class names for the overall rating summary. 9862 Cover all common patterns so the row is centered regardless of exact DOM shape. */ 9863 /* NOTE the :not([class*="drawer"]) guard: the broad [class*="reviews-summary"] 9864 match also hit the AI reviews-summary DRAWER (.yotpo-reviews-summary-drawer- 9865 container), forcing its header/body/footer into a centered flex-wrap row and 9866 scattering the panel. Exclude the drawer so it keeps Yotpo's native column 9867 layout (styled separately above). */ 9868 .yotpo-main-widget-bottom-line, 9869 .yotpo-reviews-summary, 9870 .yotpo-bottom-line-wrapper, 9871 .yotpo-bottom-line-inner, 9872 [class*="main-widget-bottom-line"], 9873 [class*="reviews-summary"]:not([class*="drawer"]) { 9874 display: flex !important; 9875 align-items: center !important; 9876 justify-content: center !important; 9877 gap: 40px !important; 9878 padding: 24px 0 !important; 9879 background: transparent !important; 9880 flex-wrap: wrap !important; 9881 } 9882 9883 /* Left group: score number + star row + count â laid out as a row with gap */ 9884 .yotpo-bottom-line-left-panel, 9885 [class*="bottom-line-left-panel"] { 9886 display: flex !important; 9887 align-items: center !important; 9888 gap: 12px !important; 9889 flex-shrink: 0 !important; 9890 } 9891 9892 /* Big score number */ 9893 .yotpo-bottom-line-score, 9894 .yotpo-bottom-line-left-panel.yotpo-bottom-line-score { 9895 font-family: var(--bp-font-display, "Signifier"), "Times New Roman", serif !important; 9896 font-size: 56px !important; 9897 font-weight: 300 !important; 9898 line-height: 1 !important; 9899 color: var(--bp-color-text, #171609) !important; 9900 letter-spacing: -1px !important; 9901 } 9902 /* Stars + count stacked vertically inside the left group */ 9903 .yotpo-bottom-line-right-panel, 9904 [class*="bottom-line-right-panel"] { 9905 display: flex !important; 9906 flex-direction: column !important;
9907 align-items: flex-start !important; 9908 gap: 2px !important; 9909 } 9910 .yotpo-bottom-line-basic-text, 9911 .yotpo-bottom-line-text.yotpo-bottom-line-basic-text { 9912 font-family: var(--bp-font-body, "Messina Sans VF"), system-ui, sans-serif !important; 9913 font-size: 12px !important; 9914 font-weight: 500 !important; 9915 line-height: 145% !important; 9916 letter-spacing: -0.12px !important; 9917 color: var(--bp-color-text, #171609) !important; 9918 } 9919 9920 /* "Write a Review" primary CTA. 9921 The gap between the left group and the button is 40px (set on the 9922 container above). We don't use a border trick here because the button 9923 background would start AT the border, leaving no visible space. 9924 Instead, a ::before pseudo-element on the button acts as the divider. */ 9925 button#yotpo-main-widget-btn, 9926 .yotpo-new-review-btn { 9927 background: var(--bp-color-text, #171609) !important; 9928 color: var(--bp-color-cta-text, #FDFFFA) !important; 9929 font-family: var(--bp-font-body, "Messina Sans VF"), system-ui, sans-serif !important; 9930 font-size: 14px !important; 9931 font-weight: 500 !important; 9932 letter-spacing: -0.14px !important; 9933 padding: 14px 28px !important; 9934 border: none !important; 9935 border-radius: 0 !important; 9936 cursor: pointer !important; 9937 text-transform: none !important; 9938 transition: opacity 0.15s !important; 9939 flex-shrink: 0 !important; 9940 position: relative !important; 9941 } 9942 button#yotpo-main-widget-btn:hover, 9943 .yotpo-new-review-btn:hover { opacity: 0.85 !important; } 9944 9945 /* Vertical divider â rendered as a pseudo-element 20px to the left of the button */ 9946 button#yotpo-main-widget-btn::before, 9947 .yotpo-new-review-btn::before { 9948 content: '' !important; 9949 position: absolute !important; 9950 left: -20px !important; 9951 top: 50% !important; 9952 transform: translateY(-50%) !important; 9953 width: 1px !important; 9954 height: 52px !important; 9955 background: rgba(23, 22, 9, 0.2) !important; 9956 pointer-events: none !important; 9957 } 9958 /* Empty state (product has no reviews): Yotpo centers the "Write a Review" 9959 button with NO rating-summary group to its left, so the divider above is 9960 an orphan vertical line beside the button. Yotpo adds `.empty-state` to the 9961 button then, so drop the pseudo-divider in that case (QA: Brooke). */ 9962 button#yotpo-main-widget-btn.empty-state::before, 9963 .yotpo-new-review-btn.empty-state::before { 9964 content: none !important; 9965 display: none !important; 9966 } 9967 9968 /* ---- Review card: left panel (reviewer) / center+right panels (content) ---- */ 9969 .yotpo-review-left-panel { 9970 display: flex !important; 9971 flex-direction: column !important; 9972 align-items: flex-start !important; 9973 gap: 2px !important; 9974 } 9975 .yotpo-review-center-panel { 9976 display: flex !important; 9977 flex-direction: column !important; 9978 gap: 0 !important; 9979 } 9980 /* If Yotpo renders a 3rd panel (right-panel) as a sibling, keep it 9981 in the right column so it stacks below center-panel rather than 9982 wrapping back to column 1. */ 9983 .yotpo-review-right-panel { 9984 display: flex !important; 9985 flex-direction: column !important; 9986 gap: 0 !important; 9987 grid-column: 2 !important; 9988 } 9989 9990 /* ---- Review body: confirmed class from live HTML ---- */ 9991 .yotpo-read-more-text, 9992 .yotpo-review-wrapper .yotpo-read-more-text { 9993 font-family: var(--bp-font-body, "Messina Sans VF"), system-ui, sans-serif !important; 9994 font-size: 14px !important; 9995 font-weight: 400 !important; 9996 line-height: 145% !important; 9997 letter-spacing: -0.14px !important; 9998 color: var(--bp-color-text, #171609) !important; 9999 margin: 0 0 8px !important; 10000 } 10001 /* "Read more" expand link */ 10002 .yotpo-read-more-link, 10003 .yotpo-review-wrapper [class*="read-more"] a { 10004 font-family: var(--bp-font-body, "Messina Sans VF"), system-ui, sans-serif !important; 10005 font-size: 13px !important; 10006 font-weight: 500 !important; 10007 color: var(--bp-color-text, #171609) !important; 10008 text-decoration: underline !important; 10009 } 10010 10011 /* ---- Date: confirmed class .yotpo-date-format ---- */ 10012 .yotpo-date-format { 10013 font-family: var(--bp-font-body, "Messina Sans VF"), system-ui, sans-serif !important; 10014 font-size: 12px !important; 10015 font-weight: 400 !important; 10016 line-height: 145% !important; 10017 letter-spacing: -0.12px !important; 10018 color: var(--bp-color-text, #171609) !important; 10019 } 10020 10021 /* ---- Helpful votes: text + number ---- */ 10022 .yotpo-review-votes-text { 10023 font-family: var(--bp-font-body, "Messina Sans VF"), system-ui, sans-serif !important; 10024 font-size: 12px !important; 10025 font-weight: 400 !important; 10026 letter-spacing: -0.12px !important; 10027 color: var(--bp-color-text, #171609) !important; 10028 } 10029 .yotpo-review-votes-number { 10030 font-family: var(--bp-font-body, "Messina Sans VF"), system-ui, sans-serif !important; 10031 font-size: 12px !important; 10032 font-weight: 500 !important; 10033 letter-spacing: -0.12px !important; 10034 color: var(--bp-color-text, #171609) !important; 10035 } 10036 10037 /* ---- Reviews + Q&A empty states -------------------------------- 10038 Yotpo's injected rule targets .yotpo-title-text inside any 10039 [class*="-empty-state"] with var(--my-third-color) at (0,4,0). 10040 --my-third-color is undefined on our end so it renders invisible. 10041 Doubling .yotpo-title-text pushes our specificity to (0,5,0). */ 10042 .yotpo-qna-empty-state, 10043 [class*="-empty-state"] { 10044 font-family: var(--bp-font-body, "Messina Sans VF"), system-ui, sans-serif !important; 10045 font-size: 14px !important; 10046 font-weight: 400 !important; 10047 color: var(--bp-color-text, #171609) !important; 10048 text-align: center !important; 10049 padding: 32px 0 !important;
10050 align-items: center !important; 10051 } 10052 /* Center the empty-state ICON too. text-align:center only centers the icon 10053 when Yotpo renders the SVG inline (the "Tell Us Everything" variant). Other 10054 variants (e.g. "We're Looking For Stars") render the SVG as display:block / 10055 lay the container out as flex-start, so the icon left-aligns (QA: Brooke 10056 8/20). margin:auto centers it in both a block and a flex container; the 10057 align-items above is a harmless no-op unless Yotpo uses flex. */ 10058 .yotpo-qna-empty-state > svg, 10059 [class*="-empty-state"] > svg { 10060 display: block !important; 10061 margin-left: auto !important; 10062 margin-right: auto !important; 10063 } 10064 .yotpo-widget-override-css .yotpo-main-widget-layout [class*="yotpo-main-"] [class*="-empty-state"] .yotpo-title-text.yotpo-title-text, 10065 .yotpo-widget-override-css .yotpo-main-widget-layout [class*="yotpo-main-"] [class*="-empty-state"] .yotpo-body-text.yotpo-body-text { 10066 font-family: var(--bp-font-body, "Messina Sans VF"), system-ui, sans-serif !important; 10067 font-size: 14px !important; 10068 font-weight: 400 !important; 10069 color: var(--bp-color-text, #171609) !important; 10070 } 10071 button[class*="yotpo-ask-question-btn"] { 10072 background: var(--bp-color-text, #171609) !important; 10073 color: var(--bp-color-cta-text, #FDFFFA) !important; 10074 font-family: var(--bp-font-body, "Messina Sans VF"), system-ui, sans-serif !important; 10075 font-size: 14px !important; 10076 font-weight: 500 !important; 10077 letter-spacing: -0.14px !important; 10078 padding: 14px 28px !important; 10079 border: none !important; 10080 border-radius: 0 !important; 10081 cursor: pointer !important; 10082 text-transform: none !important; 10083 } 10084 button[class*="yotpo-ask-question-btn"] span { 10085 font-family: var(--bp-font-body, "Messina Sans VF"), system-ui, sans-serif !important; 10086 padding: 0 !important; 10087 } 10088 button[class*="yotpo-ask-question-btn"]:hover { opacity: 0.85 !important; } 10089 10090 /* ---- Broad font-family catch-all for Yotpo widget instances ------ 10091 Yotpo injects its own stylesheet (Futura PT / system defaults) and 10092 sometimes inline styles. This rule forces Messina Sans VF on every 10093 text node inside either widget wrapper. Specific overrides above 10094 (e.g. `.yotpo-bottom-line-score` â Signifier display face) still 10095 win because they have equal or higher specificity and appear later 10096 in the stylesheet. 10097 10098 Yotpo's injected CSS resolves font via its own --primary-font-family 10099 and --secondary-font-family variables. Redefining them on the widget 10100 root makes Yotpo's own !important rules use Messina Sans VF â no 10101 specificity fight needed. */ 10102 .yotpo-widget-instance, 10103 .bp-yotpo-reviews { 10104 --primary-font-family: 'Messina Sans VF', system-ui, sans-serif; 10105 --secondary-font-family: 'Messina Sans VF', system-ui, sans-serif; 10106 } 10107 .yotpo-widget-instance *, 10108 .bp-yotpo-reviews * { 10109 font-family: var(--bp-font-body, 'Messina Sans VF', system-ui, sans-serif) !important; 10110 } 10111 /* Re-pin the display-face elements that should stay as Signifier */ 10112 .yotpo-bottom-line-score, 10113 .yotpo-bottom-line-left-panel.yotpo-bottom-line-score { 10114 font-family: var(--bp-font-display, 'Signifier', 'Times New Roman', serif) !important; 10115 } 10116 10117 /* ---- "Let us know what you think" / write-review form heading ---- 10118 Yotpo v3 renders the write-review section header with several 10119 possible class names depending on review count (first-review state 10120 vs. existing reviews). Body/Bold/Medium spec from Figma. */ 10121 .yotpo-headline, 10122 .yotpo-write-review-header, 10123 .yotpo-write-review-title, 10124 .yotpo-first-review-headline, 10125 [class*="write-review"] [class*="title"], 10126 [class*="write-review"] [class*="header"], 10127 [class*="write-review"] [class*="headline"], 10128 [class*="first-review"] [class*="headline"], 10129 [class*="submission"] [class*="title"], 10130 .yotpo-modal-header-title, 10131 .yotpo-form-title { 10132 font-family: var(--bp-font-body, 'Messina Sans VF', system-ui, sans-serif) !important; 10133 font-size: 17px !important; 10134 font-weight: 775 !important; 10135 line-height: 145% !important; 10136 letter-spacing: -0.17px !important; 10137 color: var(--bp-color-text, #171609) !important; 10138 text-transform: none !important; 10139 } 10140 10141 /* ---- Alternate Yotpo tab selectors (v2-legacy + v3 modal tabs) --- 10142 `.ugc-storefront-widgets-tabs-container__tab` covers the main 10143 Reviews/Q&A tab bar. These additional selectors cover older nav 10144 patterns and the tab bar inside the write-review modal. */ 10145 .yotpo-nav-tab, 10146 .yotpo-nav .yotpo-nav-content, 10147 [class*="yotpo"][class*="-tab"]:not([class*="container"]) { 10148 font-family: var(--bp-font-body, 'Messina Sans VF', system-ui, sans-serif) !important; 10149 text-transform: none !important; 10150 } 10151</style>
10152<script> 10153 (function () { 10154 /* morph.js resets data-current-checked from server HTML after every async 10155 variant fetch, so we cannot rely on the onBeforeUpdate hook preserving it. 10156 Instead we store the user's selection on the variant-picker element itself 10157 â morph uses childrenOnly: true so the root element's JS properties are 10158 never touched â then re-enforce data-current-checked after every morph. */ 10159 10160 // On user click: record selection AND apply data-current-checked immediately 10161 // so the pill moves without waiting for the async fetch + morph cycle. 10162 document.addEventListener('change', function (e) { 10163 var input = e.target; 10164 if (!(input instanceof HTMLInputElement) || input.type !== 'radio') return; 10165 var picker = input.closest('variant-picker'); 10166 if (!picker) return; 10167 var fieldset = input.closest('fieldset'); 10168 if (!fieldset) return; 10169 var idx = fieldset.dataset.fieldsetIndex; 10170 if (idx === undefined || idx === '') return; 10171 10172 if (!picker._bpSel) picker._bpSel = {}; 10173 picker._bpSel[idx] = { 10174 id: input.dataset.optionValueId || '', 10175 text: input.value, 10176 }; 10177 10178 // Instant visual feedback â update data-current-checked now so the 10179 // selected button highlights immediately on click, before the fetch resolves. 10180 fieldset.querySelectorAll('input[type="radio"]').forEach(function (inp) { 10181 inp.dataset.currentChecked = inp === input ? 'true' : 'false'; 10182 }); 10183 10184 // Immediate legend update (before the fetch completes). 10185 var swatch = fieldset.querySelector('.variant-option__swatch-value'); 10186 if (swatch) swatch.textContent = input.value; 10187 }); 10188 10189 // After morph: re-apply data-current-checked and fix the legend. 10190 document.addEventListener('variant:update', function () { 10191 document.querySelectorAll('variant-picker').forEach(function (picker) { 10192 if (!picker._bpSel) return; 10193 Object.keys(picker._bpSel).forEach(function (idx) { 10194 var sel = picker._bpSel[idx]; 10195 var fieldset = picker.querySelector('fieldset[data-fieldset-index="' + idx + '"]'); 10196 if (!fieldset) return; 10197 10198 fieldset.querySelectorAll('input[type="radio"]').forEach(function (input) { 10199 input.dataset.currentChecked = input.dataset.optionValueId === sel.id ? 'true' : 'false'; 10200 }); 10201 10202 var swatch = fieldset.querySelector('.variant-option__swatch-value'); 10203 if (swatch) swatch.textContent = sel.text; 10204 }); 10205 }); 10206 }); 10207 10208 /* Toggle `[data-bp-sold-out]` on every <add-to-cart-component>. The CSS 10209 above uses that attribute to hide the ATC button â but the button may 10210 ONLY be hidden when the BP Notify Me block actually replaces it, i.e. 10211 the variant is RESTOCK-ELIGIBLE. Hiding it for every sold-out variant 10212 (as this did before) left the buy box EMPTY on a sold-out-but-not- 10213 eligible variant â e.g. one with inbound restock scheduled â because 10214 Notify Me doesn't render for it and its inline `display:''` can't beat 10215 this rule's `!important` (Ernie 7/22, both the "button disappears" and 10216 "Notify shows anytime OOS" tickets). 10217 10218 Restock-eligibility is read from the SAME per-variant table the Notify 10219 Me block embeds (blocks/bp-notify-me-button.liquid), so the two stay 10220 consistent by construction: eligible â hide button, Notify Me shows; 10221 not eligible â keep Horizon's disabled "Sold out" button. */ 10222 function bpNotifyTable() { 10223 var el = document.querySelector('[id^="bp-notify-variants-"]'); 10224 if (!el) return null; 10225 try { return JSON.parse(el.textContent || '{}'); } catch (_) { return null; } 10226 } 10227 function bpRestockEligible(vid) { 10228 var table = bpNotifyTable(); 10229 var entry = table && table[String(vid)]; 10230 if (!entry) return false; 10231 var available = entry.available !== false; 10232 var gates = entry.gates || {}; 10233 var currentInv = Number(gates.currentInv) || 0; 10234 var inboundInv = Number(gates.inboundInv) || 0; 10235 // Identical gate to bp-notify-me-button.liquid's restockEligible. 10236 return !available && !!entry.fullPrice && currentInv <= 0 && inboundInv <= 0; 10237 } 10238 function syncSoldOut(vid) { 10239 var eligible = bpRestockEligible(vid); 10240 document.querySelectorAll('.product-information add-to-cart-component').forEach(function (comp) { 10241 if (eligible) comp.setAttribute('data-bp-sold-out', ''); 10242 else comp.removeAttribute('data-bp-sold-out'); 10243 }); 10244 } 10245 /* Initial page load â gate on the current variant so a direct load of a 10246 sold-out-but-not-eligible variant keeps Horizon's disabled "Sold out" 10247 button instead of an empty box. */ 10248 (function initSoldOut() { 10249 var input = document.querySelector('.product-information input[name="id"]'); 10250 var vid = input && input.value; 10251 if (vid) syncSoldOut(vid); 10252 })(); 10253 document.addEventListener('variant:update', function (e) { 10254 var d = e && e.detail; 10255 var vid = d && ( 10256 (d.resource && d.resource.id) || 10257 (d.variant && d.variant.id) || 10258 d.id 10259 ); 10260 if (vid) syncSoldOut(vid); 10261 }); 10262 })(); 10263</script>
10263<script> 10264(function () { 10265 if (window.__bpPdpSwap) return; 10266 window.__bpPdpSwap = true; 10267 10268 var TRACKING_PREFIXES = [ 10269 'utm_', 'gclid', 'gad_', 'nb_', 'campaign', 'adgroup', 10270 'keyword', 'matchtype', 'network', 'device', 'source', 'gbraid' 10271 ]; 10272 10273 // Silent in production. Enable diagnostics (fetch/swap timings, cache 10274 // hits, prefetch) by setting window.bpPdpSwapDebug = true or visiting 10275 // with ?bp-pdp-swap-debug=1. 10276 if (typeof window.bpPdpSwapDebug === 'undefined') { 10277 window.bpPdpSwapDebug = false; 10278 try { 10279 if (new URLSearchParams(location.search).get('bp-pdp-swap-debug') === '1') { 10280 window.bpPdpSwapDebug = true; 10281 } 10282 } catch (e) {} 10283 } 10284 function log() { 10285 if (!window.bpPdpSwapDebug) return; 10286 var args = ['[bp-pdp-swap]'].concat(Array.prototype.slice.call(arguments)); 10287 console.log.apply(console, args); 10288 } 10289 10290 function preserveTracking(targetPath) { 10291 try { 10292 var src = new URL(window.location.href); 10293 var dst = new URL(targetPath, window.location.origin); 10294 src.searchParams.forEach(function (val, key) { 10295 var keep = TRACKING_PREFIXES.some(function (p) { return key.indexOf(p) === 0; }); 10296 if (keep && !dst.searchParams.has(key)) dst.searchParams.set(key, val); 10297 }); 10298 return dst.pathname + (dst.search || '') + (dst.hash || ''); 10299 } catch (e) { 10300 return targetPath; 10301 } 10302 } 10303 10304 function reexecuteScripts(root) { 10305 var scripts = root.querySelectorAll('script'); 10306 log('re-executing', scripts.length, 'scripts'); 10307 scripts.forEach(function (oldScript) { 10308 var s = document.createElement('script'); 10309 for (var i = 0; i < oldScript.attributes.length; i++) { 10310 var attr = oldScript.attributes[i]; 10311 s.setAttribute(attr.name, attr.value); 10312 } 10313 var hasSrc = oldScript.hasAttribute('src'); 10314 var typeAttr = (oldScript.getAttribute('type') || '').toLowerCase(); 10315 var isJsType = !typeAttr || typeAttr === 'text/javascript' || typeAttr === 'application/javascript'; 10316 if (!hasSrc && isJsType && oldScript.textContent) { 10317 // Wrap inline JS in an IIFE so top-level `const`/`let`/`class` 10318 // declarations are scoped to the wrapper. Without this, the 10319 // re-executed script collides with the original page-load 10320 // declarations and throws "Identifier 'X' has already been 10321 // declared" â which then aborts the rest of reexecuteScripts. 10322 // The try/catch keeps one bad script from breaking the others. 10323 s.text = '(function(){try{' + oldScript.textContent + '}catch(e){console.warn("[bp-pdp-swap] inline script error",e);}}).call(this);'; 10324 } else { 10325 s.text = oldScript.textContent; 10326 } 10327 try { 10328 oldScript.parentNode.replaceChild(s, oldScript); 10329 } catch (e) { 10330 console.warn('[bp-pdp-swap] script swap failed', e); 10331 } 10332 }); 10333 } 10334 10335 function updateMetaFromDoc(doc) { 10336 try { 10337 var newTitle = doc.querySelector('title'); 10338 if (newTitle) document.title = newTitle.textContent; 10339 ['description', 'og:title', 'og:description', 'og:image', 'og:url'].forEach(function (n) { 10340 var sel = n.indexOf('og:') === 0 ? 'meta[property="' + n + '"]' : 'meta[name="' + n + '"]'; 10341 var src = doc.querySelector(sel); 10342 var dst = document.querySelector(sel); 10343 if (src && dst) dst.setAttribute('content', src.getAttribute('content') || ''); 10344 }); 10345 var canonSrc = doc.querySelector('link[rel="canonical"]'); 10346 var canonDst = document.querySelector('link[rel="canonical"]'); 10347 if (canonSrc && canonDst) canonDst.setAttribute('href', canonSrc.getAttribute('href') || ''); 10348 } catch (e) { log('meta update failed', e); } 10349 } 10350 10351 // Snapshot the shopper's non-colour option selections (e.g. Size) 10352 // from the live Horizon variant picker, so a colour swap can 10353 // re-apply them on the freshly-swapped product. Keyed by the 10354 // option's display name (the legend prefix) so it survives the 10355 // section-suffixed input names. 10356 function captureVariantOptions() { 10357 var out = []; 10358 try { 10359 var scope = document.getElementById('MainContent') || document; 10360 scope.querySelectorAll('variant-picker input[type="radio"]:checked').forEach(function (input) { 10361 var fs = input.closest('fieldset'); 10362 var pfx = fs && fs.querySelector('.variant-option__swatch-prefix'); 10363 var name = pfx ? pfx.textContent.trim() : (input.name || ''); 10364 var lc = name.toLowerCase(); 10365 if (lc === 'color' || lc === 'colour') return; // colour changes with the swap 10366 out.push({ option: name, value: input.value }); 10367 }); 10368 } catch (e) { log('capture options failed', e); } 10369 return out; 10370 } 10371 10372 // Re-select the captured options on the swapped-in picker. Firing 10373 // `change` lets Horizon's variant-picker.js update price / ATC / 10374 // availability for the chosen size â no full reload. 10375 function restoreVariantOptions(root, saved) { 10376 if (!root || !saved || !saved.length) return; 10377 saved.forEach(function (sel) {
10378 try { 10379 var fieldsets = root.querySelectorAll('variant-picker fieldset.variant-option'); 10380 var target = null; 10381 for (var i = 0; i < fieldsets.length; i++) { 10382 var pfx = fieldsets[i].querySelector('.variant-option__swatch-prefix'); 10383 var name = pfx ? pfx.textContent.trim() : ''; 10384 if (name && sel.option && name.toLowerCase() === sel.option.toLowerCase()) { 10385 target = fieldsets[i]; 10386 break; 10387 } 10388 } 10389 if (!target) return; 10390 var radios = target.querySelectorAll('input[type="radio"]'); 10391 for (var j = 0; j < radios.length; j++) { 10392 if (radios[j].value === sel.value) { 10393 if (!radios[j].checked) { 10394 radios[j].checked = true; 10395 radios[j].dispatchEvent(new Event('input', { bubbles: true })); 10396 radios[j].dispatchEvent(new Event('change', { bubbles: true })); 10397 } 10398 break; 10399 } 10400 } 10401 } catch (e) { log('restore option failed', e); } 10402 }); 10403 } 10404 10405 function perfNow() { 10406 try { return (window.performance && performance.now) ? performance.now() : Date.now(); } 10407 catch (e) { return Date.now(); } 10408 } 10409 10410 // Primary gallery image of a parsed section/root â used to preload the 10411 // incoming colour's main image BEFORE the swap so it paints from cache 10412 // with no blank frame (the main cause of the colour-change flash). 10413 function firstGalleryImg(root) { 10414 if (!root) return ''; 10415 try { 10416 var img = root.querySelector('.bp-pdp-gallery__img, [data-bp-pdp-rail] img, .product-information__media img'); 10417 if (!img) return ''; 10418 return img.getAttribute('src') || (img.getAttribute('srcset') || '').trim().split(' ')[0] || ''; 10419 } catch (e) { return ''; } 10420 } 10421 function preloadImg(url) { 10422 return new Promise(function (resolve) { 10423 if (!url) return resolve(); 10424 var im = new Image(); 10425 var done = false; 10426 var go = function () { if (done) return; done = true; resolve(); }; 10427 im.onload = go; 10428 im.onerror = go; 10429 im.src = url; 10430 if (im.decode) { try { im.decode().then(go).catch(go); } catch (e) {} } 10431 setTimeout(go, 400); // never block the swap on a slow image 10432 }); 10433 } 10434 10435 // The product buy box + gallery live in the JSON-template `main` 10436 // section (type: product-information). Learn its wrapper id once so a 10437 // colour swap fetches JUST that section (?section_id=template--N__main) 10438 // instead of the whole document â a fraction of the bytes + server 10439 // render. Every colourway shares templates/product.json, so the id is 10440 // stable across the family and applies to the sibling URL too. 10441 function getMainSectionId() { 10442 if (window.__bpPdpMainSectionId) return window.__bpPdpMainSectionId; 10443 try { 10444 var stored = sessionStorage.getItem('bpPdpMainSectionId'); 10445 if (stored) { window.__bpPdpMainSectionId = stored; return stored; } 10446 } catch (e) {} 10447 var main = document.getElementById('MainContent'); 10448 if (!main) return null; 10449 var wrap = null; 10450 try { wrap = main.querySelector('[id^="shopify-section-"]:has(.product-information)'); } 10451 catch (e) { wrap = null; } // :has() unsupported â fall through to scan 10452 if (!wrap) { 10453 var all = main.querySelectorAll('[id^="shopify-section-"]'); 10454 for (var i = 0; i < all.length; i++) { 10455 if (all[i].querySelector('.product-information')) { wrap = all[i]; break; } 10456 } 10457 } 10458 if (!wrap) return null; 10459 var id = wrap.id.replace(/^shopify-section-/, ''); 10460 window.__bpPdpMainSectionId = id; 10461 try { sessionStorage.setItem('bpPdpMainSectionId', id); } catch (e) {} 10462 return id; 10463 } 10464 10465 // Section-scoped fetch URL â also the cache key. Tracking params ride 10466 // the pushState (display) URL, NOT the fetch, so hover-prefetch and 10467 // click resolve to the exact same key (the old cross-surface prefetch 10468 // bug was a key mismatch â do not repeat it). 10469 function sectionFetchUrl(pathOnly, sectionId) { 10470 return pathOnly + '?section_id=' + encodeURIComponent(sectionId); 10471 } 10472 10473 // In-memory cache of section-scoped sibling HTML, keyed by fetch URL. 10474 var htmlCache = (typeof Map !== 'undefined') ? new Map() : null; 10475 var CACHE_TTL = 5 * 60 * 1000; // 5 min 10476 var prefetchCtrls = {}; 10477 10478 function cacheGet(key) { 10479 if (!htmlCache) return null; 10480 var hit = htmlCache.get(key); 10481 if (!hit) return null; 10482 if ((perfNow() - hit.t) > CACHE_TTL) { htmlCache.delete(key); return null; } 10483 return hit.html; 10484 } 10485 function cacheSet(key, html) { 10486 if (htmlCache) htmlCache.set(key, { html: html, t: perfNow() }); 10487 } 10488 function clearHtmlCache() { if (htmlCache) htmlCache.clear(); } 10489 10490 // Warm the cache on intent (hover / focus / idle). One request per 10491 // handle; skips if already cached or in flight. 10492 function prefetchHandle(pathOnly) { 10493 if (!htmlCache || !pathOnly) return;
10494 // Full-page cache key â colour swaps swap the whole #MainContent, so the 10495 // click reads the FULL sibling page from cache (same key as the swap's 10496 // fetchUrl when opts.fullPage). Warms the whole colourway page + its 10497 // primary image at idle so the click is instant and flash-free. 10498 var key = pathOnly; 10499 if (cacheGet(key) || prefetchCtrls[key]) return; 10500 var ctrl = ('AbortController' in window) ? new AbortController() : null; 10501 prefetchCtrls[key] = ctrl || true; 10502 fetch(key, { 10503 credentials: 'same-origin', 10504 headers: { 'Accept': 'text/html' }, 10505 signal: ctrl ? ctrl.signal : undefined 10506 }) 10507 .then(function (r) { return r.ok ? r.text() : null; }) 10508 .then(function (html) { 10509 if (!html) return; 10510 cacheSet(key, html); 10511 log('prefetched', key); 10512 // Warm the incoming primary image too, so a hover-then-click swap 10513 // is flash-free (HTML + image both cached). 10514 try { 10515 var pdoc = new DOMParser().parseFromString(html, 'text/html'); 10516 var pimg = firstGalleryImg(pdoc); 10517 if (pimg) { var warm = new Image(); warm.src = pimg; } 10518 } catch (e) {} 10519 }) 10520 .catch(function () {}) 10521 .then(function () { delete prefetchCtrls[key]; }); 10522 } 10523 10524 // Title + canonical from the swapped-in section (a section_id response 10525 // carries no <head>, so updateMetaFromDoc can't run). Reads the 10526 // product name from the section's JSON-LD (bp-product-schema block). 10527 function updateMetaFromProduct(displayUrl, sectionId) { 10528 try { 10529 var scope = (sectionId && document.getElementById('shopify-section-' + sectionId)) || document; 10530 var ld = scope.querySelector('script[type="application/ld+json"]'); 10531 if (ld && ld.textContent) { 10532 var data = JSON.parse(ld.textContent); 10533 if (data && data.name) document.title = data.name; 10534 } 10535 } catch (e) { log('meta title failed', e); } 10536 try { 10537 var canon = document.querySelector('link[rel="canonical"]'); 10538 if (canon) canon.setAttribute('href', window.location.origin + displayUrl.split('?')[0].split('#')[0]); 10539 } catch (e) {} 10540 } 10541 10542 // Which sibling swatches are confirmed in-stock (visible, not --pending) 10543 // right now. Captured before a colour swap so we can reveal exactly those 10544 // on the re-rendered strip â the fresh strip renders its siblings 10545 // --pending (display:none) until bp-color-siblings re-fetches them, and a 10546 // click in that gap is the "have to click the colour twice" bug. 10547 function captureVisibleSiblings() { 10548 var out = {}; 10549 try { 10550 var strip = document.querySelector('[data-bp-colors]'); 10551 if (strip) { 10552 var items = strip.querySelectorAll('.bp-colors__item'); 10553 for (var i = 0; i < items.length; i++) { 10554 if (!items[i].classList.contains('bp-colors__item--pending')) { 10555 var h = items[i].getAttribute('data-bp-color-handle'); 10556 if (h) out[h] = true; 10557 } 10558 } 10559 } 10560 } catch (e) {} 10561 return out; 10562 } 10563 function revealSiblings(root, set) { 10564 if (!root || !set) return; 10565 try { 10566 var strip = root.querySelector('[data-bp-colors]'); 10567 if (!strip) return; 10568 var pend = strip.querySelectorAll('.bp-colors__item--pending'); 10569 for (var i = 0; i < pend.length; i++) { 10570 var h = pend[i].getAttribute('data-bp-color-handle'); 10571 if (h && set[h]) pend[i].classList.remove('bp-colors__item--pending'); 10572 } 10573 } catch (e) {} 10574 } 10575 10576 var inflight = null; 10577 10578 function swapToHandle(targetPath, opts) { 10579 if (!targetPath) return Promise.resolve(); 10580 opts = opts || {}; 10581 var displayUrl = preserveTracking(targetPath); 10582 var pathOnly = targetPath.split('?')[0].split('#')[0]; 10583 var savedScrollY = window.scrollY || window.pageYOffset || 0; 10584 var sectionId = getMainSectionId(); 10585 10586 if (inflight) { 10587 try { inflight.abort(); } catch (e) {} 10588 } 10589 var ctrl = ('AbortController' in window) ? new AbortController() : null; 10590 inflight = ctrl; 10591 10592 document.body.classList.add('bp-pdp-swapping'); 10593 10594 // Colour swaps (opts.fullPage) swap the ENTIRE #MainContent so every 10595 // product-dependent section reflects the new colourway â buy box, How to 10596 // Wear It, You'll Also Love, Yotpo stars/reviews â the way a full reload 10597 // would, but AJAX + cross-faded. Other swaps stay section-scoped (just the 10598 // product section) for speed. Both cache by their fetch URL. 10599 var fullPage = !!opts.fullPage; 10600 var fetchUrl = (!fullPage && sectionId) ? sectionFetchUrl(pathOnly, sectionId) : pathOnly;
10601 var cached = cacheGet(fetchUrl); 10602 var tStart = perfNow(); 10603 log('fetching', fetchUrl, cached ? '(cache hit)' : ''); 10604 10605 var pending = cached 10606 ? Promise.resolve(cached) 10607 : fetch(fetchUrl, { 10608 credentials: 'same-origin', 10609 headers: { 'Accept': 'text/html' }, 10610 signal: ctrl ? ctrl.signal : undefined 10611 }).then(function (r) { 10612 log('fetch status', r.status); 10613 if (!r.ok) throw new Error('HTTP ' + r.status); 10614 return r.text(); 10615 }).then(function (html) { 10616 cacheSet(fetchUrl, html); 10617 return html; 10618 }); 10619 10620 return pending 10621 .then(function (html) { 10622 log('fetch done in', Math.round(perfNow() - tStart), 'ms', cached ? '(cached)' : ''); 10623 var doc = new DOMParser().parseFromString(html, 'text/html'); 10624 var freshSection = (!fullPage && sectionId) 10625 ? (doc.getElementById('shopify-section-' + sectionId) || doc.querySelector('.shopify-section')) 10626 : null; 10627 10628 // Decode the incoming primary image before touching the DOM so 10629 // the swap reveals a ready image instead of a blank â pop flash. 10630 return preloadImg(firstGalleryImg(freshSection || doc)).then(function () { 10631 var tSwap = perfNow(); 10632 var newRoot = null; 10633 10634 // Confirmed-in-stock siblings BEFORE the swap â revealed instantly on 10635 // the re-rendered strip so it never flashes hidden ("click twice"). 10636 var visibleSiblings = opts.crossfade ? captureVisibleSiblings() : null; 10637 10638 // The DOM mutation itself. Kept in one function so it can run either 10639 // directly or inside a View-Transition callback (which must mutate 10640 // synchronously). Sets the closure `newRoot`. 10641 function performSwap() { 10642 if (sectionId && freshSection) { 10643 var liveSection = document.getElementById('shopify-section-' + sectionId); 10644 if (liveSection) { 10645 // Replace only the product section â the browser parses + 10646 // connects its custom elements; reexecuteScripts re-runs the 10647 // block IIFEs (same mechanism as before, smaller surface). 10648 liveSection.outerHTML = freshSection.outerHTML; 10649 newRoot = document.getElementById('shopify-section-' + sectionId); 10650 } 10651 } 10652 if (!newRoot) { 10653 // Fallback: full-#MainContent swap (id unresolved or response 10654 // wasn't section-scoped) â preserves the original behavior. 10655 var freshMain = doc.getElementById('MainContent'); 10656 var liveMain = document.getElementById('MainContent'); 10657 if (!freshMain || !liveMain) throw new Error('no #MainContent to swap'); 10658 liveMain.outerHTML = freshMain.outerHTML; 10659 newRoot = document.getElementById('MainContent'); 10660 updateMetaFromDoc(doc); 10661 } 10662 if (newRoot) { 10663 reexecuteScripts(newRoot); 10664 if (visibleSiblings) revealSiblings(newRoot, visibleSiblings); 10665 } 10666 } 10667 10668 // Everything that must run AFTER the DOM is swapped â meta, size 10669 // restore, and critically the `bp:pdp:swapped` event that tells the 10670 // OTHER product sections (How to Wear It, etc.) to re-render for the 10671 // new colour. This MUST run after performSwap, not just after the 10672 // call that schedules it: startViewTransition() runs its callback 10673 // asynchronously, so dispatching here synchronously (as before) fired 10674 // the event against the OLD DOM and those sections never updated â 10675 // the "How to Wear It doesn't switch" regression. On release 10676 // (no transition) performSwap is synchronous and this ran after it. 10677 function afterSwap() { 10678 log('swap applied in', Math.round(perfNow() - tSwap), 'ms', (opts.crossfade && allowMotion && document.startViewTransition) ? '(view-transition)' : ''); 10679 10680 // Full-page swaps already updated meta from the parsed doc 10681 // (updateMetaFromDoc in performSwap); only the section-scoped path 10682 // needs the JSON-LD title/canonical fallback. 10683 if (!fullPage && sectionId) updateMetaFromProduct(displayUrl, sectionId); 10684 10685 // Re-apply the shopper's size on the swapped-in product so a 10686 // colour change doesn't reset it (QA 7/16). rAF lets the new 10687 // <variant-picker> upgrade + connect first. 10688 if (opts.restoreOptions && opts.restoreOptions.length && newRoot) { 10689 requestAnimationFrame(function () { 10690 restoreVariantOptions(newRoot, opts.restoreOptions); 10691 }); 10692 } 10693 10694 var handle = (pathOnly.match(/\/products\/([^\/?#]+)/) || [])[1] || ''; 10695 document.body.classList.remove('bp-pdp-swapping'); 10696 if (opts.keepScroll) { 10697 // Colour swap = in-place content update, not a navigation. 10698 // Hold the shopper's scroll position so it doesn't read as a 10699 // full page reload. 10700 try { window.scrollTo(0, savedScrollY); } catch (e) {} 10701 } else { 10702 try { 10703 window.scrollTo({ top: 0, behavior: 'auto' }); 10704 } catch (e) { window.scrollTo(0, 0); } 10705 } 10706 10707 document.dispatchEvent(new CustomEvent('bp:pdp:swapped', { 10708 detail: { handle: handle, url: displayUrl } 10709 })); 10710 log('done â', displayUrl); 10711 } 10712 10713 // Cross-fade the swap with the View Transitions API (colour swaps 10714 // only). The browser snapshots the old product area, applies the 10715 // swap, and cross-fades oldânew â a smooth dissolve, not a hard 10716 // repaint/flash. Every colourway is prefetched at page load, so this 10717 // fires with no network wait ("preload all â instant cross-fade"). 10718 // afterSwap() is chained off updateCallbackDone so the swapped-in DOM 10719 // is live before bp:pdp:swapped fires. Falls back to the 10720 // .bp-pdp-fade-in class when the API is absent (Firefox) or motion is 10721 // reduced. 10722 var allowMotion = !(window.matchMedia && window.matchMedia('(prefers-reduced-motion: reduce)').matches); 10723 if (opts.crossfade && allowMotion && document.startViewTransition) { 10724 var transition = document.startViewTransition(performSwap); 10725 return transition.updateCallbackDone.then(afterSwap, afterSwap); 10726 } else { 10727 performSwap(); 10728 if (newRoot) newRoot.classList.add('bp-pdp-fade-in'); 10729 afterSwap(); 10730 } 10731 }); 10732 }) 10733 .catch(function (err) { 10734 document.body.classList.remove('bp-pdp-swapping'); 10735 if (err && err.name === 'AbortError') return; 10736 console.warn('[bp-pdp-swap] swap failed', err); 10737 document.dispatchEvent(new CustomEvent('bp:pdp:swap-failed', { 10738 detail: { url: displayUrl, error: err } 10739 })); 10740 }); 10741 } 10742 10743 function isSwapCandidate(anchor) { 10744 if (!anchor || !anchor.href) return false;
10745 var hrefAttr = anchor.getAttribute('href') || ''; 10746 if (!hrefAttr || hrefAttr.charAt(0) === '#') return false; 10747 if (anchor.target && anchor.target !== '_self') return false; 10748 if (anchor.hasAttribute('download')) return false; 10749 var u; 10750 try { u = new URL(anchor.href, window.location.origin); } catch (e) { return false; } 10751 if (u.origin !== window.location.origin) return false; 10752 if (!/^\/products\//.test(u.pathname)) return false; 10753 var here = (window.location.pathname.match(/\/products\/[^\/?#]+/) || [''])[0]; 10754 var there = (u.pathname.match(/\/products\/[^\/?#]+/) || [''])[0]; 10755 if (here === there) return false; 10756 return true; 10757 } 10758 10759 document.addEventListener('click', function (e) { 10760 if (e.defaultPrevented) return; 10761 if (e.button !== 0) return; 10762 if (e.metaKey || e.ctrlKey || e.shiftKey || e.altKey) return; 10763 10764 var anchor = e.target.closest('a[href]'); 10765 if (!anchor) return; 10766 // Anchors inside the quick-view drawer are handled by the drawer 10767 // itself (it swaps the sibling into the drawer, not the main page). 10768 if (anchor.closest('#quick-add-dialog, .quick-add-modal, .bp-qd')) return; 10769 if (!anchor.closest('[data-bp-colors], [data-bp-pdp-swap]')) return; 10770 if (!isSwapCandidate(anchor)) return; 10771 10772 e.preventDefault(); 10773 var href = anchor.getAttribute('href'); 10774 var preserved = preserveTracking(href); 10775 log('intercept click â', preserved); 10776 if (window.history && window.history.pushState) { 10777 window.history.pushState({ bpPdpSwap: true }, '', preserved); 10778 } 10779 // Colour-swatch swaps stay in place: keep the scroll position, carry the 10780 // shopper's size selection across, and cross-fade the change (View 10781 // Transitions) so it reads as a smooth dissolve, not a reload/flash. 10782 var isColorSwap = !!anchor.closest('[data-bp-colors]'); 10783 swapToHandle(href, isColorSwap ? { 10784 keepScroll: true, 10785 crossfade: true, 10786 fullPage: true, 10787 restoreOptions: captureVariantOptions() 10788 } : undefined); 10789 }, true); 10790 10791 // Warm the section-scoped cache on intent so the click resolves with 10792 // no network. mouseover/focusin/touchstart all bubble, so one 10793 // delegated listener covers every swatch. prefetchHandle de-dupes. 10794 function onIntent(e) { 10795 var t = e.target; 10796 var anchor = t && t.closest ? t.closest('[data-bp-colors] a[href]') : null; 10797 if (!anchor) return; 10798 if (anchor.closest('#quick-add-dialog, .quick-add-modal, .bp-qd')) return; 10799 if (!isSwapCandidate(anchor)) return; 10800 prefetchHandle(anchor.getAttribute('href').split('?')[0].split('#')[0]); 10801 } 10802 ['mouseover', 'focusin', 'touchstart'].forEach(function (evt) { 10803 document.addEventListener(evt, onIntent, { passive: true }); 10804 }); 10805 10806 // Idle-preload EVERY colourway sibling after first paint ("preload all"): 10807 // the whole colour family shares one section id and is cached in-memory, so 10808 // once this runs, every swatch click â on this product or any colourway we 10809 // swap to â resolves from cache with no network. prefetchHandle also warms 10810 // each incoming primary image, so the click cross-fade is flash-free. The 10811 // 30-item cap is a runaway guard (INV5 families are far smaller); the swatch 10812 // anchors are all present in the DOM even while --pending, so this doesn't 10813 // depend on bp-color-siblings having finished revealing them. 10814 function idlePrefetchSiblings() { 10815 try { 10816 var anchors = document.querySelectorAll('[data-bp-colors] a[href]'); 10817 var count = 0; 10818 for (var i = 0; i < anchors.length && count < 30; i++) { 10819 if (!isSwapCandidate(anchors[i])) continue; 10820 prefetchHandle(anchors[i].getAttribute('href').split('?')[0].split('#')[0]); 10821 count++; 10822 } 10823 } catch (e) {} 10824 } 10825 if ('requestIdleCallback' in window) { 10826 requestIdleCallback(idlePrefetchSiblings, { timeout: 3000 }); 10827 } else { 10828 setTimeout(idlePrefetchSiblings, 1500); 10829 } 10830 10831 // Price / availability can drift after a cart change â drop the cache. 10832 // 'cart:update' is Horizon's stock event; 'cart:updated' is BP-internal. 10833 document.addEventListener('cart:update', clearHtmlCache); 10834 document.addEventListener('cart:updated', clearHtmlCache); 10835 10836 window.addEventListener('popstate', function () { 10837 if (!/^\/products\//.test(window.location.pathname)) return; 10838 swapToHandle(window.location.pathname + window.location.search); 10839 }); 10840 10841 document.addEventListener('bp:pdp:swapped', function () { 10842 // Re-hydrate the swapped-in Yotpo widgets (stars + reviews). The full 10843 // #MainContent swap injects fresh widget markup for the new product, so 10844 // the SDK has to be poked to scan + render it. The MODERN widget SDK is 10845 // window.yotpoWidgetsContainer.initWidgets() â the old code only called the 10846 // legacy yotpo.refreshWidgets(), which no-ops on the new widget markup, so 10847 // stars/reviews never rendered after a colour swap. 10848 // 10849 // Call only the FIRST available API â don't stack the legacy 10850 // refreshWidgets() on top of initWidgets() (extra full re-scans). Firing 10851 // both back-to-back races two concurrent scans against the same 10852 // .yotpo-widget-instance nodes, which left some stars painted from one 10853 // pass and others stuck on the pre-render "full" placeholder (DEV-1587: 10854 // stars showing all-filled regardless of actual rating after a colour 10855 // swap). Same fix already applied in bp-quick-drawer.liquid â mirror it 10856 // here. 10857 try { 10858 var yotpoWidgets = document.querySelectorAll('.yotpo-widget-instance');
10859 yotpoWidgets.forEach(function (w) { 10860 w.removeAttribute('data-yotpo-element-loaded'); 10861 w.removeAttribute('data-yotpo-widget-uuid'); 10862 }); 10863 if (window.yotpoWidgetsContainer && typeof window.yotpoWidgetsContainer.initWidgets === 'function') { 10864 window.yotpoWidgetsContainer.initWidgets(); 10865 } else if (window.YotpoApi && typeof window.YotpoApi.refreshWidgets === 'function') { 10866 window.YotpoApi.refreshWidgets(); 10867 } else if (window.Yotpo && window.Yotpo.API && typeof window.Yotpo.API.refreshWidgets === 'function') { 10868 window.Yotpo.API.refreshWidgets(); 10869 } else if (window.yotpo && typeof window.yotpo.refreshWidgets === 'function') { 10870 window.yotpo.refreshWidgets(); 10871 } 10872 } catch (e) {} 10873 10874 try { 10875 if (window.tfcapi && typeof window.tfcapi === 'function') { 10876 window.tfcapi('calculate'); 10877 } 10878 } catch (e) {} 10879 10880 try { 10881 var script = document.querySelector('[data-bp-product-schema]'); 10882 var newProduct = script && script.textContent ? JSON.parse(script.textContent) : null; 10883 if (newProduct && window.dataLayer && Array.isArray(window.dataLayer)) { 10884 window.dataLayer.push({ event: 'view_item', ecommerce: { items: [{ 10885 item_id: newProduct.id, item_name: newProduct.title, price: newProduct.price 10886 }]}}); 10887 } 10888 } catch (e) {} 10889 }); 10890 10891 window.bpPdpSwap = swapToHandle; 10892})(); 10893</script>
10893 10894 10895<style> 10896 .bp-pdp-swapping { cursor: progress; } 10897 10898 /* Colour-swap cross-fade via the View Transitions API. The browser 10899 snapshots the old page, we swap the product section, and it cross-fades 10900 oldânew. Only the product area differs between colourways, so the root 10901 cross-fade reads as that area dissolving â the header/footer are 10902 identical old vs new, so they don't visibly change. Short + eased for a 10903 premium feel. Under prefers-reduced-motion the JS skips 10904 startViewTransition entirely, so no snapshot/animation runs. */ 10905 ::view-transition-old(root), 10906 ::view-transition-new(root) { animation-duration: 0.26s; animation-timing-function: ease; } 10907 10908 /* Fallback (Firefox / no View-Transitions): fade the swapped-in section in 10909 so a colour change still reads as a transition, not a hard repaint. */ 10910 @keyframes bpPdpFadeIn { from { opacity: 0; } to { opacity: 1; } } 10911 .bp-pdp-fade-in { animation: bpPdpFadeIn 0.22s ease both; } 10912 @media (prefers-reduced-motion: reduce) { .bp-pdp-fade-in { animation: none; } } 10913</style>
10913<script src="//www.bostonproper.com/cdn/shop/t/1521/assets/bp-stl-drawer.js?v=94264581757947261801790713714" defer></script>
10913 10914
10914<script src="//www.bostonproper.com/cdn/shop/t/1521/assets/bp-mega-hover.js?v=154014205721503400101790713714" defer></script>
10914 10915 10916 10917 10918 10919 10920 10921
10921<script> 10922 (function (w, d, s, l, i) { 10923 w[l] = w[l] || []; 10924 w[l].push({ 'gtm.start': new Date().getTime(), event: 'gtm.js' }); 10925 var f = d.getElementsByTagName(s)[0], 10926 j = d.createElement(s), 10927 dl = l != 'dataLayer' ? '&l=' + l : ''; 10928 j.async = true; 10929 j.src = 10930 'https://sst.bostonproper.com/gtm.js?id=' + 10931 i + 10932 dl + 10933 '>m_auth=288FkNoOr_QXQEJ-WPHnww>m_preview=env-1>m_cookies_win=x'; 10934 f.parentNode.insertBefore(j, f); 10935 })(window, document, 'script', 'dataLayer', 'GTM-NS3X79G'); 10936 </script>
10936<script id="customizer-settings" type="application/json"> 10937 { 10938 "final_sale_message": "Final Sale" 10939 } 10940 </script>
10940 10941 10942
10942<script>window.performance && window.performance.mark && window.performance.mark('shopify.content_for_header.start');</script>
10942<meta name="facebook-domain-verification" content="9a5visacrvjynl4zpb4ys6j4byfwwp"> 10943<meta id="shopify-digital-wallet" name="shopify-digital-wallet" content="/52960723110/digital_wallets/dialog"> 10944<meta id="in-context-paypal-metadata" data-shop-id="52960723110" data-venmo-supported="false" data-environment="production" data-locale="en_US" data-paypal-v4="true" data-currency="USD"> 10945<link rel="ucp" href="/.well-known/ucp" version="2026-08-25"><link rel="buy" href="/cart/56389031657638:1?ucp/version=2026-08-25&context/payment/0/handler=dev.shopify.shop_pay" payment="dev.shopify.shop_pay com.google.pay dev.shopify.card"><link rel="alternate" type="application/json+oembed" href="https://www.bostonproper.com/products/boston-proper-printed-scarf-with-love-bp.oembed">
10946<script async="async" src="/checkouts/internal/preloads.js?locale=en-US&default_configuration_id=8857419942"></script>
10946 10947<link rel="preconnect" href="https://shop.app" crossorigin="anonymous">
10948<script async="async" src="https://shop.app/checkouts/internal/preloads.js?locale=en-US&default_configuration_id=8857419942&shop_id=52960723110" crossorigin="anonymous"></script>
vendor: 1 bytes, line 10948
10948
10949<script id="apple-pay-shop-capabilities" type="application/json">{"shopId":52960723110,"countryCode":"US","currencyCode":"USD","merchantCapabilities":["supports3DS"],"merchantId":"gid:\/\/shopify\/Shop\/52960723110","merchantName":"Boston Proper","requiredBillingContactFields":["postalAddress","email"],"requiredShippingContactFields":["postalAddress","email"],"shippingType":"shipping","supportedNetworks":["visa","masterCard","amex","discover","elo","jcb"],"total":{"type":"pending","label":"Boston Proper","amount":"1.00"},"shopifyPaymentsEnabled":true,"supportsSubscriptions":true}</script>
vendor: 1 bytes, line 10949
10949
10950<script id="shopify-features" type="application/json">{"accessToken":"eb9ee10d84a005c2b8e778780263a9d2","betas":["rich-media-storefront-analytics"],"domain":"www.bostonproper.com","predictiveSearch":true,"shopId":52960723110,"locale":"en"}</script>
vendor: 1 bytes, line 10950
10950
10951<script>var Shopify = Shopify || {}; 10952Shopify.shop = "bostonproper.myshopify.com"; 10953Shopify.locale = "en"; 10954Shopify.currency = {"active":"USD","rate":"1.0"}; 10955Shopify.country = "US"; 10956Shopify.theme = {"name":"Winter Home 1 | 10\/1 | V2.0.12","id":198106775718,"schema_name":"Horizon","schema_version":"3.5.0","theme_store_id":null,"role":"main"}; 10957Shopify.theme.handle = "null"; 10958Shopify.theme.style = {"id":null,"handle":null}; 10959Shopify.cdnHost = "www.bostonproper.com/cdn"; 10960Shopify.routes = Shopify.routes || {}; 10961Shopify.routes.root = "/"; 10962Shopify.shopJsCdnBaseUrl = "https://cdn.shopify.com/shopifycloud/shop-js"; 10963Shopify.SignInWithShop = Shopify.SignInWithShop || {}; 10964Shopify.SignInWithShop.User = Shopify.SignInWithShop.User || {}; 10965Shopify.SignInWithShop.User.recognized = false;</script>
vendor: 1 bytes, line 10965
10965
10966<script type="module">!function(o){(o.Shopify=o.Shopify||{}).modules=!0}(window);</script>
vendor: 1 bytes, line 10966
10966
10967<script>!function(o){function n(){var o=[];function n(){o.push(Array.prototype.slice.apply(arguments))}return n.q=o,n}var t=o.Shopify=o.Shopify||{};t.loadFeatures=n(),t.autoloadFeatures=n()}(window);</script>
vendor: 1 bytes, line 10967
10967
10968<script> 10969 window.ShopifyPay = window.ShopifyPay || {}; 10970 window.ShopifyPay.apiHost = "shop.app\/pay"; 10971 window.ShopifyPay.redirectState = null; 10972</script>
vendor: 1 bytes, line 10972
10972
10973<script> 10974 window.Shopify = window.Shopify || {}; 10975 window.Shopify.SignInWithShop = window.Shopify.SignInWithShop || {}; 10976 window.Shopify.SignInWithShop.assetMetrics = { sampleRate: 0.25 }; 10977 window.Shopify.SignInWithShop.eligible = true; 10978</script>
vendor: 1 bytes, line 10978
10978
10979<script id="shop-js-analytics" type="application/json">{"pageType":"product"}</script>
vendor: 1 bytes, line 10979
10979
10980<script defer="defer" async type="module" src="https://cdn.shopify.com/shopifycloud/shop-js/modules/v2/loader.init-shop-cart-sync.en.esm.js"></script>
vendor: 1 bytes, line 10980
10980
10981<script type="module"> 10982 await import("https://cdn.shopify.com/shopifycloud/shop-js/modules/v2/loader.init-shop-cart-sync.en.esm.js"); 10983 10984 window.Shopify.SignInWithShop?.initShopCartSync?.({"fedCMEnabled":true,"windoidEnabled":true}); 10985 10986</script>
vendor: 1 bytes, line 10986
10986
10987<script> 10988 window.Shopify = window.Shopify || {}; 10989 if (!window.Shopify.featureAssets) window.Shopify.featureAssets = {}; 10990 window.Shopify.featureAssets['shop-js'] = {"avatar":["modules/v2/loader.avatar.en.esm.js"],"checkout-modal":["modules/v2/loader.checkout-modal.en.esm.js"],"init-customer-accounts-sign-up":["modules/v2/loader.init-customer-accounts-sign-up.en.esm.js"],"init-customer-accounts":["modules/v2/loader.init-customer-accounts.en.esm.js"],"init-fed-cm":["modules/v2/loader.init-fed-cm.en.esm.js"],"init-shop-cart-sync":["modules/v2/loader.init-shop-cart-sync.en.esm.js"],"init-shop-email-lookup-coordinator":["modules/v2/loader.init-shop-email-lookup-coordinator.en.esm.js"],"init-shop-for-new-customer-accounts":["modules/v2/loader.init-shop-for-new-customer-accounts.en.esm.js"],"init-shop-user-recognition":["modules/v2/loader.init-shop-user-recognition.en.esm.js"],"init-windoid":["modules/v2/loader.init-windoid.en.esm.js"],"lead-capture":["modules/v2/loader.lead-capture.en.esm.js"],"listener":["modules/v2/loader.listener.en.esm.js"],"pay-button":["modules/v2/loader.pay-button.en.esm.js"],"payment-terms":["modules/v2/loader.payment-terms.en.esm.js"],"shop-button":["modules/v2/loader.shop-button.en.esm.js"],"shop-cart-sync":["modules/v2/loader.shop-cart-sync.en.esm.js"],"shop-cash-offers":["modules/v2/loader.shop-cash-offers.en.esm.js"],"shop-follow-button":["modules/v2/loader.shop-follow-button.en.esm.js"],"shop-login-button":["modules/v2/loader.shop-login-button.en.esm.js"],"shop-login":["modules/v2/loader.shop-login.en.esm.js"],"shop-toast-manager":["modules/v2/loader.shop-toast-manager.en.esm.js"],"shop-user-recognition":["modules/v2/loader.shop-user-recognition.en.esm.js"]}; 10991</script>
vendor: 1 bytes, line 10991
10991
10992<script>(function() { 10993 var isLoaded = false; 10994 function asyncLoad() { 10995 if (isLoaded) return; 10996 isLoaded = true; 10997 var urls = ["https:\/\/assets.smartwishlist.webmarked.net\/static\/v6\/smartwishlist.js?shop=bostonproper.myshopify.com","\/\/staticw2.yotpo.com\/gadsaMpv721mEDmSKobOdMTvocj7m5YYA1Mhe5gz\/widget.js?shop=bostonproper.myshopify.com","https:\/\/str.rise-ai.com\/?shop=bostonproper.myshopify.com","https:\/\/strn.rise-ai.com\/?shop=bostonproper.myshopify.com","https:\/\/cdn-loyalty.yotpo.com\/loader\/K13MR0xh3osOG6J13_kFIw.js?shop=bostonproper.myshopify.com","\/\/staticw2.yotpo.com\/l9OjGj5YqKVR9PFonEHJc4ms9HXq9RV8dOWnI8LL\/widget.js?shop=bostonproper.myshopify.com","https:\/\/config.gorgias.chat\/bundle-loader\/01GYCCVTPDFMYGPZ5V9AWDKET3?source=shopify1click\u0026shop=bostonproper.myshopify.com","https:\/\/container.pepperjam.com\/4041270386.js?shop=bostonproper.myshopify.com","https:\/\/media.9gtb.com\/loader.js?g_cvt_id=78dc5f7e-d6e2-4440-bdb7-1111f59638b3\u0026shop=bostonproper.myshopify.com"]; 10998 for (var i = 0; i < urls.length; i++) { 10999 var s = document.createElement('script'); 11000 s.type = 'text/javascript'; 11001 s.async = true; 11002 s.src = urls[i]; 11003 var x = document.getElementsByTagName('script')[0]; 11004 x.parentNode.insertBefore(s, x); 11005 } 11006 }; 11007 if(window.attachEvent) { 11008 window.attachEvent('onload', asyncLoad); 11009 } else { 11010 window.addEventListener('load', asyncLoad, false); 11011 } 11012})();</script>
vendor: 1 bytes, line 11012
11012
11013<script id="__st">var __st={"a":52960723110,"offset":-14400,"reqid":"366c6d67-7b42-4351-97e1-f226d34f308c-1791063068","pageurl":"www.bostonproper.com\/products\/boston-proper-printed-scarf-with-love-bp","u":"a321b22096fb","p":"product","rtyp":"product","rid":15286962618534};</script>
vendor: 1 bytes, line 11013
11013
11014<script>window.ShopifyPaypalV4VisibilityTracking = true;</script>
vendor: 1 bytes, line 11014
11014
11015<script id="captcha-bootstrap">!function(){'use strict';const t='contact',e='account',n='new_comment',o=[[t,t],['blogs',n],['comments',n],[t,'customer']],c=[[e,'customer_login'],[e,'guest_login'],[e,'recover_customer_password'],[e,'create_customer']],r=t=>t.map((([t,e])=>`form[action*='/${t}']:not([data-nocaptcha='true']) input[name='form_type'][value='${e}']`)).join(','),a=t=>()=>t?[...document.querySelectorAll(t)].map((t=>t.form)):[];function s(){const t=[...o],e=r(t);return a(e)}const i='password',u='form_key',d=['recaptcha-v3-token','g-recaptcha-response','h-captcha-response',i],f=()=>{try{return window.sessionStorage}catch{return}},m='__shopify_v',_=t=>t.elements[u];function p(t,e,n=!1){try{const o=window.sessionStorage,c=JSON.parse(o.getItem(e)),{data:r}=function(t){const{data:e,action:n}=t;return t[m]||n?{data:e,action:n}:{data:t,action:n}}(c);for(const[e,n]of Object.entries(r))t.elements[e]&&(t.elements[e].value=n);n&&o.removeItem(e)}catch(o){console.error('form repopulation failed',{error:o})}}const l='form_type',E='cptcha';function T(t){t.dataset[E]=!0}const w=window,h=w.document,L='Shopify',v='ce_forms',y='captcha';let A=!1;((t,e)=>{const n=(g='f06e6c50-85a8-45c8-87d0-21a2b65856fe',I='https://cdn.shopify.com/shopifycloud/storefront-forms-hcaptcha/ce_storefront_forms_captcha_hcaptcha.v1.5.3.iife.js',D={infoText:'Protected by hCaptcha',privacyText:'Privacy',termsText:'Terms'},(t,e,n)=>{const o=w[L][v],c=o.bindForm;if(c)return c(t,g,e,D).then(n);var r;o.q.push([[t,g,e,D],n]),r=I,A||(h.body.append(Object.assign(h.createElement('script'),{id:'captcha-provider',async:!0,src:r})),A=!0)});var g,I,D;w[L]=w[L]||{},w[L][v]=w[L][v]||{},w[L][v].q=[],w[L][y]=w[L][y]||{},w[L][y].protect=function(t,e){n(t,void 0,e),T(t)},Object.freeze(w[L][y]),function(t,e,n,w,h,L){const[v,y,A,g]=function(t,e,n){const i=e?o:[],u=t?c:[],d=[...i,...u],f=r(d),m=r(i),_=r(d.filter((([t,e])=>n.includes(e))));return[a(f),a(m),a(_),s()]}(w,h,L),I=t=>{const e=t.target;return e instanceof HTMLFormElement?e:e&&e.form},D=t=>v().includes(t);t.addEventListener('submit',(t=>{const e=I(t);if(!e)return;const n=D(e)&&!e.dataset.hcaptchaBound&&!e.dataset.recaptchaBound,o=_(e),c=g().includes(e)&&(!o||!o.value);(n||c)&&t.preventDefault(),c&&!n&&(function(t){try{if(!f())return;!function(t){const e=f();if(!e)return;const n=_(t);if(!n)return;const o=n.value;o&&e.removeItem(o)}(t);const e=Array.from(Array(32),(()=>Math.random().toString(36)[2])).join('');!function(t,e){_(t)||t.append(Object.assign(document.createElement('input'),{type:'hidden',name:u})),t.elements[u].value=e}(t,e),function(t,e){const n=f();if(!n)return;const o=[...t.querySelectorAll(`input[type='${i}']`)].map((({name:t})=>t)),c=[...d,...o],r={};for(const[a,s]of new FormData(t).entries())c.includes(a)||(r[a]=s);n.setItem(e,JSON.stringify({[m]:1,action:t.action,data:r}))}(t,e)}catch(e){console.error('failed to persist form',e)}}(e),e.submit())}));const S=(t,e)=>{t&&!t.dataset[E]&&(n(t,e.some((e=>e===t))),T(t))};for(const o of['focusin','change'])t.addEventListener(o,(t=>{const e=I(t);D(e)&&S(e,y())}));const B=e.get('form_key'),M=e.get(l),P=B&&M;t.addEventListener('DOMContentLoaded',(()=>{const t=y();if(P)for(const e of t)e.elements[l].value===M&&p(e,B);[...new Set([...A(),...v().filter((t=>'true'===t.dataset.shopifyCaptcha))])].forEach((e=>S(e,t)))}))}(h,new URLSearchParams(w.location.search),n,t,e,['guest_login'])})(!1,!0)}();</script>
vendor: 1 bytes, line 11015
11015
11016<script integrity="sha256-JjoPp5ZfB1sSAs5SQaol1x1GgvveM+BgmRzyDexInEQ=" data-source-attribution="shopify.loadfeatures" defer="defer" src="//www.bostonproper.com/cdn/shopifycloud/storefront/assets/storefront/load_feature-1bd60354.js" crossorigin="anonymous"></script>
vendor: 1 bytes, line 11016
11016
11017<script>(function () {var userAgent = navigator.userAgent;var platform = navigator.platform;var maxTouchPoints = navigator.maxTouchPoints || 0;var isIOS = /iPad|iPhone|iPod/.test(platform) || (platform === 'MacIntel' && maxTouchPoints > 1);var isMacSafari = platform.indexOf('Mac') === 0 && /Safari/.test(userAgent) && !/Chrome|Chromium|CriOS|FxiOS|Edg|OPR|Android/.test(userAgent);var isAppleSafari = isIOS || isMacSafari;if (isAppleSafari) {fetch('/sf_private_access_tokens' + location.search).catch(function () {});}function browserMajorVersion(pattern) {var match = userAgent.match(pattern);return match ? parseInt(match[1], 10) : null;}function shouldLoadAutosizesPolyfill() {if (!window.PerformanceObserver?.supportedEntryTypes?.includes('paint')) {return false;}var chromeVersion = browserMajorVersion(/Chrome\/(\d+)/);if (chromeVersion !== null) {return chromeVersion < 126;}var firefoxVersion = browserMajorVersion(/Firefox\/(\d+)/);if (firefoxVersion !== null) {return firefoxVersion < 150;}var safariVersion = isAppleSafari ? browserMajorVersion(/Version\/(\d+).*Safari\//) : null;if (safariVersion !== null) {return safariVersion < 27;}return true;}if (shouldLoadAutosizesPolyfill()) {var autosizesScript = document.createElement('script');autosizesScript.async = true;autosizesScript.crossOrigin = 'anonymous';autosizesScript.src = "//www.bostonproper.com/cdn/shopifycloud/storefront/assets/storefront/autosizes-84416378.js";(document.head || document.documentElement).appendChild(autosizesScript);}window.ShopifyAnalytics = window.ShopifyAnalytics || {};window.ShopifyAnalytics.performance = window.ShopifyAnalytics.performance || {};(function () {var LONG_FRAME_THRESHOLD = 50;var longAnimationFrames = [];var activeRafId = null;function collectLongFrames() {var previousTime = null;function rafMonitor(now) {if (activeRafId === null) {return;}var delta = now - previousTime;if (delta > LONG_FRAME_THRESHOLD) {longAnimationFrames.push({startTime: previousTime,endTime: now,});}previousTime = now;activeRafId = requestAnimationFrame(rafMonitor);}previousTime = performance.now();activeRafId = requestAnimationFrame(rafMonitor);}if (!window.PerformanceObserver?.supportedEntryTypes?.includes('long-animation-frame')) {collectLongFrames();var timeoutId = setTimeout(function () {cancelAnimationFrame(activeRafId);}, 10000);window.ShopifyAnalytics.performance.getLongAnimationFrames = function (stopCollection) {if (stopCollection === undefined) {stopCollection = false;}if (stopCollection) {clearTimeout(timeoutId);cancelAnimationFrame(activeRafId);}return longAnimationFrames;};}})();})();</script>
11017<script crossorigin="anonymous" defer="defer" src="//www.bostonproper.com/cdn/shopifycloud/storefront/assets/shopify_pay/storefront-616277c7.js?v=20250812"></script>
vendor: 1 bytes, line 11017
11017
11018<script id="shopify-origin-trials" async="async" integrity="sha256-qJbe/jEZBHtr5W/BlA2cKAz3vkXu6vJQyLjgvgCC86g=" src="//cdn.shopify.com/shopifycloud/storefront/assets/storefront/origin_trials-318ab40a.js" crossorigin="anonymous" onload="window.__shopifyOriginTrialsDone = true" onerror="window.__shopifyOriginTrialsDone = true"></script>
vendor: 1 bytes, line 11018
11018
11019<script> 11020 window.Shopify = window.Shopify || {}; 11021 window.Shopify.MCP = window.Shopify.MCP || {}; 11022 window.Shopify.MCP.enabled = true; 11023 window.Shopify.MCP.shop = "bostonproper.myshopify.com"; 11024 window.Shopify.MCP.mcpEndpoint = "https:\/\/www.bostonproper.com\/api\/mcp"; 11025 window.Shopify.MCP.tools = [{"name":"search_shop_policies_and_faqs","description":"Used to get facts about the stores policies, products, or services.\nSome examples of questions you can ask are:\n - What is your return policy?\n - What is your shipping policy?\n - What is your phone number?\n - What are your hours of operation?\"\n","inputSchema":{"$schema":"https:\/\/json-schema.org\/draft\/2020-12\/schema","type":"object","properties":{"query":{"type":"string","description":"A natural language query."},"context":{"type":"string","description":"Additional information about the request such as user demographics, mood, location, or other relevant details that could help in tailoring the response appropriately."}},"required":["query"]}}]; 11026</script>
vendor: 1 bytes, line 11026
11026
11027<script>(()=>{var d="shopify:webmcp_adapter_loaded",n=Symbol.for("shopify.webmcp_adapter_loading");function s(c,a,{win:r=window,doc:o=document}={}){function p(){try{return r.localStorage.getItem(d)==="true"}catch{return!1}}function l(){try{r.localStorage.setItem(d,"true")}catch{}}function u(){return typeof(o.modelContext||r.navigator?.modelContext)?.registerTool=="function"}function f(){if(r[n]||!u())return;let t=o.head||o.getElementsByTagName("head")[0];if(!t)return;let e=o.createElement("script");e.type="module",e.crossOrigin="anonymous",a&&(e.integrity=a),e.src=c,e.addEventListener("load",l,{once:!0}),e.addEventListener("error",()=>{r[n]=!1},{once:!0}),t.appendChild(e),r[n]=!0}function _(t){let e=o.getElementById("shopify-origin-trials");if(!e||r.__shopifyOriginTrialsDone){t();return}e.addEventListener("load",t,{once:!0}),e.addEventListener("error",t,{once:!0})}function i(){_(()=>r.setTimeout(f,0))}function m(){o.addEventListener("DOMContentLoaded",i,{once:!0})}p()?i():m()}s("\/\/www.bostonproper.com\/cdn\/shopifycloud\/storefront\/assets\/storefront\/webmcp-c6b62ece.js","sha256-ke4DDuBjdvea3vxLWxHc9E9nq8zihuG4IfYEGbb77ys=");})(); 11028</script>
vendor: 1 bytes, line 11028
11028
11029<script data-source-attribution="shopify.dynamic_checkout.dynamic.init">var Shopify=Shopify||{};Shopify.PaymentButton=Shopify.PaymentButton||{isStorefrontPortableWallets:!0,init:function(){window.Shopify.PaymentButton.init=function(){};var t=document.createElement("script");t.src="https://www.bostonproper.com/cdn/shopifycloud/portable-wallets/latest/portable-wallets.en.js",t.type="module",document.head.appendChild(t)}}; 11030</script>
vendor: 1 bytes, line 11030
11030
11031<script data-source-attribution="shopify.dynamic_checkout.buyer_consent"> 11032 function portableWalletsHideBuyerConsent(e){var t=document.getElementById("shopify-buyer-consent"),n=document.getElementById("shopify-subscription-policy-button");t&&n&&(t.classList.add("hidden"),t.setAttribute("aria-hidden","true"),n.removeEventListener("click",e))}function portableWalletsShowBuyerConsent(e){var t=document.getElementById("shopify-buyer-consent"),n=document.getElementById("shopify-subscription-policy-button");t&&n&&(t.classList.remove("hidden"),t.removeAttribute("aria-hidden"),n.addEventListener("click",e))}window.Shopify?.PaymentButton&&(window.Shopify.PaymentButton.hideBuyerConsent=portableWalletsHideBuyerConsent,window.Shopify.PaymentButton.showBuyerConsent=portableWalletsShowBuyerConsent); 11033</script>
vendor: 1 bytes, line 11033
11033
11034<script> 11035 function portableWalletsCleanup(e){e&&e.src&&console.error("Failed to load portable wallets script "+e.src);var t=document.querySelectorAll("shopify-accelerated-checkout .shopify-payment-button__skeleton, shopify-accelerated-checkout-cart .wallet-cart-button__skeleton"),e=document.getElementById("shopify-buyer-consent");for(let e=0;e<t.length;e++)t[e].remove();e&&e.remove()}function portableWalletsNotLoadedAsModule(e){e instanceof ErrorEvent&&"string"==typeof e.message&&e.message.includes("import.meta")&&"string"==typeof e.filename&&e.filename.includes("portable-wallets")&&(window.removeEventListener("error",portableWalletsNotLoadedAsModule),window.Shopify.PaymentButton.failedToLoad=e,"loading"===document.readyState?document.addEventListener("DOMContentLoaded",window.Shopify.PaymentButton.init):window.Shopify.PaymentButton.init())}window.addEventListener("error",portableWalletsNotLoadedAsModule); 11036</script>
11036 11037
11038<script type="module" src="https://www.bostonproper.com/cdn/shopifycloud/portable-wallets/latest/portable-wallets.en.js" onError="portableWalletsCleanup(this)" crossorigin="anonymous"></script>
vendor: 1 bytes, line 11038
11038
11039<script nomodule> 11040 document.addEventListener("DOMContentLoaded", portableWalletsCleanup); 11041</script>
11041 11042
11043<script integrity="sha256-/LA1C2Bx6899fYCos0FS9qLj2opaGFm7AXrTUvBEb4s=" data-source-attribution="shopify.remote_product_tracking" data-shopify-remote-tracking="{"session_id":"01M41TV44XWD8DKQ9237Y83TWX","publisher_shop_id":52960723110,"theme_id":198106775718,"theme_published":true,"theme_store_id":0,"placement":"collection","event_data":{"source_product_id":15286962618534,"handle":"boston-proper-printed-scarf-with-love-bp","page_type":"product"},"recommendations_tracking_enabled":true}" defer="defer" src="//www.bostonproper.com/cdn/shopifycloud/storefront/assets/storefront/remote_product_tracking-91d95044.js" crossorigin="anonymous"></script>
11043 11044<link id="shopify-accelerated-checkout-styles" rel="stylesheet" media="screen" href="https://www.bostonproper.com/cdn/shopifycloud/portable-wallets/latest/accelerated-checkout-backwards-compat.css" crossorigin="anonymous"> 11045<style id="shopify-accelerated-checkout-cart"> 11046 #shopify-buyer-consent { 11047 margin-top: 1em; 11048 display: inline-block; 11049 width: 100%; 11050} 11051 11052#shopify-buyer-consent.hidden { 11053 display: none; 11054} 11055 11056#shopify-subscription-policy-button { 11057 background: none; 11058 border: none; 11059 padding: 0; 11060 text-decoration: underline; 11061 font-size: inherit; 11062 cursor: pointer; 11063} 11064 11065#shopify-subscription-policy-button::before { 11066 box-shadow: none; 11067} 11068 11069 </style> 11070<link rel="stylesheet" media="screen" href="//www.bostonproper.com/cdn/shop/t/1521/compiled_assets/styles.css?v=14521000909776209651790719704">
11071<script id="sections-script" data-sections="bp-how-to-wear-it,bp-collection-details,bp-yotpo-reviews,bp-footer-main,bp-quick-drawer" defer="defer" src="//www.bostonproper.com/cdn/shop/t/1521/compiled_assets/scripts.js?v=99640834855126272041790719705"></script>
vendor: 1 bytes, line 11071
11071
11072<script id="blocks-script" data-blocks="bp-complete-the-look,bp-pdp-bundle,bp-pdp-media-gallery,bp-promo-line,bp-require-size" defer="defer" src="//www.bostonproper.com/cdn/shop/t/1521/compiled_assets/block-scripts.js?v=169005028727735037821790713714"></script>
vendor: 1 bytes, line 11072
11072
11073<script id="snippets-script" data-snippets="bp-search-drawer,bp-card-swatches" defer="defer" src="//www.bostonproper.com/cdn/shop/t/1521/compiled_assets/snippet-scripts.js?v=66079492489357529931790713714"></script>
vendor: 1 bytes, line 11073
11073
11074<script id="shopify-cfh-end">window.performance && window.performance.mark && window.performance.mark('shopify.content_for_header.end');</script>
11074 11075 <!-- BEGIN app block: shopify://apps/black-crow-ai-that-delivers/blocks/blackcrow_ai_script/3e73f597-cb71-47a1-8a06-0c931482ecd2 --> 11076
11077<script type="text/javascript" async src="https://shopify-init.blackcrow.ai/js/core/bostonproper.js?shop=bostonproper.myshopify.com&tagSrc=ShopifyThemeAppExtension"></script>
11077 11078 11079 11080<!-- END app block --><!-- BEGIN app block: shopify://apps/yotpo-loyalty-rewards/blocks/loader-app-embed-block/2f9660df-5018-4e02-9868-ee1fb88d6ccd --> 11081
11081<script src="https://cdn-widgetsrepository.yotpo.com/v1/loader/K13MR0xh3osOG6J13_kFIw" async></script>
11081 11082 11083 11084 11085
11085<script src="https://cdn-loyalty.yotpo.com/loader/K13MR0xh3osOG6J13_kFIw.js?shop=www.bostonproper.com" async></script>
11085 11086 11087 11088 11089 11090 11091<!-- END app block --><!-- BEGIN app block: shopify://apps/bloomreach-loomi/blocks/unified-tracking-script/019beafc-b2ba-790d-9ed1-850d869fc572 --> 11092 11093 11094 11095 11096 11097 11098 11099 11100 <!-- BEGIN app snippet: bloomreach-analytics-publish -->
11100<script> 11101 /** 11102 * Publishes an event to Shopify.analytics with retry logic. 11103 * Waits for Shopify.analytics to become available before publishing. 11104 * 11105 * @param {string} eventName - The name of the event to publish 11106 * @param {object} eventData - The data payload for the event 11107 */ 11108 window.bloomreachPublishEvent = function(eventName, eventData) { 11109 const maxAttempts = 100; // Max 10 seconds (100 * 100ms) 11110 let attempts = 0; 11111 11112 function tryPublish() { 11113 if (typeof Shopify !== "undefined" && Shopify.analytics && typeof Shopify.analytics.publish === "function") { 11114 Shopify.analytics.publish(eventName, eventData); 11115 } else if (attempts < maxAttempts) { 11116 attempts++; 11117 setTimeout(tryPublish, 100); 11118 } else { 11119 console.error('[Bloomreach Unified Tracking] Shopify.analytics object is not available'); 11120 } 11121 } 11122 tryPublish(); 11123 }; 11124</script>
11124 11125<!-- END app snippet --> 11126 <!-- BEGIN app snippet: bloomreach-consent-gate -->
11126<script> 11127 (function() { 11128 const LOG_PREFIX = '[Bloomreach Unified Tracking]'; 11129 const POLL_INTERVAL_MS = 100; 11130 const MAX_POLL_ATTEMPTS = 100; 11131 let trackingInitialized = false; 11132 let consentListenerRegistered = false; 11133 11134 function isBloomreachTrackingConsentGranted() { 11135 const privacy = window.Shopify && window.Shopify.customerPrivacy; 11136 if (!privacy) { 11137 return false; 11138 } 11139 return typeof privacy.analyticsProcessingAllowed === 'function' 11140 && typeof privacy.marketingAllowed === 'function' 11141 && typeof privacy.saleOfDataAllowed === 'function' 11142 && privacy.analyticsProcessingAllowed() 11143 && privacy.marketingAllowed() 11144 && privacy.saleOfDataAllowed(); 11145 } 11146 11147 function waitForCustomerPrivacy(onReady, onTimeout) { 11148 let attempts = 0; 11149 11150 function tryLoadPrivacyApi() { 11151 if (typeof window.Shopify === 'undefined' 11152 || typeof window.Shopify.loadFeatures !== 'function') { 11153 if (attempts < MAX_POLL_ATTEMPTS) { 11154 attempts++; 11155 setTimeout(tryLoadPrivacyApi, POLL_INTERVAL_MS); 11156 return; 11157 } 11158 if (onTimeout) { 11159 onTimeout(); 11160 } 11161 return; 11162 } 11163 11164 window.Shopify.loadFeatures( 11165 [{ name: 'consent-tracking-api', version: '0.1' }], 11166 function(error) { 11167 if (error) { 11168 if (onTimeout) { 11169 onTimeout(); 11170 } 11171 return; 11172 } 11173 onReady(); 11174 }, 11175 ); 11176 } 11177 11178 tryLoadPrivacyApi(); 11179 } 11180 11181 function tryInitialize(initializeFn, config) { 11182 if (trackingInitialized) { 11183 return; 11184 } 11185 if (!isBloomreachTrackingConsentGranted()) { 11186 return; 11187 } 11188 if (typeof window.initializeDiscoveryCookie === 'function') { 11189 window.initializeDiscoveryCookie(); 11190 } 11191 trackingInitialized = true; 11192 initializeFn(config); 11193 } 11194 11195 window.runWhenBloomreachTrackingAllowed = function(startFn, config, loadFn) { 11196 if (typeof loadFn === 'function') { 11197 loadFn(config); 11198 } 11199 11200 function onPrivacyApiReady() { 11201 tryInitialize(startFn, config); 11202 11203 if (!consentListenerRegistered) { 11204 consentListenerRegistered = true; 11205 document.addEventListener('visitorConsentCollected', function() { 11206 tryInitialize(startFn, config); 11207 }); 11208 } 11209 } 11210 11211 function onPrivacyApiUnavailable() { 11212 console.error(LOG_PREFIX + ' Customer Privacy API unavailable'); 11213 } 11214 11215 waitForCustomerPrivacy(onPrivacyApiReady, onPrivacyApiUnavailable); 11216 }; 11217 })(); 11218</script>
11218 11219<!-- END app snippet -->
11220<script> 11221 window.bloomreachPublishEvent("bre.domain_key", { 11222 domain_key: "bostonproper" 11223 }); 11224 </script>
11224<script> 11225 // Publish localization data to the web pixel extension 11226 window.bloomreachPublishEvent("bre.localization", { 11227 country: "US", 11228 language: "en" 11229 }); 11230 11231 11232 window.bloomreachPublishEvent("bre.feature_flags", {}); 11233 window.bloomreachPublishEvent("bre.discovery_config", { 11234 test_data: false, 11235 debug: false 11236 }); 11237 </script>
11237 11238 11239 11240 11241 11242 <!-- BEGIN app snippet: bloomreach-br-uid-2-cookie -->
11242<script> 11243 (function() { 11244 const BR_UID_2_COOKIE_NAME = '_br_uid_2'; 11245 const BR_UID_2_VERSION = 'app'; 11246 const BR_UID_2_COOKIE_MAX_AGE_SECONDS = 60 * 60 * 24 * 365 * 2; 11247 11248 function decodeBrUid2CookieValue(value) { 11249 try { 11250 return decodeURIComponent(value); 11251 } catch (error) { 11252 return value; 11253 } 11254 } 11255 11256 function generateBrUid2Uid() { 11257 return String(Math.round(Math.random() * 10e12)); 11258 } 11259 11260 function formatBrUid2CookieValue(uid, version, timestamp, hitCount) { 11261 return 'uid=' + uid + ':v=' + version + ':ts=' + timestamp + ':hc=' + hitCount; 11262 } 11263 11264 function generateBrUid2CookieValue(hitCount) { 11265 return formatBrUid2CookieValue( 11266 generateBrUid2Uid(), 11267 BR_UID_2_VERSION, 11268 Date.now(), 11269 hitCount 11270 ); 11271 } 11272 11273 function parseBrUid2CookieValue(value) { 11274 const segments = decodeBrUid2CookieValue(value).split(':'); 11275 let uid; 11276 let version; 11277 let timestamp; 11278 let hitCount; 11279 11280 for (let i = 0; i < segments.length; i++) { 11281 const segment = segments[i]; 11282 if (segment.indexOf('uid=') === 0) { 11283 uid = segment.slice(4); 11284 } else if (segment.indexOf('v=') === 0) { 11285 version = segment.slice(2); 11286 } else if (segment.indexOf('ts=') === 0) { 11287 timestamp = parseInt(segment.slice(3), 10); 11288 } else if (segment.indexOf('hc=') === 0) { 11289 hitCount = parseInt(segment.slice(3), 10); 11290 } 11291 } 11292 11293 if (!uid || !version || isNaN(timestamp) || isNaN(hitCount) || hitCount < 0) { 11294 return null; 11295 } 11296 11297 return { 11298 uid: uid, 11299 version: version, 11300 timestamp: timestamp, 11301 hitCount: hitCount 11302 }; 11303 } 11304 11305 function getBrUid2CookieValue() { 11306 const cookiePrefix = BR_UID_2_COOKIE_NAME + '='; 11307 const cookies = document.cookie.split('; '); 11308 11309 for (let i = 0; i < cookies.length; i++) { 11310 if (cookies[i].indexOf(cookiePrefix) === 0) { 11311 return decodeBrUid2CookieValue(cookies[i].substring(cookiePrefix.length)); 11312 } 11313 } 11314 11315 return null; 11316 } 11317 11318 function getBaseDomain(host) { 11319 if (!host) { 11320 return ''; 11321 } 11322 const parts = host.split('.'); 11323 const length = parts.length; 11324 11325 // Handle cases like 'localhost', 'example.com', '192.168.1.1' 11326 if (length <= 2 || /^\d+\.\d+\.\d+\.\d+$/.test(host)) { 11327 return host; 11328 } 11329 11330 return parts[length - 1].length <= 2 && parts[length - 2].length <= 3 11331 ? parts.slice(-3).join('.') 11332 : parts.slice(-2).join('.'); 11333 } 11334 11335 function buildBrUid2CookieString(value, domain) { 11336 const cookieParts = [ 11337 BR_UID_2_COOKIE_NAME + '=' + encodeURIComponent(value), 11338 'path=/', 11339 'max-age=' + BR_UID_2_COOKIE_MAX_AGE_SECONDS, 11340 'SameSite=Lax' 11341 ]; 11342 11343 if (domain) { 11344 cookieParts.push('domain=' + domain); 11345 } 11346 11347 return cookieParts.join('; '); 11348 } 11349 11350 function setBrUid2CookieValue(value) { 11351 const baseDomain = getBaseDomain(location.hostname); 11352 11353 document.cookie = buildBrUid2CookieString(value, baseDomain); 11354 11355 if (getBrUid2CookieValue() !== value) { 11356 document.cookie = buildBrUid2CookieString(value, null); 11357 } 11358 } 11359 11360 function ensureBrUid2Cookie() { 11361 const existingValue = getBrUid2CookieValue(); 11362 11363 if (existingValue) { 11364 const parsed = parseBrUid2CookieValue(existingValue); 11365 if (parsed) { 11366 return existingValue; 11367 } 11368 } 11369 11370 const createdValue = generateBrUid2CookieValue(1); 11371 setBrUid2CookieValue(createdValue); 11372 return createdValue; 11373 } 11374 11375 window.initializeDiscoveryCookie = function() { 11376 const discoveryCookie = ensureBrUid2Cookie(); 11377 if (typeof window.bloomreachPublishEvent === 'function') { 11378 window.bloomreachPublishEvent('bre.discovery_cookie', { cookie: discoveryCookie }); 11379 } 11380 }; 11381 })(); 11382</script>
11382 11383<!-- END app snippet --> 11384 11385 11386
11386<script src="https://cdn.shopify.com/extensions/01a0f796-156f-7d78-b8ad-f52a94b2ec91/bloomreach-loomi-298/assets/unified-tracking-shop-price.js" defer></script>
11386 11387 11388 11389 11390 11391 11392 11393 11394 11395 11396 11397 11398 11399 11400 11401 11402 11403 11404 11405 11406 11407 11408 11409 11410 11411 11412 11413 11414 11415 11416 11417 11418 11419 11420 11421 11422 11423 11424 11425 11426 11427
11427<script> 11428 const trackingConfig = { 11429 brweb: { 11430 target: "https:\/\/us3-api.eng.bloomreach.com", 11431 stream_id: "d042b089-6e20-44b6-82b8-7bd75742c4a8", 11432 data_layer: true, 11433 11434 track: { 11435 default_properties: { 11436 customer_locale: "en-US" 11437 } 11438 11439 }, 11440 customConfigRaw: "" 11441 }, 11442 11443 11444 shopCurrency: "USD", 11445 shopCountryCode: "", 11446 11447 customer: { 11448 11449 }, 11450 metadata: { 11451 domain_key: "bostonproper" 11452 11453 11454 }, 11455 deferredEvents: [ 11456 11457 11458 { 11459 name: "product_viewed_custom", 11460 data: { 11461 product_id: 15286962618534, 11462 title: "Boston Proper Printed Scarf With Love BP", 11463 price: "50.00", 11464 vendor: "Boston Proper", 11465 brand: "Boston Proper", 11466 variant_id: 56389031657638, 11467 tags: ["2CT80","2CT8071","799","Active","Charmeuse","Fashion","Floral","Full Price","INV5_2CT80","Miscellaneous","N\/A","Non-Apparel","nvgroup_2CT80","Pink","Promotions","Published: 20261001","rtly-gc:offer-free-shipping","Scarves","Winter 26","Work","Woven","YGroup_2CT80"], 11468 available: true, 11469 stock_level: 2617, 11470 variant_inventory: 2617, 11471 sku: "2CT8071000", 11472 price_local_currency: 50.0, 11473 11474 original_price: 50.0, 11475 11476 original_price_local_currency: 50.0, 11477 local_currency: "USD", 11478 language: "en", 11479 categories_id: ["na"], 11480 categories_name: ["Uncategorized"], 11481 collections_id: [283797979302,680550269094,294174752934,305400348838,294285869222,290934751398,295577223334,295577190566,301181567142,294917963942], 11482 collections_name: ["All","All - Excluding Gift Cards and BWL","Auto Shoes \u0026 Accessories","Breast Cancer Awareness","Charmeuse","Full Price","New Arrivals","New Arrivals: Shoes \u0026 Accessories","No Collectives","Pink Collection"] 11483 } 11484 }, 11485 11486 ] 11487 }; 11488 11489 function buildBrwebInitConfig(config) { 11490 const brwebBaseConfig = Object.assign({}, config.brweb); 11491 const customConfigRaw = brwebBaseConfig.customConfigRaw; 11492 delete brwebBaseConfig.customConfigRaw; 11493 11494 let customConfigOverrides = {}; 11495 try { 11496 if (customConfigRaw && customConfigRaw.trim() !== '') { 11497 customConfigOverrides = JSON.parse(customConfigRaw); 11498 } 11499 } catch (e) { 11500 console.error('[Bloomreach Unified Tracking] Invalid JSON in Custom Configuration, ignoring: ' + e.message); 11501 } 11502 11503 return Object.assign({}, brwebBaseConfig, customConfigOverrides); 11504 } 11505 11506 function loadBloomreachSdk(config) { 11507 const brwebInitConfig = buildBrwebInitConfig(config); 11508 11509 !function(e,t,n,i,o,r){const s=4e3,c="xnpe_async_hide";function a(e){if("number"!=typeof e)return e;const t=new Date;return new Date(t.getTime()+1e3*e)}function m(e){return e.reduce((function(e,t){return e[t]=function(){e._.push([t.toString(),arguments])},e}),{_:[]})}function p(e,t,n){const i=n.createElement(t);i.src=e;const o=n.getElementsByTagName(t)[0];return o.parentNode.insertBefore(i,o),i}function l(e){return"[object Date]"===Object.prototype.toString.call(e)}r.target=r.target||"https://api.exponea.com",r.file_path=r.file_path||r.target+"/js/brweb.min.js",o[t]=m(["anonymize","initialize","identify","getSegments","update","track","trackLink","trackEnhancedEcommerce","getHtml","showHtml","showBanner","showWebLayer","ping","getAbTest","loadDependency","getRecommendation","reloadWebLayers","_preInitialize","_initializeConfig","metadata"]),o[t].notifications=m(["isAvailable","isSubscribed","subscribe","unsubscribe"]),o[t].segments=m(["subscribe"]),o[t]["snippetVersion"]="v3.1.1",function(e,t,n,i){e[i]={sdk:e[n],sdkObjectName:n,skipExperiments:!!t.new_experiments,path:t.target,streamId:t.stream_id}}(o,r,t,i),function(e,t,n){p(e.file_path,t,n)}(r,n,e),function(e,t,n,i,o,r,m,u){if(!e.new_experiments)return;!0===e.new_experiments&&(e.new_experiments={});const f=e.new_experiments.hide_class||c,d=e.new_experiments.timeout||s,_=encodeURIComponent(r.location.href);let g;e.cookies&&e.cookies.expires&&("number"==typeof e.cookies.expires||l(e.cookies.expires)?g=a(e.cookies.expires):e.cookies.expires.tracking&&("number"==typeof e.cookies.expires.tracking||l(e.cookies.expires.tracking))&&(g=a(e.cookies.expires.tracking))),g&&g<ne
11509w Date&&(g=void 0);const k=e.target+"/webxp/streams/"+e.stream_id+"/"+t+"/"+(u.exec(m.cookie)||[0,"new"])[1]+"/modifications.min.js?http-referer="+_+"&timeout="+d+"ms"+(g?"&cookie-expires="+Math.floor(g.getTime()/1e3):"");"sync"===e.new_experiments.mode&&r.localStorage.getItem("__exponea__sync_modifications__")?function(e,t,n,i,o){n[o][t]="<"+t+' src="'+e+'"></'+t+">",i.writeln(n[o][t]),i.writeln("<"+t+">!"+o+".init && document.writeln("+o+"."+t+'.replace("/'+t+'/", "/'+t+'-async/").replace("><", " async><"))</'+t+">")}(k,t,r,m,n):function(e,t,n,i,o,r,s,c){r.documentElement.classList.add(e);const a=p(n,i,r);function m(){o[c].init||p(n.replace("/"+i+"/","/"+i+"-async/"),i,r)}function l(){r.documentElement.classList.remove(e)}a.onload=m,a.onerror=m,o.setTimeout(l,t),o[s]._revealPage=l}(f,d,k,t,r,m,o,n)}(r,n,i,0,t,o,e,RegExp("__exponea_etc__"+"=([\\w-]+)")),function(e,t,n){var i;e[t]._initializeConfig(n),(null===(i=n.experimental)||void 0===i?void 0:i.non_personalized_weblayers)&&e[t]._preInitialize(n),e[t].start=function(i){i&&Object.keys(i).forEac
11509h((e=>n[e]=i[e])),e[t].initialize(n)}}(o,t,r)}(document,"brweb","script","webxpClient",window, brwebInitConfig); 11510 11511 window.exponea = brweb; 11512 } 11513 11514 function startBloomreachTracking(config) { 11515 window.dataLayer = window.dataLayer || []; 11516 11517 function subscribeCookieListener(dataLayer) { 11518 dataLayer.forEach(processDatalayerEvent); 11519 11520 const originalPush = dataLayer.push; 11521 dataLayer.push = function() { 11522 originalPush.apply(dataLayer, arguments); 11523 Array.from(arguments).forEach(processDatalayerEvent); 11524 }; 11525 } 11526 11527 function processDatalayerEvent(event) { 11528 if (event.event === "bre.cookie") { 11529 Shopify.analytics.publish("bre.cookie", event); 11530 } 11531 } 11532 11533 subscribeCookieListener(window.dataLayer); 11534 11535 function getCookie(cname) { 11536 var name = cname + "="; 11537 var ca = document.cookie.split(';'); 11538 var cookie = undefined; 11539 for (var i = 0; i < ca.length; i++) { 11540 var c = ca[i]; 11541 while (c.charAt(0) == ' ') { 11542 c = c.substring(1); 11543 } 11544 if (c.indexOf(name) == 0) { 11545 cookie = c.substring(name.length, c.length); 11546 break; 11547 } 11548 } 11549 cookie = decodeURIComponent(cookie); 11550 cookie = cookie.split('?')[0]; 11551 return cookie; 11552 } 11553 11554 var customer = Object.assign({}, config.customer); 11555 var cart_id = getCookie('cart'); 11556 if (cart_id) customer.cart_id = cart_id; 11557 11558 exponea.start({ 11559 customer: customer 11560 }); 11561 11562 if (config.metadata) { 11563 exponea.metadata(config.metadata); 11564 } 11565 11566 /** 11567 * Quick View Tracking 11568 * Listens for clicks on native Shopify quick-add-component elements. 11569 * Only tracks when clicking a button[type="button"] (opens modal). 11570 * Ignores button[type="submit"] (direct add to cart). 11571 */ 11572 (function() { 11573 document.addEventListener('click', function(event) { 11574 const quickAddComponent = event.target.closest('quick-add-component'); 11575 if (!quickAddComponent) return; 11576 11577 const clickedButton = event.target.closest('button'); 11578 if (!clickedButton || clickedButton.type !== 'button') return; 11579 11580 const productForm = quickAddComponent.querySelector('product-form-component'); 11581 const productId = productForm?.getAttribute('data-product-id'); 11582 if (!productId) return; 11583 11584 const variantInput = quickAddComponent.querySelector('input[name="id"]'); 11585 window.bloomreachPublishEvent('bre.quick_view', { 11586 product_id: productId, 11587 title: quickAddComponent.getAttribute('data-product-title') || '', 11588 variant_id: variantInput?.value || '' 11589 }); 11590 }); 11591 })(); 11592 11593 config.deferredEvents.forEach(function(event) { 11594 if (event.name !== "product_viewed_custom") { 11595 window.bloomreachPublishEvent(event.name, event.data); 11596 return; 11597 } 11598 11599 var published = false; 11600 var deadlineId = setTimeout(publishPresentment, 3000); 11601 var waitForEnrichId = null; 11602 var enrichAbort = typeof AbortController === "function" 11603 ? new AbortController() 11604 : null; 11605 function publishProductViewed(data) { 11606 if (published) { 11607 return; 11608 } 11609 published = true; 11610 clearTimeout(deadlineId); 11611 clearInterval(waitForEnrichId); 11612 window.removeEventListener("pagehide", publishPresentment); 11613 if (enrichAbort) { 11614 enrichAbort.abort(); 11615 } 11616 window.bloomreachPublishEvent(event.name, data); 11617 } 11618 function publishPresentment() { 11619 publishProductViewed(event.data); 11620 } 11621 function tryRunProductViewedEnrich() { 11622 var enrich = window.bloomreachEnrichProductViewedData; 11623 if (typeof enrich !== "function") { 11624 return false; 11625 } 11626 enrich(event.data, { 11627 shopCurrency: config.shopCurrency || "", 11628 shopCountryCode: config.shopCountryCode || "", 11629 signal: enrichAbort ? enrichAbort.signal : undefined 11630 }).then(publishProductViewed, publishPresentment); 11631 return true; 11632 } 11633 window.addEventListener("pagehide", publishPresentment, { once: true }); 11634 if (!tryRunProductViewedEnrich()) { 11635 waitForEnrichId = setInterval(function() { 11636 if (published || tryRunProductViewedEnrich()) { 11637 clearInterval(waitForEnrichId); 11638 } 11639 }, 25); 11640 } 11641 }); 11642 } 11643 11644 runWhenBloomreachTrackingAllowed(startBloomreachTracking, trackingConfig, loadBloomreachSdk); 11645 </script>
11645 11646 11647<!-- END app block --><!-- BEGIN app block: shopify://apps/microsoft-clarity/blocks/brandAgents_js/31c3d126-8116-4b4a-8ba1-baeda7c4aeea --> 11648 11649 11650
11651<script type="text/javascript"> 11652 (function(d){ 11653 var s = d.createElement('script'); 11654 s.async = true; 11655 s.src = "https://adsagentclientafd-b7hqhjdrf3fpeqh2.b01.azurefd.net/frontendInjection.js"; 11656 var firstScript = d.getElementsByTagName('script')[0]; 11657 firstScript.parentNode.insertBefore(s, firstScript); 11658 })(document); 11659</script>
11659 11660 11661 11662 11663<!-- END app block --><!-- BEGIN app block: shopify://apps/klaviyo-email-marketing-sms/blocks/klaviyo-onsite-embed/2632fe16-c075-4321-a88b-50b567f42507 --> 11664 11665 11666 11667 11668 11669 11670 11671 11672 11673 11674 11675 11676
11676<script async src="https://static.klaviyo.com/onsite/js/VZrTML/klaviyo.js?company_id=VZrTML"></script>
11676 11677
11677<script>!function(){if(!window.klaviyo){window._klOnsite=window._klOnsite||[];try{window.klaviyo=new Proxy({},{get:function(n,i){return"push"===i?function(){var n;(n=window._klOnsite).push.apply(n,arguments)}:function(){for(var n=arguments.length,o=new Array(n),w=0;w<n;w++)o[w]=arguments[w];var t="function"==typeof o[o.length-1]?o.pop():void 0,e=new Promise((function(n){window._klOnsite.push([i].concat(o,[function(i){t&&t(i),n(i)}]))}));return e}}})}catch(n){window.klaviyo=window.klaviyo||[],window.klaviyo.push=function(){var n;(n=window._klOnsite).push.apply(n,arguments)}}}}();</script>
11677 11678 11679 11680 11681 11682 11683 11684
11684<script> 11685 window.klaviyoReviewsProductDesignMode = false 11686 </script>
11686 11687 11688 11689 11690 <!-- BEGIN app snippet: customer-hub-data -->
11690<script> 11691 if (!window.customerHub) { 11692 window.customerHub = {}; 11693 } 11694 window.customerHub.storefrontRoutes = { 11695 login: "https://www.bostonproper.com/customer_authentication/redirect?locale=en®ion_country=US?return_url=%2F%23k-hub", 11696 register: "https://shopify.com/52960723110/account?locale=en®ion_country=US&buyer_flags=eyJhbGciOiJIUzI1NiJ9.eyJpc3MiOiJib3N0b25wcm9wZXIubXlzaG9waWZ5LmNvbSIsImZsYWdzIjpbXSwiZXhwIjoxNzkxNjY3ODY5LCJuYmYiOjE3OTEwNjMwNjl9.Y6VmvHvB93TtkRDu_h4pqSFO1UdZ1sak09aAYo6kJxE?return_url=%2F%23k-hub", 11697 logout: "/account/logout", 11698 profile: "/account", 11699 addresses: "/account/addresses", 11700 }; 11701 11702 window.customerHub.userId = null; 11703 11704 window.customerHub.storeDomain = "bostonproper.myshopify.com"; 11705 11706 11707 window.customerHub.activeProduct = { 11708 name: "Boston Proper Printed Scarf With Love BP", 11709 category: null, 11710 imageUrl: "https://www.bostonproper.com/cdn/shop/files/2CT8071_SET_e6f3e3ce-5a5f-451b-bc3b-ea1f8f71d236_grande.jpg?v=1791009198", 11711 id: "15286962618534", 11712 link: "https://www.bostonproper.com/products/boston-proper-printed-scarf-with-love-bp", 11713 options: [ 11714 11715 { 11716 name: "Color", 11717 values: [ 11718 11719 11720 11721 { 11722 name: "With Love BP", 11723 colorHex: null, 11724 swatchImageUrl: null 11725 } 11726 11727 ] 11728 }, 11729 11730 { 11731 name: "Size", 11732 values: [ 11733 11734 11735 11736 { 11737 name: "One Size", 11738 colorHex: null, 11739 swatchImageUrl: null 11740 } 11741 11742 ] 11743 } 11744 11745 ], 11746 variants: [ 11747 11748 { 11749 id: "56389031657638", 11750 selectedOptions: [ 11751 11752 11753 { 11754 name: "Color", 11755 value: "With Love BP" 11756 }, 11757 11758 11759 { 11760 name: "Size", 11761 value: "One Size" 11762 } 11763 11764 ], 11765 11766 imageUrl: "https://www.bostonproper.com/cdn/shop/files/2CT8071_SET_e6f3e3ce-5a5f-451b-bc3b-ea1f8f71d236_grande.jpg?v=1791009198", 11767 11768 price: "5000", 11769 currency: "USD", 11770 availableForSale: true, 11771 title: "With Love BP \/ One Size", 11772 }, 11773 11774 ], 11775 };
11776 window.customerHub.activeProduct.variants.forEach((variant) => { 11777 11778 variant.price = `${variant.price.slice(0, -2)}.${variant.price.slice(-2)}`; 11779 }); 11780 11781 11782 11783 window.customerHub.storeLocale = { 11784 currentLanguage: 'en', 11785 currentCountry: 'US', 11786 availableLanguages: [ 11787 11788 { 11789 iso_code: 'en', 11790 endonym_name: 'English' 11791 } 11792 11793 ], 11794 availableCountries: [ 11795 11796 { 11797 iso_code: 'CA', 11798 name: 'Canada', 11799 currency_code: 'CAD' 11800 }, 11801 11802 { 11803 iso_code: 'US', 11804 name: 'United States', 11805 currency_code: 'USD' 11806 } 11807 11808 ] 11809 }; 11810 11811</script>
11811 11812<!-- END app snippet --> 11813 11814 11815 11816 <!-- BEGIN app snippet: customer-hub-replace-links -->
11817<script> 11818 function replaceAccountLinks() { 11819 const selector = 11820 'a[href$="/account/login"], a[href$="/account"], a[href^="https://shopify.com/"][href*="/account"], a[href*="/customer_identity/redirect"], a[href*="/customer_authentication/redirect"], a[href$="/account'; 11821 const accountLinksNodes = document.querySelectorAll(selector); 11822 for (const node of accountLinksNodes) { 11823 const ignore = node.dataset.kHubIgnore !== undefined && node.dataset.kHubIgnore !== 'false'; 11824 if (!ignore) { 11825 // Any login links to Shopify's account system, point them at the customer hub instead. 11826 node.href = '#k-hub'; 11827 /** 11828 * There are some themes which apply a page transition on every click of an anchor tag (usually a fade-out) that's supposed to be faded back in when the next page loads. 11829 * However, since clicking the k-hub link doesn't trigger a page load, the page gets stuck on a blank screen. 11830 * Luckily, these themes usually have a className you can add to links to skip the transition. 11831 * Let's hope that all such themes are consistent/copy each other and just proactively add those classNames when we replace the link. 11832 **/ 11833 node.classList.add('no-transition', 'js-no-transition'); 11834 } 11835 } 11836 } 11837 11838 11839 if (document.readyState === 'complete') { 11840 replaceAccountLinks(); 11841 } else { 11842 const controller = new AbortController(); 11843 document.addEventListener( 11844 'readystatechange', 11845 () => { 11846 replaceAccountLinks(); // try to replace links both during `interactive` state and `complete` state 11847 if (document.readyState === 'complete') { 11848 // readystatechange can fire with "complete" multiple times per page load, so make sure we're not duplicating effort 11849 // by removing the listener afterwards. 11850 controller.abort(); 11851 } 11852 }, 11853 { signal: controller.signal }, 11854 ); 11855 } 11856 11857</script>
11857 11858<!-- END app snippet --> 11859 11860 11861 11862 11863 11864<!-- END app block --><!-- BEGIN app block: shopify://apps/ab-tasty/blocks/abtasty-tag/50439cd8-89e0-4240-838a-2948c07c763d --> 11865 11866
11866<script 11867 type="text/javascript" 11868 src="https://try.abtasty.com/443033f8f050ee927ac9047efa5db622.js" 11869 ></script>
11869 11870 11871
11871<script type="text/javascript"> 11872 // This waits for all the external ressources (including ABTasty tag) to load before executing the callback function. 11873 window.addEventListener('load', function () { 11874 if(!window.ABTasty) { 11875 // AB Tasty tag is not loaded so exit. 11876 return 11877 } 11878 11879 if (window.ABTasty.eventState?.['consentValid']?.status === 'complete') { 11880 updateShopifyDataForWebPixel(); 11881 } else { 11882 window.addEventListener('abtasty_consentValid', () => { 11883 updateShopifyDataForWebPixel(); 11884 }); 11885 } 11886 11887 11888 async function waitForVisitorId(maxRetries = 5, delayMs = 500) { 11889 for (let i = 0; i < maxRetries; i++) { 11890 const visitorId = ABTasty.api.v1.getValue('visitorId') 11891 if (visitorId) { 11892 return visitorId; 11893 } 11894 await new Promise(resolve => setTimeout(resolve, delayMs)); 11895 } 11896 return null; 11897 } 11898 11899 11900 async function updateShopifyDataForWebPixel() { 11901 const STORAGE_KEY = 'ABTastyShopify'; 11902 const consentReady = ABTasty.consentReady; 11903 const shouldHandleSubdomain = "false" === "true"; 11904 11905 if(!consentReady) { 11906 if(shouldHandleSubdomain) { 11907 await cookieStore.delete(STORAGE_KEY); 11908 } 11909 return localStorage.removeItem(STORAGE_KEY); 11910 } 11911 11912 const visitorId = await waitForVisitorId(5, 500); 11913 11914 if (!visitorId) { 11915 console.error('[AB Tasty] Unable to get AB Tasty visitorID after 5 retry'); 11916 return; 11917 } 11918 11919 const ABTastyData = { 11920 identifier: "443033f8f050ee927ac9047efa5db622", 11921 campaigns: ABTasty.getCampaignHistory(), 11922 visitorId: visitorId, 11923 accountId: ABTasty.getAccountSettings().id, 11924 consent: ABTasty.consentReady, 11925 }; 11926 11927 if (shouldHandleSubdomain) { 11928 await cookieStore.set({ 11929 name: STORAGE_KEY, 11930 value: JSON.stringify(ABTastyData), 11931 expires: Date.now() + 1 * 24 * 60 * 60 * 1000, // 1 day 11932 domain: window.location.hostname, 11933 }); 11934 } 11935 localStorage.setItem(STORAGE_KEY, JSON.stringify(ABTastyData)); 11936 } 11937 }); 11938 </script>
11938 11939 11940 11941 11942 11943 11944<!-- END app block --><!-- BEGIN app block: shopify://apps/microsoft-clarity/blocks/clarity_js/31c3d126-8116-4b4a-8ba1-baeda7c4aeea -->
11945<script type="text/javascript"> 11946 (function (c, l, a, r, i, t, y) { 11947 c[a] = c[a] || function () { (c[a].q = c[a].q || []).push(arguments); }; 11948 t = l.createElement(r); t.async = 1; t.src = "https://www.clarity.ms/tag/" + i + "?ref=shopify"; 11949 y = l.getElementsByTagName(r)[0]; y.parentNode.insertBefore(t, y); 11950 11951 c.Shopify.loadFeatures([{ name: "consent-tracking-api", version: "0.1" }], error => { 11952 if (error) { 11953 console.error("Error loading Shopify features:", error); 11954 return; 11955 } 11956 11957 c[a]('consentv2', { 11958 ad_Storage: c.Shopify.customerPrivacy.marketingAllowed() ? "granted" : "denied", 11959 analytics_Storage: c.Shopify.customerPrivacy.analyticsProcessingAllowed() ? "granted" : "denied", 11960 source: 101, 11961 }); 11962 }); 11963 11964 l.addEventListener("visitorConsentCollected", function (e) { 11965 c[a]('consentv2', { 11966 ad_Storage: e.detail.marketingAllowed ? "granted" : "denied", 11967 analytics_Storage: e.detail.analyticsAllowed ? "granted" : "denied", 11968 source: 101, 11969 }); 11970 }); 11971 })(window, document, "clarity", "script", "v94zgzk99z"); 11972</script>
11972 11973 11974 11975 11976<!-- END app block --><!-- BEGIN app block: shopify://apps/pc97pbn58-opensend/blocks/opensend/019d6301-977a-74bc-a568-af824856dc19 -->
11977<script type='text/javascript'> 11978!function(s) { 11979 let o = s.createElement('script'), u = s.getElementsByTagName('script')[0]; 11980 o.src = 'https://cdn.aggle.net/oir/oir.min.js'; 11981 o.async = !0, o.setAttribute('oirtyp', '48b1feaa'), o.setAttribute('oirid', 'P7C25MF72'); 11982 u.parentNode.insertBefore(o, u); 11983}(document); 11984</script>
11984 11985
11986<script type='text/javascript'> 11987 { 11988 const cleanObj = (value) => { 11989 if (Array.isArray(value)) { 11990 const cleanedArray = value.map(cleanObj).filter((v) => v !== undefined); 11991 return cleanedArray.length ? cleanedArray : undefined; 11992 } 11993 if (value && typeof value === "object") { 11994 const cleanedObject = Object.entries(value).reduce((acc, [key, val]) => { 11995 const cleanedValue = cleanObj(val); 11996 if (cleanedValue !== undefined && cleanedValue !== null && cleanedValue !== "") { 11997 acc[key] = cleanedValue; 11998 } 11999 return acc; 12000 }, {}); 12001 return Object.keys(cleanedObject).length ? cleanedObject : undefined; 12002 } 12003 if (value === null || value === undefined || value === "") { 12004 return undefined; 12005 } 12006 return value; 12007 }; 12008 const getCustomer = () => ({ 12009 shopifyCustomerId: "", 12010 email: "", 12011 phone: "", 12012 first_name: null, 12013 last_name: null, 12014 lifetime_value: null, 12015 order_count: null, 12016 address: { 12017 street: null, 12018 city: null, 12019 state: null, 12020 stateCode: null, 12021 postalCode: "", 12022 country: "", 12023 province: "", 12024 countryCode: "", 12025 } 12026 }); 12027 12028 } 12029</script>
12029 12030 12031 12032<!-- END app block --><link href="https://monorail-edge.shopifysvc.com" rel="dns-prefetch">
12033<script>(function(){if ("sendBeacon" in navigator && "performance" in window) {try {var session_token_from_headers = performance.getEntriesByType('navigation')[0].serverTiming.find(x => x.name == '_s').description;} catch {var session_token_from_headers = undefined;}var session_cookie_matches = document.cookie.match(/_shopify_s=([^;]*)/);var session_token_from_cookie = session_cookie_matches && session_cookie_matches.length === 2 ? session_cookie_matches[1] : "";var session_token = session_token_from_headers || session_token_from_cookie || "";function handle_abandonment_event(e) {var entries = performance.getEntries().filter(function(entry) {return /monorail-edge.shopifysvc.com/.test(entry.name);});if (!window.abandonment_tracked && entries.length === 0) {window.abandonment_tracked = true;var currentMs = Date.now();var navigation_start = performance.timing.navigationStart;var payload = {shop_id: 52960723110,url: window.location.href,navigation_start,duration: currentMs - navigation_start,session_token,page_type: "product"};window.navigator.sendBeacon("https://monorail-edge.shopifysvc.com/v1/produce", JSON.stringify({schema_id: "online_store_buyer_site_abandonment/1.1",payload: payload,metadata: {event_created_at_ms: currentMs,event_sent_at_ms: currentMs}}));}}window.addEventListener('pagehide', handle_abandonment_event);}}());</script>
vendor: 1 bytes, line 12033
12033
12034<script> 12035 window.__TREKKIE_SHIM_QUEUE = window.__TREKKIE_SHIM_QUEUE || []; 12036</script>
vendor: 1 bytes, line 12036
12036
12037<script>(function(){var wpmLoader=function(){"use strict";var e=/Googlebot|Storebot-Google|bingbot|Baiduspider|YandexBot|DuckDuckBot|Slurp|facebookexternalhit|Twitterbot|LinkedInBot|Applebot|AdsBot-Google|Mediapartners-Google|APIs-Google|PetalBot|SemrushBot|AhrefsBot|MJ12bot|DotBot|Acunetix|PerplexityBot|Perplexity-User/i,t=/bytedance/i;function i(){try{var e=document.cookie;if(!e||"string"!=typeof e)return;for(var t=0,i=e.split(";");t<i.length;t++){var n=i[t],r=n.indexOf("=");if(-1!==r){var o=n.slice(0,r).trim();if(o){var a=void 0;try{a=decodeURIComponent(o)}catch(e){a=o}if("_shopify_s"===a){var d=n.slice(r+1).trim();try{return decodeURIComponent(d)}catch(e){return d}}}}}return}catch(e){return}}function n(e){try{"undefined"!=typeof console&&"function"==typeof console.warn&&console.warn(e)}catch(e){}}function r(e,t){return"string"==typeof e&&e.length>0&&e.length<=t?e:void 0}function o(e){var t;switch(null==(t=null==e?void 0:e.navigation)?void 0:t.type){case 0:return"navigate";case 1:return"reload";case 2:return"back_forward";default:return}}function a(){var e,t,i;try{var n=null==(i=null==(t=null==(e=self.ShopifyAnalytics)?void 0:e.lib)?void 0:t.trekkie)?void 0:i.state;return"awaiting-consent"===n||"initialized"===n?n:void 0}catch(e){return}}return function(d,s,u,l){var c=arguments.length>4&&void 0!==arguments[4]?arguments[4]:{};try{var p=c.trekkieShim;!0!==p&&"true"!==p||null!=window.__TREKKIE_SHIM_QUEUE||(window.__TREKKIE_SHIM_QUEUE=[])}catch(e){}var f,v,h,y,g=(v=(f={modern:/Edge?\/(1{2}[4-9]|1[2-9]\d|[2-9]\d{2}|\d{4,})\.\d+(\.\d+|)|Firefox\/(1{2}[4-9]|1[2-9]\d|[2-9]\d{2}|\d{4,})\.\d+(\.\d+|)|Chrom(ium|e)\/(9{2}|\d{3,})\.\d+(\.\d+|)|(Maci|X1{2}).+ Version\/(15\.\d+|(1[6-9]|[2-9]\d|\d{3,})\.\d+)([,.]\d+|)( \(\w+\)|)( Mobile\/\w+|) Safari\/|Chrome.+OPR\/(9{2}|\d{3,})\.\d+\.\d+|(CPU[ +]OS|iPhone[ +]OS|CPU[ +]iPhone|CPU IPhone OS|CPU iPad OS)[ +]+(15[._]\d+|(1[6-9]|[2-9]\d|\d{3,})[._]\d+)([._]\d+|)|Android:?[ /-](14[89]|1[5-9]\d|[2-9]\d{2}|\d{4,})(\.\d+|)(\.\d+|)|Android.+Firefox\/(15\d|1[6-9]\d|[2-9]\d{2}|\d{4,})\.\d+(\.\d+|)|Android.+Chrom(ium|e)\/(14[89]|1[5-9]\d|[2-9]\d{2}|\d{4,})\.\d+(\.\d+|)|SamsungBrowser\/([2-9]\d|\d{3,})\.\d+/,legacy:/Edge?\/(1[6-9]|[2-9]\d|\d{3,})\.\d+(\.\d+|)|Firefox\/(5[4-9]|[6-9]\d|\d{3,})\.\d+(\.\d+|)|Chrom(ium|e)\/(5[1-9]|[6-9]\d|\d{3,})\.\d+(\.\d+|)([\d.]+$|.*Safari\/(?![\d.]+ Edge\/[\d.]+$))|(Maci|X1{2}).+ Version\/(10\.\d+|(1[1-9]|[2-9]\d|\d{3,})\.\d+)([,.]\d+|)( \(\w+\)|)( Mobile\/\w+|) Safari\/|Chrome.+OPR\/(3[89]|[4-9]\d|\d{3,})\.\d+\.\d+|(CPU[ +]OS|iPhone[ +]OS|CPU[ +]iPhone|CPU IPhone OS|CPU iPad OS)[ +]+(10[._]\d+|(1[1-9]|[2-9]\d|\d{3,})[._]\d+)([._]\d+|)|Android:?[ /-](14[89]|1[5-9]\d|[2-9]\d{2}|\d{4,})(\.\d+|)(\.\d+|)|Mobile Safari.+OPR\/([89]\d|\d{3,})\.\d+\.\d+|Android.+Firefox\/(15\d|1[6-9]\d|[2-9]\d{2}|\d{4,})\.\d+(\.\d+|)|Android.+Chrom(ium|e)\/(14[89]|1[5-9]\d|[2-9]\d{2}|\d{4,})\.\d+(\.\d+|)|Android.+(UC? ?Browser|UCWEB|U3)[ /]?(15\.([5-9]|\d{2,})|(1[6-9]|[2-9]\d|\d{3,})\.\d+)\.\d+|SamsungBrowser\/(5\.\d+|([6-9]|\d{2,})\.\d+)|Android.+MQ{2}Browser\/(14(\.(9|\d{2,})|)|(1[5-9]|[2-9]\d|\d{3,})(\.\d+|))(\.\d+|)|K[Aa][Ii]OS\/(3\.\d+|([4-9]|\d{2,})\.\d+)(\.\d+|)/}).modern,h=f.legacy,(y=navigator.userAgent).match(e)?"bot":y.match(v)?"modern":y.match(h)?"legacy":y.match(t)?"bot":"unknown"),m=function(e){var t,i,n=r(e,128);try{var a=window.performance,d=null==(t=null==a?void 0:a.getEntriesByType)?void 0:t.call(a,"navigation")[0];if(!d)return{requestId:n,navigationType:o(a)};var s="type"in d?r(d.type,64):void 0,u="serverTiming"in d&&Array.isArray(d.serverTiming)?d.serverTiming:[];return{requestId:null!=n?n:r(null==(i=u.find(function(e){return"requestID"===e.name}))?void 0:i.description,128),navigationType:s}}catch(e){return{requestId:n}}}(c.requestId),w=function(e){var t=e.version,r=e.browserTarget,o=e.surface,a=e.shopId,d=e.monorailEndpoint,s=window.location.href;return{emit:function(e){var u=e.status,l=e.errorMsg,c=e.failureReason,p=e.useKeepaliveFallback,f=void 0!==p&&p,v=e.navigationType,h=e.pagehidePersisted,y=e.requestId,g=e.trekkieState;if(d){var m,w;try{var b=(new Date).getTime();m=JSON.stringify({metadata:{event_sent_at_ms:b},events:[{schema_id:"web_pixels_manager_load/3.3",payload:{version:t,bundle_target:r,page_url:s,status:u,surface:o,error_msg:l,failure_reason:c,navigation_type:v,pagehide_persisted:h,request_id:y,trekkie_state:g,shop_id:a,visit_token:i()},metadata:{event_created_at_ms:b}}]})}catch(e){return}try{if("function"==typeof window.navigator.sendBeacon&&-1===(w=window.navigator.userAgent).indexOf("iPhone; CPU iPhone OS 12_")&&-1===w.indexOf("iPad; CPU OS 12_")&&-1===w.indexOf("iPod touch; CPU iPhone OS 12_")&&window.navigator.sendBeacon.bind(window.navigator)(d,m))return}catch(e){}if(f)try{"function"==typeof window.fetch&&window.fetch(d,{method:"POST",headers:{"Content-Type":"text/plain"},body:m,keepalive:!0}
12037).catch(function(){})}catch(e){}else try{var _=new XMLHttpRequest;_.open("POST",d,!0),_.setRequestHeader("Content-Type","text/plain"),_.send(m)}catch(e){n("[Web Pixels Manager] Got an unhandled error while logging to Monorail.")}}else n("[Web Pixels Manager] No Monorail endpoint provided, skipping logging.")}}}({version:u,browserTarget:g,surface:d.surface,shopId:d.shopId,monorailEndpoint:d.monorailEndpoint});if(Boolean(null==(_=null==(b=window.Shopify)?void 0:b.analytics)?void 0:_.replayQueue))w.emit({status:"setup-skipped",errorMsg:"replay queue already initialized."});else{var b,_;w.emit({status:"setup-started"}),window.Shopify=window.Shopify||{};var S=window.Shopify;S.analytics=S.analytics||{};var P=S.analytics;P.replayQueue=[],P.publish=function(e,t,i){return P.replayQueue.push([e,t,i]),!0};try{self.performance.mark("wpm:start")}catch(e){}var k,C="modern"===g?"modern":"legacy",E=(null!=l?l:{modern:"",legacy:""})[C],I=[(k={baseUrl:s,hashVersion:u,buildTarget:C}).baseUrl,"/wpm","/b",k.hashVersion,"modern"===k.buildTarget?"m":"l",".js"].join(""),O=!1,T=!1;try{c.browserTarget=g,function(){O=!0;try{window.addEventListener("pagehide",M)}catch(e){O=!1}}(),R(function(){try{R(x)}catch(e){D(e)}}),w.emit({status:"loading",requestId:m.requestId,navigationType:m.navigationType,trekkieState:a()})}catch(e){D(e)}}function U(){var e;if(q(),!function(){var e,t;return Boolean(null==(t=null==(e=window.Shopify)?void 0:e.analytics)?void 0:t.initialized)}()){var t,i,n;try{if("function"!=typeof(null==(e=window.webPixelsManager)?void 0:e.init))return void A();if(!(null!==(n=i=t=window.webPixelsManager.init(d))&&"function"!=typeof n&&Object(n)===n&&"publish"in i&&"function"==typeof i.publish&&"publishCustomEvent"in i&&"function"==typeof i.publishCustomEvent&&"visitor"in i&&"function"==typeof i.visitor))return void A()}catch(e){return void A(e)}var r=window.Shopify.analytics;r.replayQueue.forEach(function(e){var i=e[0],n=e[1],r=e[2];t.publishCustomEvent(i,n,r)}),r.replayQueue=[],r.publish=t.publishCustomEvent,r.visitor=t.visitor,r.initialized=!0}}function A(e){B("manager_api_unavailable",void 0===e?void 0:Q(e))}function x(){B("script_load_failed","".concat(I," has failed to load"))}function B(e,t){T||(T=!0,q(),w.emit({status:"failed",errorMsg:t,failureReason:e,requestId:m.requestId,navigationType:m.navigationType,trekkieState:a()}))}function M(e){O&&(q(),w.emit({status:"pagehide-while-loading",useKeepaliveFallback:!0,pagehidePersisted:e.persisted,requestId:m.requestId,navigationType:m.navigationType,trekkieState:a()}))}function q(){O=!1;try{window.removeEventListener("pagehide",M)}catch(e){}}function R(e){var t;!function(e){var t=e.src,i=e.async,n=void 0===i||i,r=e.onload,o=e.onerror,a=e.sri,d=e.scriptDataAttributes,s=void 0===d?{}:d,u=document.createElement("script"),l=document.querySelector("head"),c=document.querySelector("body");if(u.async=n,u.src=t,a&&(u.integrity=a,u.crossOrigin="anonymous"),s)for(var p in s)if(Object.prototype.hasOwnProperty.call(s,p))try{u.dataset[p]=String(s[p])}catch(e){}if(r&&u.addEventListener("load",r),o&&u.addEventListener("error",o),l)l.appendChild(u);else{if(!c)throw new Error("Did not find a head or body element to append the script");c.appendChild(u)}}({src:I,async:!0,onload:U,onerror:e,sri:(t=E,"string"==typeof t&&/^sha384-[A-Za-z0-9+/=]+$/.test(t)?E:""),scriptDataAttributes:c})}function D(e){B("script_append_failed",Q(e))}function Q(e){return e instanceof Error?e.message:"Unknown error"}}}();wpmLoader({shopId: 52960723110,storefrontBaseUrl: "https://www.bostonproper.com",extensionsBaseUrl: "https://extensions.shopifycdn.com/cdn/shopifycloud/web-pixels-manager",monorailEndpoint: "https://www.bostonproper.com/.well-known/shopify/monorail/unstable/produce_batch",surface: "storefront-renderer",enabledBetaFlags: ["4c64608a","16072cab","8450a54b","7ee89bf1"],webPixelsConfigList: [{"id":"3346137254","configuration":"{\"storeIdentity\":\"bostonproper.myshopify.com\",\"baseURL\":\"https:\\\/\\\/api.printful.com\\\/shopify-pixels\"}","eventPayloadVersion":"v1","runtimeContext":"STRICT","scriptVersion":"74f275712857ab41bea9d998dcb2f9da","type":"APP","apiClientId":156624,"privacyPurposes":["ANALYTICS","MARKETING","SALE_OF_DATA"],"dataSharingAdjustments":{"protectedCustomerApprovalScopes":["read_customer_address","read_customer_email","read_customer_name","read_customer_personal_data","read_customer_phone"],"dataSharingControls":["share_all_events"]},"dataSharingState":"optimized"},{"id":"3309076646","configuration":"{\"target\":\"https:\/\/us3-api.eng.bloomreach.com\", \"token\":\"discovery_pixel_token\", \"search_tracking\":\"true\", \"checkout_tracking\":\"false\", \"view_category_tracking\":\"true\", \"view_item_tracking\":\"true\", \"page_viewed_tracking\":\"false\", \"quick_view_tracking\":\"true\", \"suggest_tracking\":\"true\", \"cart_update_tracking\":\"true\", \"view_homepage_tracking\":\"true\", \"view_search_results_tracking\":\"true\", \"purchase_tracking\":\"true\", \"widget_view_tracking\":\"true\", \"widget_click_tracking\":\"true\", \"integration_id\":\"7fd16166-a982-463e-b56a-8e1f1c50ecd7\", \"gateway_url\":\"https:\/\/us3-api.eng.bloomreach.com\", \"domain\":\"bostonproper.myshopify.com\", \"stream_id\":\"d042b089-6e20-44b6-82b8-7bd75742c4a8\"}","eventPayloadVersion":"v1","runtimeContext":"STRICT","scriptVersion":"d45e93818bd39663ecc8d16ea4407a82","type":"APP","apiClientId":35679600641,"privacyPurposes":["ANALYTICS","MARKETING","SALE_OF_DATA"],"dataSharingAdjustments":{"protectedCustomerApprovalScopes":["read_customer_address","read_customer_email","read_customer_name","read_customer_personal_data","read_customer_phone"],"dataSharingControls":["share_all_events"]},"dataSharingState":"optimized"},{"id":"3154477222","configuration":"{\"storeHandle\":\"bostonproper.myshopify.com\", \"publisherId\": \"P7C25MF72\"}","eventPayloadVersion":"v1","runtimeContext":"STRICT","scriptVersion":"94a881e8d211e628cef2d7cbc6e68cf4","type":"APP","apiClientId":343457300481,"privacyPurposes":["ANALYTICS","MARKETING","SALE_OF_DATA"],"dataSharingAdjustments":{"protectedCustomerApprovalScopes":["read_customer_address","read_customer_email","read_customer_name","read_customer_phone","read_customer_personal_data"],"dataSharingControls":["share_all_events"]}
12037,"dataSharingState":"unrestricted"},{"id":"3038904486","configuration":"{\"storeKey\":\"bog\"}","eventPayloadVersion":"v1","runtimeContext":"STRICT","scriptVersion":"347d2fe4d0c5c4fe761e9f8699b67474","type":"APP","apiClientId":43378180097,"privacyPurposes":[],"dataSharingAdjustments":{"protectedCustomerApprovalScopes":["read_customer_email","read_customer_personal_data"],"dataSharingControls":["share_all_events"]},"dataSharingState":"unrestricted"},{"id":"2927460518","configuration":"{\"projectId\":\"v94zgzk99z\"}","eventPayloadVersion":"v1","runtimeContext":"STRICT","scriptVersion":"d8a2bb2ccbb513dc8e8b6937f4b8033e","type":"APP","apiClientId":240074326017,"privacyPurposes":[],"capabilities":["advanced_dom_events"],"dataSharingAdjustments":{"protectedCustomerApprovalScopes":["read_customer_personal_data"],"dataSharingControls":["share_all_events"]},"dataSharingState":"unrestricted"},{"id":"1857388710","configuration":"{\"pixelCode\":\"CMG5U8JC77UDUQCE3LVG\"}","eventPayloadVersion":"v1","runtimeContext":"STRICT","scriptVersion":"22e92c2ad45662f435e4801458fb78cc","type":"APP","apiClientId":4383523,"privacyPurposes":["ANALYTICS","MARKETING","SALE_OF_DATA"],"dataSharingAdjustments":{"protectedCustomerApprovalScopes":["read_customer_address","read_customer_email","read_customer_name","read_customer_personal_data","read_customer_phone"],"dataSharingControls":["share_all_events"]},"dataSharingState":"optimized"},{"id":"1787723942","configuration":"{\"eventKey\":\"219c8a5d-569a-4644-8a21-6d301b41f586\",\"name\":\"bostonproper - Axon Pixel\"}","eventPayloadVersion":"v1","runtimeContext":"STRICT","scriptVersion":"fae105ca09bb030f70a8b83292a88611","type":"APP","apiClientId":256560922625,"privacyPurposes":["ANALYTICS","MARKETING","SALE_OF_DATA"],"dataSharingAdjustments":{"protectedCustomerApprovalScopes":["read_customer_address","read_customer_email","read_customer_personal_data","read_customer_phone"],"dataSharingControls":["share_all_events"]},"dataSharingState":"optimized"},{"id":"1686995110","configuration":"{\"pixelId\":\"d5052b6d-6a66-4b65-bb5d-37f5d558bb94\"}","eventPayloadVersion":"v1","runtimeContext":"STRICT","scriptVersion":"c119f01612c13b62ab52809eb08154bb","type":"APP","apiClientId":2556259,"privacyPurposes":["ANALYTICS","MARKETING","SALE_OF_DATA"],"dataSharingAdjustments":{"protectedCustomerApprovalScopes":["read_customer_address","read_customer_email","read_customer_name","read_customer_personal_data","read_customer_phone"],"dataSharingControls":["share_all_events"]},"dataSharingState":"optimized"},{"id":"1653538982","configuration":"{\"accountID\":\"VZrTML\",\"webPixelConfig\":\"eyJlbmFibGVBZGRlZFRvQ2FydEV2ZW50cyI6IHRydWV9\"}","eventPayloadVersion":"v1","runtimeContext":"STRICT","scriptVersion":"c63c5b72c8d77be5f2d27d14c081da38","type":"APP","apiClientId":123074,"privacyPurposes":["ANALYTICS","MARKETING"],"dataSharingAdjustments":{"protectedCustomerApprovalScopes":["read_customer_address","read_customer_email","read_customer_name","read_customer_personal_data","read_customer_phone"],"dataSharingControls":["share_all_events"]},"dataSharingState":"optimized","enabledFlags":["9a3ed68a"]},{"id":"1598685350","configuration":"{\"vibePixelId\":\"Nh1Pwu\"}","eventPayloadVersion":"v1","runtimeContext":"STRICT","scriptVersion":"72859894ec8aa8d5e0246c40aaed9466","type":"APP","apiClientId":132266098689,"privacyPurposes":["ANALYTICS","MARKETING"],"dataSharingAdjustments":{"protectedCustomerApprovalScopes":[],"dataSharingControls":["share_all_events"]},"dataSharingState":"optimized"},{"id":"1383923878","configuration":"{\"isUserEvents\":\"enabled\",\"isSavePromiseDate\":\"enabled\"}","eventPayloadVersion":"v1","runtimeContext":"STRICT","scriptVersion":"6d0a6deccdda72f2ea2eba90a96ee634","type":"APP","apiClientId":1461275,"privacyPurposes":["ANALYTICS","PREFERENCES"],"dataSharingAdjustments":{"protectedCustomerApprovalScopes":["read_customer_address","read_customer_email","read_customer_name","read_customer_personal_data","read_customer_phone"],"dataSharingControls":["share_all_events"]},"dataSharingState":"unrestricted"},{"id":"1312194726","configuration":"{\"cart_viewed\":\"true\",\"checkout_address_info_submitted\":\"true\",\"checkout_completed\":\"true\",\"checkout_contact_info_submitted\":\"true\",\"checkout_shipping_info_submitted\":\"true\",\"checkout_started\":\"true\",\"collection_viewed\":\"true\",\"page_viewed\":\"true\",\"payment_info_submitted\":\"true\",\"product_added_to_cart\":\"true\",\"product_removed_from_cart\":\"true\",\"product_viewed\":\"true\",\"search_submitted\":\"true\"}","eventPayloadVersion":"v1","runtimeContext":"STRICT","scriptVersion":"4b912d4db8d67578541bfae8b70a40a9","type":"APP","apiClientId":100317265921,"privacyPurposes":["ANALYTICS"],"dataSharingAdjustments":{"protectedCustomerApprovalScopes":[],"dataSharingControls":["share_all_events"]}
12037,"dataSharingState":"unrestricted"},{"id":"1013219494","configuration":"{\"yotpoStoreId\":\"l9OjGj5YqKVR9PFonEHJc4ms9HXq9RV8dOWnI8LL\"}","eventPayloadVersion":"v1","runtimeContext":"STRICT","scriptVersion":"c5c2579aac465eb24ca7bc0501a6000d","type":"APP","apiClientId":70132,"privacyPurposes":["ANALYTICS","MARKETING","SALE_OF_DATA"],"dataSharingAdjustments":{"protectedCustomerApprovalScopes":["read_customer_address","read_customer_email","read_customer_name","read_customer_personal_data","read_customer_phone"],"dataSharingControls":["share_all_events"]},"dataSharingState":"optimized"},{"id":"900497574","configuration":"{\"myshopifyDomain\":\"bostonproper.myshopify.com\"}","eventPayloadVersion":"v1","runtimeContext":"STRICT","scriptVersion":"23b97d18e2aa74363140dc29c9284e87","type":"APP","apiClientId":2775569,"privacyPurposes":["ANALYTICS","MARKETING","SALE_OF_DATA"],"dataSharingAdjustments":{"protectedCustomerApprovalScopes":["read_customer_address","read_customer_email","read_customer_name","read_customer_phone","read_customer_personal_data"],"dataSharingControls":["share_all_events"]},"dataSharingState":"optimized"},{"id":"159482022","configuration":"{\"partnerId\": \"51328\", \"environment\": \"prod\", \"countryCode\": \"US\", \"logLevel\": \"debug\"}","eventPayloadVersion":"v1","runtimeContext":"STRICT","scriptVersion":"e39367b785b1d63117e387ccb6b0db54","type":"APP","apiClientId":5829751,"privacyPurposes":["ANALYTICS","MARKETING","SALE_OF_DATA"],"dataSharingAdjustments":{"protectedCustomerApprovalScopes":["read_customer_email","read_customer_personal_data"],"dataSharingControls":["share_all_events"]},"dataSharingState":"optimized","enabledFlags":["9a3ed68a"]},{"id":"112263334","configuration":"{\"tagID\":\"2613459126604\"}","eventPayloadVersion":"v1","runtimeContext":"STRICT","scriptVersion":"7ae565e198624b39e0856d352b9a6b4c","type":"APP","apiClientId":3009811,"privacyPurposes":["ANALYTICS","MARKETING","SALE_OF_DATA"],"dataSharingAdjustments":{"protectedCustomerApprovalScopes":["read_customer_address","read_customer_email","read_customer_name","read_customer_personal_data","read_customer_phone"],"dataSharingControls":["share_all_events"]},"dataSharingState":"optimized"},{"id":"26443942","configuration":"{ \"bundleName\": \"bostonproper\" }","eventPayloadVersion":"v1","runtimeContext":"STRICT","scriptVersion":"6d69af31d151d9c0dc4842dc2c8531ca","type":"APP","apiClientId":5106349,"privacyPurposes":["ANALYTICS","MARKETING","SALE_OF_DATA"],"dataSharingAdjustments":{"protectedCustomerApprovalScopes":["read_customer_address","read_customer_email","read_customer_name","read_customer_personal_data","read_customer_phone"],"dataSharingControls":["share_all_events"]},"dataSharingState":"optimized"},{"id":"13041830","configuration":"{\"northbeamId\": \"bad8c33b-2e51-4306-b610-a4bff168053e\", \"collectorDomain\": \"i.bostonproper.com\"}","eventPayloadVersion":"v1","runtimeContext":"STRICT","scriptVersion":"e46c2d6352a678fc6581f4bd0bc772e1","type":"APP","apiClientId":3188721,"privacyPurposes":["ANALYTICS","MARKETING","SALE_OF_DATA"],"dataSharingAdjustments":{"protectedCustomerApprovalScopes":["read_customer_address","read_customer_email","read_customer_name","read_customer_personal_data","read_customer_phone"],"dataSharingControls":["share_all_events"]},"dataSharingState":"optimized"},{"id":"24903846","eventPayloadVersion":"1","runtimeContext":"LAX","scriptVersion":"2","type":"CUSTOM","privacyPurposes":["ANALYTICS","MARKETING","SALE_OF_DATA"],"name":"Merkle \/ 4Cite tracking pixel"},{"id":"42565798","eventPayloadVersion":"1","runtimeContext":"LAX","scriptVersion":"1","type":"CUSTOM","privacyPurposes":["ANALYTICS","MARKETING","SALE_OF_DATA"],"name":"Klaviyo Tracking"},{"id":"43909286","eventPayloadVersion":"1","runtimeContext":"LAX","scriptVersion":"2","type":"CUSTOM","privacyPurposes":["ANALYTICS","MARKETING","SALE_OF_DATA"],"name":"Ascend"},{"id":"130089126","eventPayloadVersion":"1","runtimeContext":"LAX","scriptVersion":"1","type":"CUSTOM","privacyPurposes":[],"name":"Postcard-Conversion-Pixel"},{"id":"142573734","eventPayloadVersion":"1","runtimeContext":"LAX","scriptVersion":"1","type":"CUSTOM","privacyPurposes":["ANALYTICS","MARKETING","SALE_OF_DATA"],"name":"Northbeam Checkout Events"}
12037,{"id":"149815462","eventPayloadVersion":"1","runtimeContext":"LAX","scriptVersion":"5","type":"CUSTOM","privacyPurposes":[],"name":"GTM \/ GA4 DataLayer"},{"id":"149848230","eventPayloadVersion":"1","runtimeContext":"LAX","scriptVersion":"1","type":"CUSTOM","privacyPurposes":["ANALYTICS","MARKETING","SALE_OF_DATA"],"name":"Publitas"},{"id":"149913766","eventPayloadVersion":"1","runtimeContext":"LAX","scriptVersion":"1","type":"CUSTOM","privacyPurposes":["ANALYTICS","MARKETING","SALE_OF_DATA"],"name":"Graphene"},{"id":"149946534","eventPayloadVersion":"1","runtimeContext":"LAX","scriptVersion":"1","type":"CUSTOM","privacyPurposes":["ANALYTICS","MARKETING","SALE_OF_DATA"],"name":"Stylitics"},{"id":"155549862","eventPayloadVersion":"1","runtimeContext":"LAX","scriptVersion":"1","type":"CUSTOM","privacyPurposes":["ANALYTICS","MARKETING","SALE_OF_DATA"],"name":"Fit Analytics V2"},{"id":"shopify-app-pixel","configuration":"{}","eventPayloadVersion":"v1","runtimeContext":"STRICT","scriptVersion":"0530","apiClientId":"shopify-pixel","type":"APP","privacyPurposes":["ANALYTICS","MARKETING"]},{"id":"shopify-custom-pixel","eventPayloadVersion":"v1","runtimeContext":"LAX","scriptVersion":"0530","apiClientId":"shopify-pixel","type":"CUSTOM","privacyPurposes":["ANALYTICS","MARKETING"]}],isMerchantRequest: false,initData: {"shop":{"name":"Boston Proper","paymentSettings":{"currencyCode":"USD"},"myshopifyDomain":"bostonproper.myshopify.com","countryCode":"US","storefrontUrl":"https:\/\/www.bostonproper.com"},"customer":null,"cart":null,"checkout":null,"productVariants":[{"price":{"amount":50.0,"currencyCode":"USD"},"product":{"title":"Boston Proper Printed Scarf With Love BP","vendor":"Boston Proper","id":"15286962618534","untranslatedTitle":"Boston Proper Printed Scarf With Love BP","url":"\/products\/boston-proper-printed-scarf-with-love-bp","type":"Promotions"},"id":"56389031657638","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2CT8071_SET_e6f3e3ce-5a5f-451b-bc3b-ea1f8f71d236.jpg?v=1791009198"},"sku":"2CT8071000","title":"With Love BP \/ One Size","untranslatedTitle":"With Love BP \/ One Size"}],"products":[{"id":"15286962618534","handle":"boston-proper-printed-scarf-with-love-bp","isCollective":false,"title":"Boston Proper Printed Scarf With Love BP","type":"Promotions","untranslatedTitle":"Boston Proper Printed Scarf With Love BP","url":"\/products\/boston-proper-printed-scarf-with-love-bp","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"56389031657638","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2CT8071_SET_e6f3e3ce-5a5f-451b-bc3b-ea1f8f71d236.jpg?v=1791009198"},"price":{"amount":50.0,"currencyCode":"USD"},"sku":"2CT8071000","title":"With Love BP \/ One Size","untranslatedTitle":"With Love BP \/ One Size"}]},{"id":"15202416099494","handle":"boston-proper-printed-scarf-americana-bandana","isCollective":false,"title":"Boston Proper Printed Scarf Americana Bandana","type":"Promotions","untranslatedTitle":"Boston Proper Printed Scarf Americana Bandana","url":"\/products\/boston-proper-printed-scarf-americana-bandana","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"56032129351846","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2CT8031_SET.jpg?v=1791009109"},"price":{"amount":19.98,"currencyCode":"USD"},"sku":"2CT8031000","title":"Americana Bandana \/ One Size","untranslatedTitle":"Americana Bandana \/ One Size"}]},{"id":"8358210928806","handle":"boston-proper-printed-scarf-florence-fleur","isCollective":false,"title":"Boston Proper Printed Scarf Florence Fleur","type":"Promotions","untranslatedTitle":"Boston Proper Printed Scarf Florence Fleur","url":"\/products\/boston-proper-printed-scarf-florence-fleur","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"45993381560486","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2CT8091_SET.jpg?v=1791008657"},"price":{"amount":50.0,"currencyCode":"USD"},"sku":"2CT8091000","title":"Florence Fleur \/ One Size","untranslatedTitle":"Florence Fleur \/ One Size"}]},{"id":"8464862806182","handle":"boston-proper-printed-scarf-flowers-of-hope","isCollective":false,"title":"Boston Proper Printed Scarf Flowers of Hope","type":"Promotions","untranslatedTitle":"Boston Proper Printed Scarf Flowers of Hope","url":"\/products\/boston-proper-printed-scarf-flowers-of-hope","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"46302477189286","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2CT8070_SET_491d3f8a-0135-4d4a-b984-7131fa65c497.jpg?v=1791008846"},"price":{"amount":50.0,"currencyCode":"USD"},"sku":"2CT8070000","title":"Flowers of Hope \/ One Size","untranslatedTitle":"Flowers of Hope \/ One Size"}]},{"id":"8358199918758","handle":"boston-proper-printed-scarf-iris-garden","isCollective":false,"title":"Boston Proper Printed Scarf Iris Garden","type":"Promotions","untranslatedTitle":"Boston Proper Printed Scarf Iris Garden","url":"\/products\/boston-proper-printed-scarf-iris-garden","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"45993311436966","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2CT8090_SET.jpg?v=1791008608"},"price":{"amount":50.0,"currencyCode":"USD"},"sku":"2CT8090000","title":"Iris Garden \/ One Size","untranslatedTitle":"Iris Garden \/ One Size"}]},{"id":"8595151159462","handle":"boston-proper-printed-scarf-party-polka","isCollective":false,"title":"Boston Proper Printed Scarf Party Polka","type":"Promotions","untranslatedTitle":"Boston Proper Printed Scarf Party Polka","url":"\/products\/boston-proper-printed-scarf-party-polka","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"46716370288806","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2CT8020_SET_dc17f3cc-0922-44e3-b186-0430474125fb.jpg?v=1791009015"},"price":{"amount":50.0,"currencyCode":"USD"},"sku":"2CT8020000","title":"Party Polka \/ One Size","untranslatedTitle":"Party Polka \/ One Size"}]},{"id":"8622122041510","handle":"boston-proper-printed-scarf-stunning-sealife","isCollective":false,"title":"Boston Proper Printed Scarf Stunning Sealife","type":"Promotions","untranslatedTitle":"Boston Proper Printed Scarf Stunning Sealife","url":"\/products\/boston-proper-printed-scarf-stunning-sealife","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"46789620039846","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2CT8030_SET.jpg?v=1791009065"},"price":{"amount":50.0,"currencyCode":"USD"},"sku":"2CT8030000","title":"Stunning Sealife \/ One Size","untranslatedTitle":"Stunning Sealife \/ One Size"}]}
12037,{"id":"15399681392806","handle":"leopard-print-skirt-brown-multi","isCollective":false,"title":"Leopard Print Skirt Brown Multi","type":"Skirts","untranslatedTitle":"Leopard Print Skirt Brown Multi","url":"\/products\/leopard-print-skirt-brown-multi","vendor":"Bagatelle","remoteShopId":null,"variants":[{"id":"56779671077030","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DO3390_SET.jpg?v=1791009176"},"price":{"amount":110.0,"currencyCode":"USD"},"sku":"2DO3390800","title":"Brown Multi \/ L","untranslatedTitle":"Brown Multi \/ L"}]},{"id":"15399681360038","handle":"metallic-zebra-printed-dress-tan-multi","isCollective":false,"title":"Metallic Zebra Printed Dress Tan Multi","type":"Dresses","untranslatedTitle":"Metallic Zebra Printed Dress Tan Multi","url":"\/products\/metallic-zebra-printed-dress-tan-multi","vendor":"Bagatelle","remoteShopId":null,"variants":[{"id":"56779670782118","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DO0590_SET.jpg?v=1791009160"},"price":{"amount":180.0,"currencyCode":"USD"},"sku":"2DO0590006","title":"Tan Multi \/ 6","untranslatedTitle":"Tan Multi \/ 6"}]},{"id":"15399680114854","handle":"sam-edelman-fur-lace-up-bootie-tan","isCollective":false,"title":"Sam Edelman Fur Lace Up Bootie Tan","type":"Shoes","untranslatedTitle":"Sam Edelman Fur Lace Up Bootie Tan","url":"\/products\/sam-edelman-fur-lace-up-bootie-tan","vendor":"Sam Edelman","remoteShopId":null,"variants":[{"id":"56779666161830","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DO5680_SET.jpg?v=1791009185"},"price":{"amount":180.0,"currencyCode":"USD"},"sku":"2DO5680060","title":"Tan \/ 6.0","untranslatedTitle":"Tan \/ 6.0"}]},{"id":"15399680082086","handle":"ultimate-velvet-double-breasted-shaped-blazer-black","isCollective":false,"title":"Ultimate Velvet Double Breasted Shaped Blazer Black","type":"Jackets","untranslatedTitle":"Ultimate Velvet Double Breasted Shaped Blazer Black","url":"\/products\/ultimate-velvet-double-breasted-shaped-blazer-black","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"56779665801382","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DP1920_SET.jpg?v=1791009211"},"price":{"amount":150.0,"currencyCode":"USD"},"sku":"2DP1920002","title":"Black \/ 2","untranslatedTitle":"Black \/ 2"}]},{"id":"15371225596070","handle":"breast-cancer-awareness-graphic-sweatshirt-azalea","isCollective":false,"title":"Breast Cancer Awareness Graphic Sweatshirt Azalea","type":"Athleisure","untranslatedTitle":"Breast Cancer Awareness Graphic Sweatshirt Azalea","url":"\/products\/breast-cancer-awareness-graphic-sweatshirt-azalea","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"56671921471654","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DR2070_SET.jpg?v=1791009256"},"price":{"amount":60.0,"currencyCode":"USD"},"sku":"2DR2070700","title":"Azalea \/ M","untranslatedTitle":"Azalea \/ M"}]},{"id":"15366923452582","handle":"mock-neck-intarsia-flare-sleeve-tunic-west-side-paisley","isCollective":false,"title":"Mock Neck Intarsia Flare Sleeve Tunic West Side Paisley","type":"Sweater Tops","untranslatedTitle":"Mock Neck Intarsia Flare Sleeve Tunic West Side Paisley","url":"\/products\/mock-neck-intarsia-flare-sleeve-tunic-west-side-paisley","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"56658234933414","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DM9540_SET.jpg?v=1791009180"},"price":{"amount":100.0,"currencyCode":"USD"},"sku":"2DM9540400","title":"West Side Paisley \/ XXS","untranslatedTitle":"West Side Paisley \/ XXS"}]},{"id":"15364508745894","handle":"bcbgeneration-hardware-strap-to-the-knee-boot-cognac","isCollective":false,"title":"BCBGeneration Hardware Strap To The Knee Boot Cognac","type":"Shoes","untranslatedTitle":"BCBGeneration Hardware Strap To The Knee Boot Cognac","url":"\/products\/bcbgeneration-hardware-strap-to-the-knee-boot-cognac","vendor":"BCBG","remoteShopId":null,"variants":[{"id":"56648379236518","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DO6680_SET_538a0547-6fc7-4be8-8b60-4ea8b9720f2e.jpg?v=1791009184"},"price":{"amount":89.0,"currencyCode":"USD"},"sku":"2DO6680060","title":"Cognac \/ 6.0","untranslatedTitle":"Cognac \/ 6.0"}]}
12037,{"id":"15363223355558","handle":"high-waisted-sport-legging-cherry-lacquer","isCollective":false,"title":"High Waisted Sport Legging Cherry Lacquer","type":"Athleisure","untranslatedTitle":"High Waisted Sport Legging Cherry Lacquer","url":"\/products\/high-waisted-sport-legging-cherry-lacquer","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"56643511681190","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DB3271_SET.jpg?v=1791009254"},"price":{"amount":50.0,"currencyCode":"USD"},"sku":"2DB3271500","title":"Cherry Lacquer \/ XS","untranslatedTitle":"Cherry Lacquer \/ XS"}]},{"id":"15342913847462","handle":"herringbone-double-breasted-blazer-with-rhinestone-embellishment-gold-gray-multi","isCollective":false,"title":"Herringbone Double Breasted Blazer With Rhinestone Embellishment Gold\/Gray Multi","type":"Jackets","untranslatedTitle":"Herringbone Double Breasted Blazer With Rhinestone Embellishment Gold\/Gray Multi","url":"\/products\/herringbone-double-breasted-blazer-with-rhinestone-embellishment-gold-gray-multi","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"56564923891878","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DP1690_SET.jpg?v=1791009251"},"price":{"amount":250.0,"currencyCode":"USD"},"sku":"2DP1690006","title":"Gold\/Gray Multi \/ 6","untranslatedTitle":"Gold\/Gray Multi \/ 6"}]},{"id":"15336227864742","handle":"knit-suede-drape-front-jacket-stone","isCollective":false,"title":"Knit Suede Drape Front Jacket Stone","type":"Jackets","untranslatedTitle":"Knit Suede Drape Front Jacket Stone","url":"\/products\/knit-suede-drape-front-jacket-stone","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"56545549123750","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DR1780_SET.jpg?v=1791009250"},"price":{"amount":125.0,"currencyCode":"USD"},"sku":"2DR1780400","title":"Stone \/ XXS","untranslatedTitle":"Stone \/ XXS"}]},{"id":"15336227831974","handle":"knit-suede-drape-front-jacket-blue","isCollective":false,"title":"Knit Suede Drape Front Jacket Blue","type":"Jackets","untranslatedTitle":"Knit Suede Drape Front Jacket Blue","url":"\/products\/knit-suede-drape-front-jacket-blue","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"56545548927142","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DR1730_SET_9fea2518-0e42-496d-aca4-19f54a5af591.jpg?v=1791009249"},"price":{"amount":125.0,"currencyCode":"USD"},"sku":"2DR1730400","title":"Blue \/ XXS","untranslatedTitle":"Blue \/ XXS"}]},{"id":"15334258868390","handle":"half-zip-sparkle-snowflake-sweater-french-kiss","isCollective":false,"title":"Half Zip Sparkle Snowflake Sweater French Kiss","type":"Sweater Tops","untranslatedTitle":"Half Zip Sparkle Snowflake Sweater French Kiss","url":"\/products\/half-zip-sparkle-snowflake-sweater-french-kiss","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"56539690696870","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DQ0611_SET.jpg?v=1791009258"},"price":{"amount":100.0,"currencyCode":"USD"},"sku":"2DQ0611500","title":"French Kiss \/ XS","untranslatedTitle":"French Kiss \/ XS"}]},{"id":"15321518833830","handle":"velvet-sequin-strapless-gown-with-scarf-emerald","isCollective":false,"title":"Velvet Sequin Strapless Gown with Scarf Emerald","type":"Dresses","untranslatedTitle":"Velvet Sequin Strapless Gown with Scarf Emerald","url":"\/products\/velvet-sequin-strapless-gown-with-scarf-emerald","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"56499191120038","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DR1460_SET_2cbf8591-5dcd-479c-845e-834d7eb0cb64.jpg?v=1791009253"},"price":{"amount":220.0,"currencyCode":"USD"},"sku":"2DR1460400","title":"Emerald \/ XXS","untranslatedTitle":"Emerald \/ XXS"}]},{"id":"15317730918566","handle":"faux-fur-tote-bag-multi","isCollective":false,"title":"Faux Fur Tote Bag Multi","type":"Accessories","untranslatedTitle":"Faux Fur Tote Bag Multi","url":"\/products\/faux-fur-tote-bag-multi","vendor":"Daisy Rose","remoteShopId":null,"variants":[{"id":"56483682812070","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DR0290_SET.jpg?v=1791009247"},"price":{"amount":95.0,"currencyCode":"USD"},"sku":"2DR0290000","title":"Multi \/ One Size","untranslatedTitle":"Multi \/ One Size"}]}
12037,{"id":"15314674811046","handle":"ultimate-ponte-back-zip-skirt-proper-black","isCollective":false,"title":"Ultimate Ponte Back Zip Skirt Proper Black","type":"Skirts","untranslatedTitle":"Ultimate Ponte Back Zip Skirt Proper Black","url":"\/products\/ultimate-ponte-back-zip-skirt-proper-black","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"56473775669414","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DN1520_SET_414c1527-6223-4932-ab01-9883dffc85b9.jpg?v=1791009230"},"price":{"amount":75.0,"currencyCode":"USD"},"sku":"2DN1520400","title":"Proper Black \/ XXS","untranslatedTitle":"Proper Black \/ XXS"}]},{"id":"15314674253990","handle":"printed-multi-ruffle-v-neck-blouse-leopard","isCollective":false,"title":"Printed Multi Ruffle V Neck Blouse Leopard","type":"Woven Tops","untranslatedTitle":"Printed Multi Ruffle V Neck Blouse Leopard","url":"\/products\/printed-multi-ruffle-v-neck-blouse-leopard","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"56473771278502","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DQ6190_SET.jpg?v=1791009243"},"price":{"amount":100.0,"currencyCode":"USD"},"sku":"2DQ6190400","title":"Leopard \/ XXS","untranslatedTitle":"Leopard \/ XXS"}]},{"id":"15314673795238","handle":"belted-trench-luxe-wool-coat-chocolate-torte","isCollective":false,"title":"Belted Trench Luxe Wool Coat Chocolate Torte","type":"Outerwear","untranslatedTitle":"Belted Trench Luxe Wool Coat Chocolate Torte","url":"\/products\/belted-trench-luxe-wool-coat-chocolate-torte","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"56473768427686","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DP6680_SET_69366f09-7a73-492d-922f-e6e6a7f5d5f1.jpg?v=1791009236"},"price":{"amount":200.0,"currencyCode":"USD"},"sku":"2DP6680400","title":"Chocolate Torte \/ XXS","untranslatedTitle":"Chocolate Torte \/ XXS"}]},{"id":"15312523722918","handle":"cozy-faux-fur-slide-dove-grey","isCollective":false,"title":"Cozy Faux Fur Slide Dove Grey","type":"Shoes","untranslatedTitle":"Cozy Faux Fur Slide Dove Grey","url":"\/products\/cozy-faux-fur-slide-dove-grey","vendor":"Matisse","remoteShopId":null,"variants":[{"id":"56470391881894","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DO5230_SET_6e2012f3-5f8c-4ecb-a141-34579cc2000c.jpg?v=1791009236"},"price":{"amount":65.0,"currencyCode":"USD"},"sku":"2DO5230060","title":"Dove Grey \/ 6.0","untranslatedTitle":"Dove Grey \/ 6.0"}]},{"id":"15311695413414","handle":"monterey-high-rise-faux-leather-trouser-gunmetal","isCollective":false,"title":"Monterey High Rise Faux Leather Trouser Gunmetal","type":"Pants","untranslatedTitle":"Monterey High Rise Faux Leather Trouser Gunmetal","url":"\/products\/monterey-high-rise-faux-leather-trouser-gunmetal","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"56468015808678","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DN3090_SET.jpg?v=1791009234"},"price":{"amount":110.0,"currencyCode":"USD"},"sku":"2DN3090000","title":"Gunmetal \/ 0","untranslatedTitle":"Gunmetal \/ 0"}]},{"id":"15311695282342","handle":"beyond-travel-tunic-coordinate-set-jet-black","isCollective":false,"title":"Beyond Travel Tunic Coordinate Set Jet Black","type":"Athleisure","untranslatedTitle":"Beyond Travel Tunic Coordinate Set Jet Black","url":"\/products\/beyond-travel-tunic-coordinate-set-jet-black","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"56468015022246","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DA2820_SET.jpg?v=1791009229"},"price":{"amount":150.0,"currencyCode":"USD"},"sku":"2DA2820400","title":"Jet Black \/ XXS","untranslatedTitle":"Jet Black \/ XXS"}]},{"id":"15309443694758","handle":"crystal-embellished-faux-fur-chubby-hunt-club","isCollective":false,"title":"Crystal Embellished Faux Fur Chubby Hunt Club","type":"Outerwear","untranslatedTitle":"Crystal Embellished Faux Fur Chubby Hunt Club","url":"\/products\/crystal-embellished-faux-fur-chubby-hunt-club","vendor":"Dolce Cabo","remoteShopId":null,"variants":[{"id":"56461571424422","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DP9360_SET.jpg?v=1791009227"},"price":{"amount":190.0,"currencyCode":"USD"},"sku":"2DP9360500","title":"Hunt Club \/ XS","untranslatedTitle":"Hunt Club \/ XS"}]}
12037,{"id":"15309443661990","handle":"velvet-scoop-neck-tank-black","isCollective":false,"title":"Velvet Scoop Neck Tank Black","type":"Knit Tops","untranslatedTitle":"Velvet Scoop Neck Tank Black","url":"\/products\/velvet-scoop-neck-tank-black","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"56461571195046","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DP5920_SET_138cab40-f130-435d-9513-06df8f4abe2b.jpg?v=1791009226"},"price":{"amount":50.0,"currencyCode":"USD"},"sku":"2DP5920400","title":"Black \/ XXS","untranslatedTitle":"Black \/ XXS"}]},{"id":"15309443629222","handle":"embellished-cable-turtleneck-sweater-tank-airy-blue","isCollective":false,"title":"Embellished Cable Turtleneck Sweater Tank Airy Blue","type":"Sweater Tops","untranslatedTitle":"Embellished Cable Turtleneck Sweater Tank Airy Blue","url":"\/products\/embellished-cable-turtleneck-sweater-tank-airy-blue","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"56461570998438","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2CP9531_SET.jpg?v=1791009225"},"price":{"amount":95.0,"currencyCode":"USD"},"sku":"2CP9531400","title":"Airy Blue \/ XXS","untranslatedTitle":"Airy Blue \/ XXS"}]},{"id":"15309443596454","handle":"faux-fur-embellished-cable-duster-airy-blue","isCollective":false,"title":"Faux Fur Embellished Cable Duster Airy Blue","type":"Sweater Tops","untranslatedTitle":"Faux Fur Embellished Cable Duster Airy Blue","url":"\/products\/faux-fur-embellished-cable-duster-airy-blue","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"56461570801830","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2CQ1531_SET_ecd9640b-58df-465f-92d6-45d3dce125bc.jpg?v=1791009224"},"price":{"amount":190.0,"currencyCode":"USD"},"sku":"2CQ1531400","title":"Airy Blue \/ XXS","untranslatedTitle":"Airy Blue \/ XXS"}]},{"id":"15309443563686","handle":"scoop-neck-balloon-sleeve-sweater-dress-airy-blue","isCollective":false,"title":"Scoop Neck Balloon Sleeve Sweater Dress Airy Blue","type":"Dresses","untranslatedTitle":"Scoop Neck Balloon Sleeve Sweater Dress Airy Blue","url":"\/products\/scoop-neck-balloon-sleeve-sweater-dress-airy-blue","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"56461570605222","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2CG7532_SET_e76ef1c1-7fac-44ba-8a01-76aa20799aaa.jpg?v=1791009223"},"price":{"amount":120.0,"currencyCode":"USD"},"sku":"2CG7532700","title":"Airy Blue \/ M","untranslatedTitle":"Airy Blue \/ M"}]},{"id":"15300355522726","handle":"boucle-yarn-slouchy-sweater-ballerina","isCollective":false,"title":"Boucle Yarn Slouchy Sweater Ballerina","type":"Sweater Tops","untranslatedTitle":"Boucle Yarn Slouchy Sweater Ballerina","url":"\/products\/boucle-yarn-slouchy-sweater-ballerina","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"56437744074918","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DP7170_SET.jpg?v=1791009221"},"price":{"amount":75.0,"currencyCode":"USD"},"sku":"2DP7170400","title":"Ballerina \/ XXS","untranslatedTitle":"Ballerina \/ XXS"}]},{"id":"15300355489958","handle":"boucle-yarn-slouchy-sweater-off-white","isCollective":false,"title":"Boucle Yarn Slouchy Sweater Off White","type":"Sweater Tops","untranslatedTitle":"Boucle Yarn Slouchy Sweater Off White","url":"\/products\/boucle-yarn-slouchy-sweater-off-white","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"56437743878310","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DP7140_SET.jpg?v=1791009220"},"price":{"amount":75.0,"currencyCode":"USD"},"sku":"2DP7140400","title":"Off White \/ XXS","untranslatedTitle":"Off White \/ XXS"}]},{"id":"15298631663782","handle":"rhinestone-and-chain-statement-necklace-silver","isCollective":false,"title":"Rhinestone and Chain Statement Necklace Silver","type":"Accessories","untranslatedTitle":"Rhinestone and Chain Statement Necklace Silver","url":"\/products\/rhinestone-and-chain-statement-necklace-silver","vendor":"Noir Jewelry","remoteShopId":null,"variants":[{"id":"56433081352358","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DQ2390_SET.jpg?v=1791009231"},"price":{"amount":90.0,"currencyCode":"USD"},"sku":"2DQ2390000","title":"Silver \/ One Size","untranslatedTitle":"Silver \/ One Size"}]}
12037,{"id":"15298232746150","handle":"stone-detail-chain-belt-gold","isCollective":false,"title":"Stone Detail Chain Belt Gold","type":"Accessories","untranslatedTitle":"Stone Detail Chain Belt Gold","url":"\/products\/stone-detail-chain-belt-gold","vendor":"Noir Jewelry","remoteShopId":null,"variants":[{"id":"56430366195878","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DQ2250_SET.jpg?v=1791009228"},"price":{"amount":100.0,"currencyCode":"USD"},"sku":"2DQ2250600","title":"Gold \/ S","untranslatedTitle":"Gold \/ S"}]},{"id":"15297645117606","handle":"monterey-luxe-metallic-embroidered-bootcut-jean-proper-black","isCollective":false,"title":"Monterey Luxe Metallic Embroidered Bootcut Jean Proper Black","type":"Jeans","untranslatedTitle":"Monterey Luxe Metallic Embroidered Bootcut Jean Proper Black","url":"\/products\/monterey-luxe-metallic-embroidered-bootcut-jean-proper-black","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"56425487270054","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DQ2020_SET_933e8aa4-ac0f-4bb2-8b47-093904f21fed.jpg?v=1791009226"},"price":{"amount":140.0,"currencyCode":"USD"},"sku":"2DQ2020004","title":"Proper Black \/ 4","untranslatedTitle":"Proper Black \/ 4"}]},{"id":"15295577850022","handle":"charmeuse-ruffle-collar-long-sleeve-blouse-proper-black","isCollective":false,"title":"Charmeuse Ruffle Collar Long Sleeve Blouse Proper Black","type":"Woven Tops","untranslatedTitle":"Charmeuse Ruffle Collar Long Sleeve Blouse Proper Black","url":"\/products\/charmeuse-ruffle-collar-long-sleeve-blouse-proper-black","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"56414501273766","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DP2020_SET_002f3099-0a4b-44ea-989c-a016142b9e8f.jpg?v=1791009225"},"price":{"amount":80.0,"currencyCode":"USD"},"sku":"2DP2020400","title":"Proper Black \/ XXS","untranslatedTitle":"Proper Black \/ XXS"}]},{"id":"15295577817254","handle":"charmeuse-ruffle-collar-long-sleeve-blouse-hunt-club","isCollective":false,"title":"Charmeuse Ruffle Collar Long Sleeve Blouse Hunt Club","type":"Woven Tops","untranslatedTitle":"Charmeuse Ruffle Collar Long Sleeve Blouse Hunt Club","url":"\/products\/charmeuse-ruffle-collar-long-sleeve-blouse-hunt-club","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"56414501077158","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DP2060_SET.jpg?v=1791009223"},"price":{"amount":80.0,"currencyCode":"USD"},"sku":"2DP2060400","title":"Hunt Club \/ XXS","untranslatedTitle":"Hunt Club \/ XXS"}]},{"id":"15293973856422","handle":"marilyn-cowl-neck-charmeuse-blouse-french-kiss","isCollective":false,"title":"Marilyn Cowl-Neck Charmeuse Blouse French Kiss","type":"Woven Tops","untranslatedTitle":"Marilyn Cowl-Neck Charmeuse Blouse French Kiss","url":"\/products\/marilyn-cowl-neck-charmeuse-blouse-french-kiss","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"56408288198822","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2BK4516_SET.jpg?v=1791009189"},"price":{"amount":70.0,"currencyCode":"USD"},"sku":"2BK4516400","title":"French Kiss \/ XXS","untranslatedTitle":"French Kiss \/ XXS"}]},{"id":"15292963324070","handle":"printed-lurex-lace-up-tunic-golden-status","isCollective":false,"title":"Printed Lurex Lace Up Tunic Golden Status","type":"Sweater Tops","untranslatedTitle":"Printed Lurex Lace Up Tunic Golden Status","url":"\/products\/printed-lurex-lace-up-tunic-golden-status","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"56404613333158","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DQ1980_SET_591f4bf8-5eaf-4477-8dd5-af53f3f7528f.jpg?v=1791009222"},"price":{"amount":110.0,"currencyCode":"USD"},"sku":"2DQ1980400","title":"Golden Status \/ XXS","untranslatedTitle":"Golden Status \/ XXS"}]},{"id":"15292963291302","handle":"stretch-velvet-puff-sleeve-trucker-champagne","isCollective":false,"title":"Stretch Velvet Puff Sleeve Trucker Champagne","type":"Jackets","untranslatedTitle":"Stretch Velvet Puff Sleeve Trucker Champagne","url":"\/products\/stretch-velvet-puff-sleeve-trucker-champagne","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"56404613136550","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DQ0440_SET.jpg?v=1791009221"},"price":{"amount":150.0,"currencyCode":"USD"},"sku":"2DQ0440400","title":"Champagne \/ XXS","untranslatedTitle":"Champagne \/ XXS"}]}
12037,{"id":"15291328692390","handle":"sophia-pearl-embellished-charmeuse-shirt-proper-black","isCollective":false,"title":"Sophia Pearl Embellished Charmeuse Shirt Proper Black","type":"Woven Tops","untranslatedTitle":"Sophia Pearl Embellished Charmeuse Shirt Proper Black","url":"\/products\/sophia-pearl-embellished-charmeuse-shirt-proper-black","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"56400671244454","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DP7220_SET.jpg?v=1791009219"},"price":{"amount":100.0,"currencyCode":"USD"},"sku":"2DP7220000","title":"Proper Black \/ 0","untranslatedTitle":"Proper Black \/ 0"}]},{"id":"15291328659622","handle":"faux-fur-trimmed-cuff-shimmer-sweater-duster-coral-blush","isCollective":false,"title":"Faux Fur Trimmed Cuff Shimmer Sweater Duster Coral Blush","type":"Sweater Tops","untranslatedTitle":"Faux Fur Trimmed Cuff Shimmer Sweater Duster Coral Blush","url":"\/products\/faux-fur-trimmed-cuff-shimmer-sweater-duster-coral-blush","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"56400671047846","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DP5470_SET.jpg?v=1791009218"},"price":{"amount":140.0,"currencyCode":"USD"},"sku":"2DP5470500","title":"Coral Blush \/ XS","untranslatedTitle":"Coral Blush \/ XS"}]},{"id":"15291328594086","handle":"print-sophia-charmeuse-blouse-frozen-fleurs","isCollective":false,"title":"Print Sophia Charmeuse Blouse Frozen Fleurs","type":"Woven Tops","untranslatedTitle":"Print Sophia Charmeuse Blouse Frozen Fleurs","url":"\/products\/print-sophia-charmeuse-blouse-frozen-fleurs","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"56400670392486","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2CV2032_SET.jpg?v=1791009216"},"price":{"amount":90.0,"currencyCode":"USD"},"sku":"2CV2032008","title":"Frozen Fleurs \/ 8","untranslatedTitle":"Frozen Fleurs \/ 8"}]},{"id":"15288327766182","handle":"gold-hardware-leopard-belt-multi","isCollective":false,"title":"Gold Hardware Leopard Belt Multi","type":"Accessories","untranslatedTitle":"Gold Hardware Leopard Belt Multi","url":"\/products\/gold-hardware-leopard-belt-multi","vendor":"Jardin","remoteShopId":null,"variants":[{"id":"56483682680998","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DQ0990_SET_e3c6c6a7-db56-4b09-bbc2-71da6137b642.jpg?v=1791009225"},"price":{"amount":100.0,"currencyCode":"USD"},"sku":"2DQ0990600","title":"Multi \/ S","untranslatedTitle":"Multi \/ S"}]},{"id":"15288327733414","handle":"plaid-ultimate-ponte-wide-leg-pant-charcoal-gray-multi","isCollective":false,"title":"Plaid Ultimate Ponte Wide Leg Pant Charcoal Gray Multi","type":"Pants","untranslatedTitle":"Plaid Ultimate Ponte Wide Leg Pant Charcoal Gray Multi","url":"\/products\/plaid-ultimate-ponte-wide-leg-pant-charcoal-gray-multi","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"56393534079142","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DQ0830_SET.jpg?v=1791009224"},"price":{"amount":100.0,"currencyCode":"USD"},"sku":"2DQ0830500","title":"Charcoal Gray Multi \/ XS","untranslatedTitle":"Charcoal Gray Multi \/ XS"}]},{"id":"15288327667878","handle":"straight-wideleg-denim-jean-winter-light-wash","isCollective":false,"title":"Straight Wideleg Denim Jean Winter Light Wash","type":"Jeans","untranslatedTitle":"Straight Wideleg Denim Jean Winter Light Wash","url":"\/products\/straight-wideleg-denim-jean-winter-light-wash","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"56393533554854","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DP5730_SET.jpg?v=1791009222"},"price":{"amount":90.0,"currencyCode":"USD"},"sku":"2DP5730002","title":"Winter Light Wash \/ 2","untranslatedTitle":"Winter Light Wash \/ 2"}]},{"id":"15287638065318","handle":"half-zip-sparkle-snowflake-sweater-hunt-club","isCollective":false,"title":"Half Zip Sparkle Snowflake Sweater Hunt Club","type":"Sweater Tops","untranslatedTitle":"Half Zip Sparkle Snowflake Sweater Hunt Club","url":"\/products\/half-zip-sparkle-snowflake-sweater-hunt-club","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"56391359561894","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DQ0660_SET.jpg?v=1791009216"},"price":{"amount":100.0,"currencyCode":"USD"},"sku":"2DQ0660400","title":"Hunt Club \/ XXS","untranslatedTitle":"Hunt Club \/ XXS"}]}
12037,{"id":"15287638032550","handle":"mid-length-fur-trimmed-puffer-coat-warm-cheetah","isCollective":false,"title":"Mid Length Fur Trimmed Puffer Coat Warm Cheetah","type":"Outerwear","untranslatedTitle":"Mid Length Fur Trimmed Puffer Coat Warm Cheetah","url":"\/products\/mid-length-fur-trimmed-puffer-coat-warm-cheetah","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"56391359365286","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DQ0590_SET.jpg?v=1791009214"},"price":{"amount":200.0,"currencyCode":"USD"},"sku":"2DQ0590400","title":"Warm Cheetah \/ XXS","untranslatedTitle":"Warm Cheetah \/ XXS"}]},{"id":"15287637999782","handle":"mid-length-fur-trimmed-puffer-coat-chocolate-torte","isCollective":false,"title":"Mid Length Fur Trimmed Puffer Coat Chocolate Torte","type":"Outerwear","untranslatedTitle":"Mid Length Fur Trimmed Puffer Coat Chocolate Torte","url":"\/products\/mid-length-fur-trimmed-puffer-coat-chocolate-torte","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"56391359168678","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DQ0580_SET.jpg?v=1791009214"},"price":{"amount":200.0,"currencyCode":"USD"},"sku":"2DQ0580400","title":"Chocolate Torte \/ XXS","untranslatedTitle":"Chocolate Torte \/ XXS"}]},{"id":"15287637934246","handle":"novelty-velvet-burnout-mock-neck-proper-black","isCollective":false,"title":"Novelty Velvet Burnout Mock Neck Proper Black","type":"Knit Tops","untranslatedTitle":"Novelty Velvet Burnout Mock Neck Proper Black","url":"\/products\/novelty-velvet-burnout-mock-neck-proper-black","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"56391358775462","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DQ0220_SET.jpg?v=1791009213"},"price":{"amount":65.0,"currencyCode":"USD"},"sku":"2DQ0220400","title":"Proper Black \/ XXS","untranslatedTitle":"Proper Black \/ XXS"}]},{"id":"15287637868710","handle":"pearl-and-bead-clutch-white","isCollective":false,"title":"Pearl and Bead Clutch White","type":"Accessories","untranslatedTitle":"Pearl and Bead Clutch White","url":"\/products\/pearl-and-bead-clutch-white","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"56391358546086","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DQ0040_SET_904fd260-3453-4aed-8cba-110e8361e8f9.jpg?v=1791009212"},"price":{"amount":80.0,"currencyCode":"USD"},"sku":"2DQ0040000","title":"White \/ One Size","untranslatedTitle":"White \/ One Size"}]},{"id":"15287637835942","handle":"crystal-embellished-waist-belt-black-silver","isCollective":false,"title":"Crystal Embellished Waist Belt Black\/Silver","type":"Accessories","untranslatedTitle":"Crystal Embellished Waist Belt Black\/Silver","url":"\/products\/crystal-embellished-waist-belt-black-silver","vendor":"Noir Jewelry","remoteShopId":null,"variants":[{"id":"56391358480550","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DP9920_SET.jpg?v=1791009211"},"price":{"amount":80.0,"currencyCode":"USD"},"sku":"2DP9920000","title":"Black\/Silver \/ One Size","untranslatedTitle":"Black\/Silver \/ One Size"}]},{"id":"15287637770406","handle":"fit-and-flare-sweater-dress-italian-plum","isCollective":false,"title":"Fit and Flare Sweater Dress Italian Plum","type":"Dresses","untranslatedTitle":"Fit and Flare Sweater Dress Italian Plum","url":"\/products\/fit-and-flare-sweater-dress-italian-plum","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"56391358251174","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DP7370_SET.jpg?v=1791009215"},"price":{"amount":120.0,"currencyCode":"USD"},"sku":"2DP7370400","title":"Italian Plum \/ XXS","untranslatedTitle":"Italian Plum \/ XXS"}]},{"id":"15287637672102","handle":"fit-and-flare-sweater-dress-black","isCollective":false,"title":"Fit and Flare Sweater Dress Black","type":"Dresses","untranslatedTitle":"Fit and Flare Sweater Dress Black","url":"\/products\/fit-and-flare-sweater-dress-black","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"56391357989030","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DP7320_SET.jpg?v=1791009213"},"price":{"amount":120.0,"currencyCode":"USD"},"sku":"2DP7320400","title":"Black \/ XXS","untranslatedTitle":"Black \/ XXS"}]}
12037,{"id":"15287637639334","handle":"sequin-scoop-neck-tank-hunt-club","isCollective":false,"title":"Sequin Scoop Neck Tank Hunt Club","type":"Knit Tops","untranslatedTitle":"Sequin Scoop Neck Tank Hunt Club","url":"\/products\/sequin-scoop-neck-tank-hunt-club","vendor":"Boston Proper","remoteShopId":null,"variants":[{"id":"56391357792422","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2DP6060_SET.jpg?v=1791009217"},"price":{"amount":60.0,"currencyCode":"USD"},"sku":"2DP6060400","title":"Hunt Club \/ XXS","untranslatedTitle":"Hunt Club \/ XXS"}]},{"id":"8529230004390","handle":"narvardeliveryprotection","isCollective":false,"title":"Shipping Protection","type":"insurance","untranslatedTitle":"Shipping Protection","url":"\/products\/narvardeliveryprotection","vendor":"NARVARDELIVERYPROTECTION","remoteShopId":null,"variants":[{"id":"46514716934310","image":{"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/ups-insureshield_5f014e53-930a-40e4-aeec-7d39ade750ef.png?v=1753728255"},"price":{"amount":0.0,"currencyCode":"USD"},"sku":"NARVARDELIVERYPROTECTION","title":"Default Title","untranslatedTitle":"Default Title"}]}],"purchasingCompany":null},},"https://www.bostonproper.com/cdn","5982071ew7ea472e3p69b68784m2150dfea",{"modern":"","legacy":""},{"trekkieShim":true,"agentContext":true,"apiClientId":"580111","facebookCapiEnabled":"false","themeId":"198106775718","themePublished":"true","eventMetadataId":"bd74b2c2-c5f8-444d-a394-a9f515df4e89","pageType":"product","resourceId":"15286962618534","shopId":"52960723110","storefrontBaseUrl":"https:\/\/www.bostonproper.com","extensionBaseUrl":"https:\/\/extensions.shopifycdn.com\/cdn\/shopifycloud\/web-pixels-manager","surface":"storefront-renderer","enabledBetaFlags":"[\"4c64608a\", \"16072cab\", \"8450a54b\", \"7ee89bf1\"]","isMerchantRequest":"false","hashVersion":"5982071ew7ea472e3p69b68784m2150dfea","publish":"custom","events":"[[\"page_viewed\",{}],[\"product_viewed\",{\"productVariant\":{\"price\":{\"amount\":50.0,\"currencyCode\":\"USD\"},\"product\":{\"title\":\"Boston Proper Printed Scarf With Love BP\",\"vendor\":\"Boston Proper\",\"id\":\"15286962618534\",\"untranslatedTitle\":\"Boston Proper Printed Scarf With Love BP\",\"url\":\"\/products\/boston-proper-printed-scarf-with-love-bp\",\"type\":\"Promotions\"},\"id\":\"56389031657638\",\"image\":{\"src\":\"\/\/www.bostonproper.com\/cdn\/shop\/files\/2CT8071_SET_e6f3e3ce-5a5f-451b-bc3b-ea1f8f71d236.jpg?v=1791009198\"},\"sku\":\"2CT8071000\",\"title\":\"With Love BP \/ One Size\",\"untranslatedTitle\":\"With Love BP \/ One Size\"}}]]"});})();</script>
12037<script> 12038 window.ShopifyAnalytics = window.ShopifyAnalytics || {}; 12039 window.ShopifyAnalytics.meta = window.ShopifyAnalytics.meta || {}; 12040 window.ShopifyAnalytics.meta.currency = 'USD'; 12041 var meta = {"product":{"id":15286962618534,"gid":"gid:\/\/shopify\/Product\/15286962618534","vendor":"Boston Proper","type":"Promotions","handle":"boston-proper-printed-scarf-with-love-bp","variants":[{"id":56389031657638,"price":5000,"name":"Boston Proper Printed Scarf With Love BP - With Love BP \/ One Size","public_title":"With Love BP \/ One Size","sku":"2CT8071000"}],"remote":false},"page":{"pageType":"product","resourceType":"product","resourceId":15286962618534,"requestId":"366c6d67-7b42-4351-97e1-f226d34f308c-1791063068"}}; 12042 for (var attr in meta) { 12043 window.ShopifyAnalytics.meta[attr] = meta[attr]; 12044 } 12045</script>
vendor: 1 bytes, line 12045
12045
12046<script class="analytics"> 12047 (function () { 12048 var customDocumentWrite = function(content) { 12049 var jquery = null; 12050 12051 if (window.jQuery) { 12052 jquery = window.jQuery; 12053 } else if (window.Checkout && window.Checkout.$) { 12054 jquery = window.Checkout.$; 12055 } 12056 12057 if (jquery) { 12058 jquery('body').append(content); 12059 } 12060 }; 12061 12062 var hasLoggedConversion = function(token) { 12063 if (token) { 12064 return document.cookie.indexOf('loggedConversion=' + token) !== -1; 12065 } 12066 return false; 12067 } 12068 12069 var setCookieIfConversion = function(token) { 12070 if (token) { 12071 var twoMonthsFromNow = new Date(Date.now()); 12072 twoMonthsFromNow.setMonth(twoMonthsFromNow.getMonth() + 2); 12073 12074 document.cookie = 'loggedConversion=' + token + '; expires=' + twoMonthsFromNow; 12075 } 12076 } 12077 12078 var trekkie = window.ShopifyAnalytics.lib = window.trekkie = window.trekkie || []; 12079 window.ShopifyAnalytics.lib.trekkie = window.trekkie; 12080 if (trekkie.integrations) { 12081 return; 12082 } 12083 trekkie.methods = [ 12084 'identify', 12085 'page', 12086 'ready', 12087 'track', 12088 'trackForm', 12089 'trackLink' 12090 ]; 12091 trekkie.factory = function(method) { 12092 return function() { 12093 var args = Array.prototype.slice.call(arguments); 12094 args.unshift(method); 12095 trekkie.push(args); 12096 if (method == 'track' || method == 'page') { 12097 var pageUrl; 12098 try { 12099 pageUrl = window.location.href; 12100 } catch (e) {} 12101 var pageReferrer; 12102 try { 12103 pageReferrer = document.referrer; 12104 } catch (e) {} 12105 try { 12106 if (window.__TREKKIE_SHIM_QUEUE == null) { 12107 window.__TREKKIE_SHIM_QUEUE = []; 12108 } 12109 window.__TREKKIE_SHIM_QUEUE.push({ 12110 from: 'trekkie-stub', 12111 method: method, 12112 args: args.slice(1), 12113 pageContext: { 12114 url: pageUrl, 12115 referrer: pageReferrer 12116 } 12117 }); 12118 } catch (e) { 12119 // no-op 12120 } 12121 } 12122 return trekkie; 12123 }; 12124 }; 12125 for (var i = 0; i < trekkie.methods.length; i++) { 12126 var key = trekkie.methods[i]; 12127 trekkie[key] = trekkie.factory(key); 12128 } 12129 trekkie.load = function(config) { 12130 trekkie.config = config || {}; 12131 trekkie.config.initialDocumentCookie = document.cookie; 12132 var first = document.getElementsByTagName('script')[0]; 12133var script = document.createElement('script'); 12134script.type = 'text/javascript'; 12135script.onerror = function(e) { 12136 var scriptFallback = document.createElement('script'); 12137 scriptFallback.type = 'text/javascript'; 12138 scriptFallback.onerror = function(error) { 12139 var Monorail = { 12140 produce: function produce(monorailDomain, schemaId, payload) { 12141 var currentMs = new Date().getTime(); 12142 var event = { 12143 schema_id: schemaId, 12144 payload: payload, 12145 metadata: { 12146 event_created_at_ms: currentMs, 12147 event_sent_at_ms: currentMs 12148 } 12149 }; 12150 return Monorail.sendRequest("https://" + monorailDomain + "/v1/produce", JSON.stringify(event)); 12151 }, 12152 sendRequest: function sendRequest(endpointUrl, payload) { 12153 // Try the sendBeacon API 12154 if (window && window.navigator && typeof window.navigator.sendBeacon === 'function' && typeof window.Blob === 'function' && !Monorail.isIos12()) { 12155 var blobData = new window.Blob([payload], { 12156 type: 'text/plain' 12157 }); 12158 12159 if (window.navigator.sendBeacon(endpointUrl, blobData)) { 12160 return true; 12161 } // sendBeacon was not successful 12162 12163 } // XHR beacon 12164 12165 var xhr = new XMLHttpRequest(); 12166 12167 try { 12168 xhr.open('POST', endpointUrl); 12169 xhr.setRequestHeader('Content-Type', 'text/plain'); 12170 xhr.send(payload); 12171 } catch (e) { 12172 console.log(e); 12173 } 12174 12175 return false; 12176 }, 12177 isIos12: function isIos12() { 12178 return window.navigator.userAgent.lastIndexOf('iPhone; CPU iPhone OS 12_') !== -1 || window.navigator.userAgent.lastIndexOf('iPad; CPU OS 12_') !== -1; 12179 } 12180 }; 12181 Monorail.produce('monorail-edge.shopifysvc.com', 12182 'trekkie_storefront_load_errors/1.1', 12183 {shop_id: 52960723110, 12184 theme_id: 198106775718, 12185 app_name: "storefront", 12186 context_url: window.location.href, 12187 source_url: "//www.bostonproper.com/cdn/s/trekkie.storefront.2f6d7d7ede7d24234881a9cd5f47e679642fa1f6.min.js"}); 12188 12189 }; 12190 scriptFallback.async = true; 12191 scriptFallback.src = '//www.bostonproper.com/cdn/s/trekkie.storefront.2f6d7d7ede7d24234881a9cd5f47e679642fa1f6.min.js'; 12192 first.parentNode.insertBefore(scriptFallback, first); 12193}; 12194script.async = true; 12195script.src = '//www.bostonproper.com/cdn/s/trekkie.storefront.2f6d7d7ede7d24234881a9cd5f47e679642fa1f6.min.js'; 12196first.parentNode.insertBefore(script, first); 12197 12198 }; 12199 trekkie.load( 12200 {"Trekkie":{"appName":"storefront","development":false,"defaultAttributes":{"shopId":52960723110,"isMerchantRequest":null,"themeId":198106775718,"themeCityHash":"14348548284193324120","contentLanguage":"en","currency":"USD","eventMetadataId":"bd74b2c2-c5f8-444d-a394-
12200a9f515df4e89"},"isServerSideCookieWritingEnabled":true,"monorailRegion":"shop_domain","enabledBetaFlags":["764d78cb","8450a54b","7ee89bf1","4c64608a"]},"Session Attribution":{},"S2S":{"facebookCapiEnabled":false,"source":"trekkie-storefront-renderer","apiClientId":580111}} 12201 ); 12202 12203 var loaded = false; 12204 trekkie.ready(function() { 12205 if (loaded) return; 12206 loaded = true; 12207 12208 window.ShopifyAnalytics.lib = window.trekkie; 12209 12210 var originalDocumentWrite = document.write; 12211 document.write = customDocumentWrite; 12212 try { window.ShopifyAnalytics.merchantGoogleAnalytics.call(this); } catch(error) {}; 12213 document.write = originalDocumentWrite; 12214 12215 var match = window.location.pathname.match(/checkouts\/(.+)\/(thank_you|post_purchase)/) 12216 var token = match? match[1]: undefined; 12217 if (!hasLoggedConversion(token)) { 12218 setCookieIfConversion(token); 12219 window.ShopifyAnalytics.lib.track("Viewed Product",{"currency":"USD","variantId":56389031657638,"productId":15286962618534,"productGid":"gid:\/\/shopify\/Product\/15286962618534","name":"Boston Proper Printed Scarf With Love BP - With Love BP \/ One Size","price":"50.00","sku":"2CT8071000","brand":"Boston Proper","variant":"With Love BP \/ One Size","category":"Promotions","nonInteraction":true,"remote":false,"available":true},undefined,undefined,{"shopifyEmitted":true}); 12220 } 12221 }); 12222 12223 window.ShopifyAnalytics.lib.page(null,{"pageType":"product","resourceType":"product","resourceId":15286962618534,"requestId":"366c6d67-7b42-4351-97e1-f226d34f308c-1791063068","shopifyEmitted":true}); 12224 12225 var eventsListenerScript = document.createElement('script'); 12226 eventsListenerScript.async = true; 12227 eventsListenerScript.src = "//www.bostonproper.com/cdn/shopifycloud/storefront/assets/shop_events_listener-4e26a9ce.js"; 12228 document.getElementsByTagName('head')[0].appendChild(eventsListenerScript); 12229})();</script>
vendor: 1 bytes, line 12229
12229
12230<script 12231 defer 12232 src="https://www.bostonproper.com/cdn/shopifycloud/perf-kit/shopify-perf-kit-3.9.4.min.js" 12233 data-application="storefront-renderer" 12234 data-shop-id="52960723110" 12235 data-render-region="gcp-us-central1" 12236 data-page-type="product" 12237 data-theme-instance-id="198106775718" 12238 data-theme-name="Horizon" 12239 data-theme-version="3.5.0" 12240 data-monorail-region="shop_domain" 12241 data-resource-timing-sampling-rate="10" 12242 data-shs="true" 12243 data-shs-beacon="true" 12244 data-shs-export-with-fetch="true" 12245 data-shs-logs-sample-rate="1" 12246 data-shs-beacon-endpoint="https://www.bostonproper.com/api/collect" 12247></script>
vendor: 1 bytes, line 12247
12247
12248<script src="https://cdn.shopify.com/storefront/web-components/account.js" type="module" crossorigin fetchpriority="low" async></script>
12248<script id="shopify-customer-account-data" type="application/json">{"customer":null,"shop":{"id":"52960723110","customerAccountTranslations":[{"key":"account_menu.signed_out_state.sheet_title","value":"Sign in or create account"},{"key":"account_menu.signed_out_state.section_divider","value":"More ways to sign in"},{"key":"account_menu.signed_out_state.alternate_divider","value":"OR"},{"key":"account_menu.signed_out_state.email_field_label","value":"Email"},{"key":"account_menu.signed_out_state.orders_button","value":"Orders"},{"key":"account_menu.signed_out_state.profile_button","value":"Profile"},{"key":"account_menu.signed_out_state.shop_login_button","value":"Sign in with Shop"},{"key":"account_menu.signed_out_state.continue_with_google","value":"Continue with Google"},{"key":"account_menu.signed_out_state.continue_with_facebook","value":"Continue with Facebook"},{"key":"account_menu.signed_out_state.marketing_consent_label","value":"Email me with news and offers"},{"key":"account_menu.signed_in_state.greeting_with_name","value":"Hi, {{first_name}}"},{"key":"account_menu.signed_in_state.greeting_without_name","value":"Account"},{"key":"account_menu.signed_in_state.orders_button","value":"Orders"},{"key":"account_menu.signed_in_state.profile_button","value":"Profile"},{"key":"account_menu.location_picker.sheet_title","value":"Locations"},{"key":"account_menu.location_picker.search_field_placeholder","value":"Search"},{"key":"account_menu.aria_labels.close_menu","value":"Close menu"},{"key":"account_menu.aria_labels.back_to_main_menu","value":"Back to main menu from {{title}}"},{"key":"account_menu.aria_labels.open_submenu","value":"{{title}}, opens submenu"},{"key":"account_menu.aria_labels.loading_menu","value":"Loading menu"},{"key":"account_menu.aria_labels.account","value":"Account"},{"key":"account_menu.aria_labels.sign_in_with_email","value":"Sign in with email"},{"key":"account_menu.aria_labels.sign_in","value":"Sign in"}],"socialLoginProviders":[{"handle":"Shop"}],"legacyCustomerAccounts":false,"signInLinksVisible":true,"b2bEnabled":false,"marketingConsentSigninEnabled":true,"marketingConsentPreselected":true},"visitor":{"country":"US","language":"en"}}</script>
12248 12249<meta name="shopify-y" content="cfc59d5f-4991-493d-bf87-6366517f822f" data-expiration="1822620668000"><meta name="shopify-s" content="8599fad1-de9a-436d-a4a0-4f83079cfaff" data-expiration="1791064868000"><meta name="new-cookie-storage-activated" content="t"> 12250</head> 12251 12252 <body class="page-width-narrow card-hover-effect-none"><noscript> 12253 <iframe 12254 src="https://sst.bostonproper.com/ns.html?id=GTM-NS3X79G>m_auth=288FkNoOr_QXQEJ-WPHnww>m_preview=env-1>m_cookies_win=x" 12255 height="0" 12256 width="0" 12257 style="display:none;visibility:hidden" 12258 ></iframe> 12259 </noscript><a 12260 class="skip-to-content-link button-secondary" 12261 href="#MainContent" 12262> 12263 Skip to content 12264</a> 12265 12266 12267 12268 <div id="header-group"> 12269 <!-- BEGIN sections: header-group --> 12270<div id="shopify-section-sections--29563569635494__header_announcements_9jGBFp" class="shopify-section shopify-section-group-header-group"> 12271 12272 12273
12273<script 12274 src="//www.bostonproper.com/cdn/shop/t/1521/assets/announcement-bar.js?v=171930887109884936311790713714" 12275 type="module" 12276 fetchpriority="low" 12277 ></script>
12277 12278 12279 12280<div class="section-background color-scheme-3"></div> 12281<aside 12282 class="announcement-bar spacing-style section section--page-width color-scheme-3" 12283 style="--padding-block-start: 15px; --padding-block-end:15px; 12284--padding-inline-start:0px; --padding-inline-end:0px; 12285; --border-bottom-width: 0px;" 12286> 12287 12288 12289 <announcement-bar-component 12290 class="announcement-bar__slider" 12291 12292 autoplay="5" aria-live="polite" 12293 12294 > 12295 12296 <slideshow-arrows 12297 position="center" 12298 12299><button 12300 class=" 12301 slideshow-control slideshow-control--previous 12302 12303 12304 slideshow-control--style-chevron 12305 button button-unstyled button-unstyled--transparent 12306 flip-x 12307 " 12308 12309 aria-label="Previous slide" 12310 12311 on:click="/previous" 12312 ref="previous" 12313> 12314 <span class="svg-wrapper icon-caret"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 12315 <path d="M11 5.5L7 9.5L3 5.5" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 12316</svg> 12317</span> 12318</button> 12319 12320 12321<button 12322 class=" 12323 slideshow-control slideshow-control--next 12324 12325 12326 slideshow-control--style-chevron 12327 button button-unstyled button-unstyled--transparent 12328 12329 " 12330 12331 aria-label="Next slide" 12332 12333 on:click="/next" 12334 ref="next" 12335> 12336 <span class="svg-wrapper icon-caret"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 12337 <path d="M11 5.5L7 9.5L3 5.5" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 12338</svg> 12339</span> 12340</button> 12341 12342 12343</slideshow-arrows> 12344 12345 12346 12347 12348 12349 <div class="announcement-bar__slides"> 12350 12351<slideshow-slide 12352 ref="slides[]" 12353 class=" 12354 announcement-bar__slide 12355 text-block 12356 text-block--AY1VLbVN4UEc4c01tT__announcement_ahaFK3 12357 text-block--align-center 12358 text-block--full-width 12359 custom-typography 12360 custom-font-size 12361 12362 " 12363 style=" 12364 12365--color: ;--font-size: 0.75rem; --font-weight: var(--font-subheading--weight);--font-family: var(--font-subheading--family); --text-transform: none; --text-wrap: pretty; --line-height: var(--line-height--body-); --letter-spacing: var(--letter-spacing--body-normal); 12366 --width: 100%; 12367 --text-align: center; 12368 --line-height: 1; 12369 " 12370 12371 aria-hidden="false" 12372 > 12373 <p class="announcement-bar__text"> 12374 20% Off $175+ Plus Free Shipping | Code: STYLE20 12375 </p> 12376 12377 12378 <a 12379 class="announcement-bar__link" 12380 href="/collections/new-arrivals" 12381 > 12382 <span class="visually-hidden"> 12383 20% Off $175+ Plus Free Shipping | Code: STYLE20 12384 </span> 12385 </a> 12386 12387 </slideshow-slide> 12388 12389 12390 12391<slideshow-slide 12392 ref="slides[]" 12393 class=" 12394 announcement-bar__slide 12395 text-block 12396 text-block--ANk9YK3Z6SHBQcFBJe__announcement_XRMPWL 12397 text-block--align-center 12398 text-block--full-width 12399 custom-typography 12400 custom-font-size 12401 12402 " 12403 style=" 12404 12405--color: ;--font-size: 0.75rem; --font-weight: var(--font-subheading--weight);--font-family: var(--font-subheading--family); --text-transform: none; --text-wrap: pretty; --line-height: var(--line-height--body-); --letter-spacing: var(--letter-spacing--body-normal); 12406 --width: 100%; 12407 --text-align: center; 12408 --line-height: 1; 12409 " 12410 12411 aria-hidden="true"inert 12412 > 12413 <p class="announcement-bar__text"> 12414 Shop $400+ to receive an exclusive scarf | Shop 12415 </p> 12416 12417 12418 <a 12419 class="announcement-bar__link" 12420 href="/collections/new-arrivals" 12421 > 12422 <span class="visually-hidden"> 12423 Shop $400+ to receive an exclusive scarf | Shop 12424 </span> 12425 </a> 12426 12427 </slideshow-slide> 12428 12429 12430 12431 </div> 12432 <button 12433 type="button" 12434 class="bp-announcement-playpause" 12435 data-bp-ann-playpause 12436 aria-label="Pause announcements" 12437 aria-pressed="false" 12438 > 12439 <svg class="bp-announcement-playpause__pause" width="12" height="12" viewBox="0 0 12 12" fill="none" aria-hidden="true"> 12440 <rect x="2" y="1.5" width="2.6" height="9" rx="0.4" fill="currentColor"/> 12441 <rect x="7.4" y="1.5" width="2.6" height="9" rx="0.4" fill="currentColor"/> 12442 </svg> 12443 <svg class="bp-announcement-playpause__play" width="12" height="12" viewBox="0 0 12 12" fill="none" aria-hidden="true" hidden> 12444 <path d="M2.5 1.5l8 4.5-8 4.5z" fill="currentColor"/> 12445 </svg> 12446 </button> 12447 12448 </announcement-bar-component> 12449</aside>
12449<script> 12450 (function () { 12451 if (window.__bpAnnPlayPauseWired) return; 12452 window.__bpAnnPlayPauseWired = true; 12453 12454 // Which GLYPH shows is driven purely by CSS off the bar's `[paused]` 12455 // attribute (see bp-fonts.liquid) â that survives Horizon's re-render of the 12456 // bar, which was resetting the JS-toggled `hidden` and leaving the wrong (or 12457 // both) icons showing. Here we only (a) toggle play/pause on click and 12458 // (b) keep aria-pressed/label in sync with the actual state (incl. any 12459 // auto-pause Horizon does), which CSS can't express. 12460 function syncAria(bar, btn) { 12461 var paused = bar.hasAttribute('paused'); 12462 btn.setAttribute('aria-pressed', paused ? 'true' : 'false'); 12463 btn.setAttribute('aria-label', paused ? 'Play announcements' : 'Pause announcements'); 12464 } 12465 12466 document.addEventListener('click', function (e) { 12467 var btn = e.target.closest && e.target.closest('[data-bp-ann-playpause]'); 12468 if (!btn) return; 12469 var bar = btn.closest('announcement-bar-component'); 12470 if (!bar) return; 12471 if (bar.hasAttribute('paused')) { 12472 if (typeof bar.play === 'function') bar.play(); 12473 else bar.removeAttribute('paused'); 12474 } else { 12475 if (typeof bar.pause === 'function') bar.pause(); 12476 else bar.setAttribute('paused', ''); 12477 } 12478 syncAria(bar, btn); 12479 }); 12480 12481 document.querySelectorAll('announcement-bar-component').forEach(function (bar) { 12482 var btn = bar.querySelector('[data-bp-ann-playpause]'); 12483 if (!btn) return; 12484 syncAria(bar, btn); // initial state on load 12485 try { 12486 new MutationObserver(function () { syncAria(bar, btn); }) 12487 .observe(bar, { attributes: true, attributeFilter: ['paused'] }); 12488 } catch (e) {} 12489 }); 12490 })(); 12491</script>
12491 12492 12493 12494 12495 12496</div><header id="shopify-section-sections--29563569635494__header_section" class="shopify-section shopify-section-group-header-group header-section"> 12497 12498 12499 12500 12501 12502 12503 12504 12505<header-component 12506 id="header-component" 12507 class="header" 12508 data-theme-color="rgb(254 255 250)" 12509 12510 12511 sticky="always" 12512 12513 data-skip-node-update 12514 data-scroll-direction="none" 12515 12516 12517 style=" 12518 --color-scheme-top-row: rgba(254 255 250 / 1.0); 12519 --color-scheme-bottom-row: rgba(254 255 250 / 1.0); 12520 12521 " 12522> 12523 <div class="header__underlay header__underlay-closed"></div> 12524 <div 12525 class="header__underlay header__underlay-open" 12526 ></div> 12527 12528 12529 12530 12531 12532 12533 <div 12534 class="header__row header__row--top color-scheme-1 section section--full-width-margin section--page-width" 12535 ref="headerRowTop"style="--border-bottom-width: 0px; --border-bottom-width-mobile: 0px;"> 12536 <div class="header__columns spacing-style"> 12537 <div 12538 class="header__column header__column--left" 12539 data-testid="header-top-left" 12540 > 12541 12542 12543 12544 12545 12546 12547<div 12548 class="header__drawer" 12549 ref="headerDrawerContainer" 12550 data-hydration-key="header-drawer-mobile" 12551 12552 ><bp-mobile-drawer class="bp-mobile-drawer" data-hydration-key="bp-mobile-drawer" data-skip-subtree-update> 12553 <details class="bp-mobile-drawer__details" ref="details" scroll-lock> 12554 <summary class="bp-mobile-drawer__trigger" aria-label="Menu"> 12555 <span class="bp-mobile-drawer__trigger-icon" aria-hidden="true"><svg width="44" height="44" viewBox="0 0 44 44" fill="none" xmlns="http://www.w3.org/2000/svg"> 12556<path d="M31.9999 14.75C32.2499 14.7559 32.4999 14.7617 32.7499 14.7676C32.7499 14.2559 32.7499 13.7441 32.7499 13.2324C32.4999 13.2383 32.2499 13.2441 31.9999 13.25C31.6666 13.2578 31.3333 13.2656 30.9999 13.2734C25 13.4141 19 13.5547 13 13.6953C12.6667 13.7031 12.3333 13.7109 12 13.7188C11.9084 13.7209 11.8168 13.723 11.7253 13.7252C11.7253 13.9084 11.7253 14.0916 11.7253 14.2748C11.8168 14.277 11.9084 14.2791 12 14.2812C12.3333 14.2891 12.6667 14.2969 13 14.3047C19 14.4453 25 14.5859 30.9999 14.7266C31.3333 14.7344 31.6666 14.7422 31.9999 14.75Z" fill="currentColor"/> 12557<path d="M12.0001 14.75C11.7501 14.7559 11.5001 14.7617 11.2501 14.7676C11.2501 14.2559 11.2501 13.7441 11.2501 13.2324C11.5001 13.2383 11.7501 13.2441 12.0001 13.25C12.3334 13.2578 12.6667 13.2656 13.0001 13.2734C19 13.4141 25 13.5547 31 13.6953C31.3333 13.7031 31.6667 13.7109 32 13.7188C32.0916 13.7209 32.1832 13.723 32.2747 13.7252C32.2747 13.9084 32.2747 14.0916 32.2747 14.2748C32.1832 14.277 32.0916 14.2791 32 14.2812C31.6667 14.2891 31.3333 14.2969 31 14.3047C25 14.4453 19 14.5859 13.0001 14.7266C12.6667 14.7344 12.3334 14.7422 12.0001 14.75Z" fill="currentColor"/> 12558<path d="M31.9999 22.75C32.2499 22.7559 32.4999 22.7617 32.7499 22.7676C32.7499 22.2559 32.7499 21.7441 32.7499 21.2324C32.4999 21.2383 32.2499 21.2441 31.9999 21.25C31.6666 21.2578 31.3333 21.2656 30.9999 21.2734C25 21.4141 19 21.5547 13 21.6953C12.6667 21.7031 12.3333 21.7109 12 21.7188C11.9084 21.7209 11.8168 21.723 11.7253 21.7252C11.7253 21.9084 11.7253 22.0916 11.7253 22.2748C11.8168 22.277 11.9084 22.2791 12 22.2812C12.3333 22.2891 12.6667 22.2969 13 22.3047C19 22.4453 25 22.5859 30.9999 22.7266C31.3333 22.7344 31.6666 22.7422 31.9999 22.75Z" fill="currentColor"/> 12559<path d="M12.0001 22.75C11.7501 22.7559 11.5001 22.7617 11.2501 22.7676C11.2501 22.2559 11.2501 21.7441 11.2501 21.2324C11.5001 21.2383 11.7501 21.2441 12.0001 21.25C12.3334 21.2578 12.6667 21.2656 13.0001 21.2734C19 21.4141 25 21.5547 31 21.6953C31.3333 21.7031 31.6667 21.7109 32 21.7188C32.0916 21.7209 32.1832 21.723 32.2747 21.7252C32.2747 21.9084 32.2747 22.0916 32.2747 22.2748C32.1832 22.277 32.0916 22.2791 32 22.2812C31.6667 22.2891 31.3333 22.2969 31 22.3047C25 22.4453 19 22.5859 13.0001 22.7266C12.6667 22.7344 12.3334 22.7422 12.0001 22.75Z" fill="currentColor"/> 12560<path d="M31.9999 30.75C32.2499 30.7559 32.4999 30.7617 32.7499 30.7676C32.7499 30.2559 32.7499 29.7441 32.7499 29.2324C32.4999 29.2383 32.2499 29.2441 31.9999 29.25C31.6666 29.2578 31.3333 29.2656 30.9999 29.2734C25 29.4141 19 29.5547 13 29.6953C12.6667 29.7031 12.3333 29.7109 12 29.7188C11.9084 29.7209 11.8168 29.723 11.7253 29.7252C11.7253 29.9084 11.7253 30.0916 11.7253 30.2748C11.8168 30.277 11.9
12560084 30.2791 12 30.2812C12.3333 30.2891 12.6667 30.2969 13 30.3047C19 30.4453 25 30.5859 30.9999 30.7266C31.3333 30.7344 31.6666 30.7422 31.9999 30.75Z" fill="currentColor"/> 12561<path d="M12.0001 30.75C11.7501 30.7559 11.5001 30.7617 11.2501 30.7676C11.2501 30.2559 11.2501 29.7441 11.2501 29.2324C11.5001 29.2383 11.7501 29.2441 12.0001 29.25C12.3334 29.2578 12.6667 29.2656 13.0001 29.2734C19 29.4141 25 29.5547 31 29.6953C31.3333 29.7031 31.6667 29.7109 32 29.7188C32.0916 29.7209 32.1832 29.723 32.2747 29.7252C32.2747 29.9084 32.2747 30.0916 32.2747 30.2748C32.1832 30.277 32.0916 30.2791 32 30.2812C31.6667 30.2891 31.3333 30.2969 31 30.3047C25 30.4453 19 30.5859 13.0001 30.7266C12.6667 30.7344 12.3334 30.7422 12.0001 30.75Z" fill="currentColor"/> 12562</svg> 12563</span> 12564 <span class="bp-mobile-drawer__trigger-icon bp-mobile-drawer__trigger-icon--close" aria-hidden="true"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 12565 <path d="M12 2L2 12" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 12566 <path d="M12 12L2 2" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 12567</svg> 12568</span> 12569 </summary> 12570 12571 <div class="bp-mobile-drawer__panel" data-active-view="root"> 12572 <section class="bp-mobile-drawer__view bp-mobile-drawer__view--active" data-view="root"> 12573 <header class="bp-mobile-drawer__header"> 12574 <h2 class="bp-mobile-drawer__title">Menu</h2> 12575 <button type="button" class="bp-mobile-drawer__close" data-close aria-label="Close menu"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 12576 <path d="M12 2L2 12" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 12577 <path d="M12 12L2 2" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 12578</svg> 12579</button> 12580 </header> 12581 12582 <ul class="bp-mobile-drawer__list"> 12583 <li 12584 role="presentation" 12585 class="menu-list__list-item bp-menu-item-li" 12586 data-bp-menu-item 12587 12588><a 12589 href="/collections/new-arrivals" 12590 data-skip-node-update="true" 12591 class="menu-list__link" 12592 aria-controls="bp-submenu-AR2Y1MTEyN25uOTEyM__bp_menu_new_arrivals-1" 12593 aria-haspopup="true" 12594 aria-expanded="false" 12595 ref="menuitem" 12596 > 12597 <span class="menu-list__link-title">New Arrivals</span> 12598 </a> 12599 <div class="bp-mega-submenu" id="bp-submenu-AR2Y1MTEyN25uOTEyM__bp_menu_new_arrivals-1"> 12600 <div class="bp-mega-submenu__inner"> 12601 <div class="bp-mega bp-mega--columns-promos"><div class="bp-mega__columns"><div class="bp-mega__column"> 12602 <p class="bp-mega__heading"><a href="/collections/new-arrivals">New Arrivals</a></p><ul class="bp-mega__links"><li><a href="/collections/new-arrivals">Shop All</a></li><li><a href="/pages/digital-catalog">Digital Catalog</a></li><li><a href="/collections/new-arrivals-athleisure">Athleisure</a></li><li><a href="/collections/new-arrivals-coats">Coats</a></li><li><a href="/collections/new-arrivals-dresses">Dresses</a></li><li><a href="/collections/new-arrivals-jackets-blazers">Jackets & Blazers</a></li><li><a href="/collections/new-arrivals-jeans">Jeans</a></li><li><a href="/collections/new-arrivals-loungewear">Loungewear</a></li><li><a href="/collections/new-arrivals-pants-leggings">Pants & Leggings</a></li><li><a href="/collections/new-arrivals-shoes-accessories">Shoes & Accessories</a></li><li><a href="/collections/new-arrivals-skirts-shorts">Skirts & Shorts</a></li><li><a href="/collections/new-arrivals-sweaters">Sweaters</a></li><li><a href="/collections/new-arrivals-swimsuits-swimwear">Swimwear</a></li><li><a href="/collections/new-arrivals-tops-blouses-tees">Tops</a></li></ul></div><div class="bp-mega__column"> 12603 <p class="bp-mega__heading"><a href="/collections/new-arrivals">Featured Shops</a></p><ul class="bp-mega__links"><li><a href="/collections/featured-shop-animal-prints">Animal Prints</a></li><li><a href="/collections/editors-picks">Editor's Picks</a></li><li><a href="/collections/featured-shops-fall-colors">Fall Colors</a></li><li><a href="/collections/featured-shop-intricate-details">Intricate Details</a></li><li><a href="/collections/featured-shops-matching-sets">Matching Sets</a></li><li><a href="/collections/breast-cancer-awareness">Power in Pink</a></li><li><a href="/collections/featured-shops-layering">The Layering Shop</a></li><li><a href="https://www.bostonproper.com/collections/featured-shops-suede-leather">Suede & Leather</a></li><li><a href="/pages/the-jacket-edit">Style Guide: The Jacket Edit</a></li><li><a href="/pages/the-sweater-edit">Style Guide: The Sweater Edit</a></li><li><a href="/pages/new-to-boston-proper">New to Boston Proper? </a></li></ul></div><div class="bp-mega__column"> 12604 <p class="bp-mega__heading"><a href="/pages/the-beyond-travel-collection">Beyond Travel Collection</a></p><ul class="bp-mega__links"><li><a href="/collections/travel">Shop All </a></li><li><a href="/pages/the-beyond-travel-collection">The Travel Collection</a></li><li><a href="/collections/travel-bottoms">Beyond Travel Bottoms</a></li><li><a href="/collections/travel-dresses">Beyond Travel Dresses</a></li><li><a href="/collections/travel-jackets">Beyond Travel Jackets</a></li><li><a href="/collections/travel-tops">Beyond Travel Tops</a></li><li><a href="/collections/travel-sets">Beyond Travel Sets</a></li></ul></div><div class="bp-mega__column"> 12605 <p class="bp-mega__heading"><a href="/collections/all-brands-we-love">Brands We Love</a></p><ul class="bp-mega__links"><li><a href="/collections/brands-we-love-ana-luisa">Ana Luisa</a></li><li><a href="/collections/brands-we-love-becca">Becca By Rebecca Virtue</a></li><li><a href="/collections/brands-we-love-carmen-marc-valvo">Carmen Marc Valvo</a></li><li><a href="/collections/brands-we-love-change-of-scenery">
12605Change of Scenery</a></li><li><a href="/collections/brands-we-love-fifth-and-ninth">Fifth & Ninth</a></li><li><a href="/collections/brands-we-love-jardin-jewelry">Jardin Jewelry</a></li><li><a href="/collections/brands-we-love-pickletini">Pickletini X Boston Proper</a></li><li><a href="/collections/brands-we-love-saint-jo">Saint JÅ</a></li><li><a href="/collections/brands-we-love-sum-of-all">Sum Of All</a></li><li><a href="/collections/brands-we-love-walter-baker">Walter Baker</a></li><li><a href="/collections/brands-we-love-w-y-e-t-h">WYETH</a></li></ul></div></div><div class="bp-mega__promos"><div class="bp-mega__promo"> 12606 <a href="/collections/new-arrivals" class="bp-mega__promo-link"> 12607 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/Image_73.jpg?v=1790714514&width=400" alt="New Season. New Statements." srcset="//www.bostonproper.com/cdn/shop/files/Image_73.jpg?v=1790714514&width=200 200w, //www.bostonproper.com/cdn/shop/files/Image_73.jpg?v=1790714514&width=400 400w" width="400" height="500" loading="lazy"> 12608</div> 12609 <h4 class="bp-mega__promo-title">New Season. New Statements.</h4><div class="bp-mega__promo-copy"><p>Enjoy 20% Off $175+ Plus Free Shipping. Limited Time. Use code: STYLE20</p></div><span class="bp-mega__promo-cta">Shop Now</span> 12610 </a> 12611</div> 12612 12613<div class="bp-mega__promo"> 12614 <a href="/pages/the-dress-code" class="bp-mega__promo-link"> 12615 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_New_Arrivals_Image-6.jpg?v=1790707073&width=400" alt="The Dress Code" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_New_Arrivals_Image-6.jpg?v=1790707073&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_New_Arrivals_Image-6.jpg?v=1790707073&width=400 400w" width="400" height="500" loading="lazy"> 12616</div> 12617 <h4 class="bp-mega__promo-title">The Dress Code</h4><div class="bp-mega__promo-copy"><p>The one-piece solution. Simple to style, flattering to wear, and ready for every plan.</p></div><span class="bp-mega__promo-cta">Shop Now</span> 12618 </a> 12619</div> 12620 12621</div></div> 12622 </div> 12623 </div></li><li class="bp-mm-item" data-bp-mm-id="AR2Y1MTEyN25uOTEyM__bp_menu_new_arrivals-1"><button type="button" class="bp-mm-item__row" data-open-view="AR2Y1MTEyN25uOTEyM__bp_menu_new_arrivals-1"> 12624 <span class="bp-mm-item__label">New Arrivals</span> 12625 <span class="bp-mm-item__chevron" aria-hidden="true"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 12626 <path d="M11 5.5L7 9.5L3 5.5" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 12627</svg> 12628</span> 12629 </button> 12630 12631 <section class="bp-mm-view" data-view="AR2Y1MTEyN25uOTEyM__bp_menu_new_arrivals-1" hidden> 12632 <header class="bp-mm-view__header"> 12633 <button type="button" class="bp-mm-view__back" data-back> 12634 <span class="bp-mm-view__back-icon" aria-hidden="true"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 12635 <path d="M11 5.5L7 9.5L3 5.5" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 12636</svg> 12637</span> 12638 <span>New Arrivals</span> 12639 </button> 12640 <button type="button" class="bp-mm-view__close" data-close aria-label="Close menu"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 12641 <path d="M12 2L2 12" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 12642 <path d="M12 12L2 2" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 12643</svg> 12644</button> 12645 </header><div class="bp-mm-view__sections"><section class="bp-mm-section"> 12646 <p class="bp-mm-section__heading"><a href="/collections/new-arrivals">New Arrivals</a></p><ul class="bp-mm-section__links"><li><a href="/collections/new-arrivals">Shop All</a></li><li><a href="/pages/digital-catalog">Digital Catalog</a></li><li><a href="/collections/new-arrivals-athleisure">Athleisure</a></li><li><a href="/collections/new-arrivals-coats">Coats</a></li><li><a href="/collections/new-arrivals-dresses">Dresses</a></li><li><a href="/collections/new-arrivals-jackets-blazers">Jackets & Blazers</a></li><li><a href="/collections/new-arrivals-jeans">Jeans</a></li><li><a href="/collections/new-arrivals-loungewear">Loungewear</a></li><li><a href="/collections/new-arrivals-pants-leggings">Pants & Leggings</a></li><li><a href="/collections/new-arrivals-shoes-accessories">Shoes & Accessories</a></li><li><a href="/collections/new-arrivals-skirts-shorts">Skirts & Shorts</a></li><li><a href="/collections/new-arrivals-sweaters">Sweaters</a></li><li><a href="/collections/new-arrivals-swimsuits-swimwear">Swimwear</a></li><li><a href="/collections/new-arrivals-tops-blouses-tees">Tops</a></li></ul></section><section class="bp-mm-section"> 12647 <p class="bp-mm-section__heading"><a href="/collections/new-arrivals">Featured Shops</a></p><ul class="bp-mm-section__links"><li><a href="/collections/featured-shop-animal-prints">Animal Prints</a></li><li><a href="/collections/editors-picks">Editor's Picks</a></li><li><a href="/collections/featured-shops-fall-colors">Fall Colors</a></li><li><a href="/collections/featured-shop-intricate-details">Intricate Details</a></li><li><a href="/collections/featured-shops-matching-sets">Matching Sets</a></li><li><a href="/collections/breast-cancer-awareness">Power in Pink</a></li><li><a href="/collections/featured-shops-layering">The Layering Shop</a></li><li><a href="https://www.bostonproper.com/collections/featured-shops-suede-leather">Suede & Leather</a></li><li><a href="/pages/the-jacket-edit">Style Guide: The Jacket Edit</a></li><li><a href="/pages/the-sweater-edit">Style Guide: The Sweater Edit</a></li><li><a href="/pages/new-to-boston-proper">New to Boston Proper? </a></li></ul></section><section class="bp-mm-section"> 12648 <p class="bp-mm-section__heading"><a href="/pages/the-beyond-travel-collection">Beyond Travel Collection</a></p><ul class="bp-mm-section__links"><li><a href="/collections/travel">Shop All </a></li><li><a href="/pages/the-beyond-travel-collection">The Travel Collection</a></li><li><a href="/collections/travel-bottoms">Beyond Travel Bottoms</a></li><li><a href="/collections/travel-dresses">Beyond Travel Dresses</a></li><li><a href="/collections/travel-jackets">Beyond Travel Jackets</a></li><li><a href="/collections/travel-tops">Beyond Travel Tops</a></li><li><a href="/collections/travel-sets">Beyond Travel Sets</a></li></ul></section><section class="bp-mm-section"> 12649 <p class="bp-mm-section__heading"><a href="/collections/all-brands-we-love">Brands We Love</a></p><ul class="bp-mm-section__links"><li><a href="/collections/brands-we-love-ana-luisa">Ana Luisa</a></li><li><a href="/collections/brands-we-love-becca">Becca By Rebecca Virtue</a></li><li><a href="/collections/brands-we-love-carmen-marc-valvo">Carmen Marc Valvo</a></li><li><a href="/collections/brands-we-love-change-of-scenery">
12649Change of Scenery</a></li><li><a href="/collections/brands-we-love-fifth-and-ninth">Fifth & Ninth</a></li><li><a href="/collections/brands-we-love-jardin-jewelry">Jardin Jewelry</a></li><li><a href="/collections/brands-we-love-pickletini">Pickletini X Boston Proper</a></li><li><a href="/collections/brands-we-love-saint-jo">Saint JÅ</a></li><li><a href="/collections/brands-we-love-sum-of-all">Sum Of All</a></li><li><a href="/collections/brands-we-love-walter-baker">Walter Baker</a></li><li><a href="/collections/brands-we-love-w-y-e-t-h">WYETH</a></li></ul></section></div><div class="bp-mm-view__promos"><div class="bp-mega__promo"> 12650 <a href="/collections/new-arrivals" class="bp-mega__promo-link"> 12651 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/Image_73.jpg?v=1790714514&width=400" alt="New Season. New Statements." srcset="//www.bostonproper.com/cdn/shop/files/Image_73.jpg?v=1790714514&width=200 200w, //www.bostonproper.com/cdn/shop/files/Image_73.jpg?v=1790714514&width=400 400w" width="400" height="500" loading="lazy"> 12652</div> 12653 <h4 class="bp-mega__promo-title">New Season. New Statements.</h4><div class="bp-mega__promo-copy"><p>Enjoy 20% Off $175+ Plus Free Shipping. Limited Time. Use code: STYLE20</p></div><span class="bp-mega__promo-cta">Shop Now</span> 12654 </a> 12655</div> 12656 12657<div class="bp-mega__promo"> 12658 <a href="/pages/the-dress-code" class="bp-mega__promo-link"> 12659 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_New_Arrivals_Image-6.jpg?v=1790707073&width=400" alt="The Dress Code" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_New_Arrivals_Image-6.jpg?v=1790707073&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_New_Arrivals_Image-6.jpg?v=1790707073&width=400 400w" width="400" height="500" loading="lazy"> 12660</div> 12661 <h4 class="bp-mega__promo-title">The Dress Code</h4><div class="bp-mega__promo-copy"><p>The one-piece solution. Simple to style, flattering to wear, and ready for every plan.</p></div><span class="bp-mega__promo-cta">Shop Now</span> 12662 </a> 12663</div> 12664 12665</div></section></li> 12666 12667 12668 12669<li 12670 role="presentation" 12671 class="menu-list__list-item bp-menu-item-li" 12672 data-bp-menu-item 12673 12674><a 12675 href="/collections/clothing" 12676 data-skip-node-update="true" 12677 class="menu-list__link" 12678 aria-controls="bp-submenu-AUjR2VlEyVFlTbXhaM__bp_menu_clothing-1" 12679 aria-haspopup="true" 12680 aria-expanded="false" 12681 ref="menuitem" 12682 > 12683 <span class="menu-list__link-title">Clothing</span> 12684 </a> 12685 <div class="bp-mega-submenu" id="bp-submenu-AUjR2VlEyVFlTbXhaM__bp_menu_clothing-1"> 12686 <div class="bp-mega-submenu__inner"> 12687 <div class="bp-mega bp-mega--columns-promos"><div class="bp-mega__columns"><div class="bp-mega__column"> 12688 <p class="bp-mega__heading"><a href="/collections/clothing">Clothing</a></p><ul class="bp-mega__links"><li><a href="/collections/clothing">All Clothing</a></li><li><a href="/collections/clothing-athleisure">Athleisure</a></li><li><a href="/collections/clothing-bottoms">Bottoms</a></li><li><a href="/collections/clothing-jeans">Denim & Jeans</a></li><li><a href="/collections/clothing-dresses">Dresses</a></li><li><a href="/collections/clothing-intimates">Intimates</a></li><li><a href="/collections/clothing-jackets-blazers">Jackets & Blazers</a></li><li><a href="/collections/clothing-loungewear">Loungewear</a></li><li><a href="/collections/clothing-pants-leggings">Pants & Leggings</a></li><li><a href="/collections/clothing-shirts-blouses">Shirts & Blouses</a></li><li><a href="/collections/clothing-skirts-shorts">Skirts & Shorts</a></li><li><a href="/collections/clothing-sweaters">Sweaters</a></li><li><a href="/collections/clothing-swimsuits-swimwear">Swimwear</a></li><li><a href="/collections/clothing-tops-blouses-tees">Tops</a></li></ul></div><div class="bp-mega__column"> 12689 <p class="bp-mega__heading"><a href="/pages/all-occasions">Occasions</a></p><ul class="bp-mega__links"><li><a href="/collections/occasions-everyday-style">Casual Chic</a></li><li><a href="/collections/occasions-night-out">Own the Evening</a></li><li><a href="/collections/occasions-special-occasion">RSVP Ready</a></li><li><a href="/collections/occasions-vacation">Escape in Style</a></li><li><a href="/collections/occasions-work">Power Moves</a></li><li><a href="/collections/occasions-wedding">Celebrate Beautifully</a></li></ul></div><div class="bp-mega__column"> 12690 <p class="bp-mega__heading"><a href="/collections/clothing">Shops</a></p><ul class="bp-mega__links"><li><a href="/collections/clothing-pants-leggings">
12690The Bottoms Guide</a></li><li><a href="/collections/clothing-jeans">The Denim Guide</a></li><li><a href="/pages/essentials-collection">The Essentials Edit</a></li><li><a href="/collections/essentials">The Tops Collection</a></li><li><a href="/collections/the-suiting-shop">The Suiting Shop</a></li><li><a href="/pages/the-dress-code">Style Guide: The Dress Code</a></li><li><a href="/pages/the-jacket-edit">Style Guide: The Jacket Edit</a></li><li><a href="/pages/the-sweater-edit">Style Guide: The Sweater Edit</a></li></ul></div><div class="bp-mega__column"> 12691 <p class="bp-mega__heading"><a href="/collections/all-brands-we-love">Brands We Love</a></p><ul class="bp-mega__links"><li><a href="/collections/brands-we-love-becca">Becca By Rebecca Virtue</a></li><li><a href="/collections/brands-we-love-carmen-marc-valvo">Carmen Marc Valvo</a></li><li><a href="/collections/brands-we-love-change-of-scenery">Change of Scenery</a></li><li><a href="/collections/brands-we-love-mimosa-lane">Mimosa Lane</a></li><li><a href="/collections/brands-we-love-pickletini">Pickletini X Boston Proper</a></li><li><a href="/collections/brands-we-love-saint-jo">Saint JÅ</a></li><li><a href="/collections/brands-we-love-sum-of-all">Sum Of All</a></li><li><a href="/collections/brands-we-love-walter-baker">Walter Baker</a></li><li><a href="/collections/brands-we-love-w-y-e-t-h">WYETH</a></li><li><a href="/collections/brands-we-love-jardin-jewelry">Jardin Jewelry</a></li></ul></div></div><div class="bp-mega__promos"><div class="bp-mega__promo"> 12692 <a href="/collections/breast-cancer-awareness" class="bp-mega__promo-link"> 12693 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/Image_69.jpg?v=1790780283&width=400" alt="Power In Pink" srcset="//www.bostonproper.com/cdn/shop/files/Image_69.jpg?v=1790780283&width=200 200w, //www.bostonproper.com/cdn/shop/files/Image_69.jpg?v=1790780283&width=400 400w" width="400" height="500" loading="lazy"> 12694</div> 12695 <h4 class="bp-mega__promo-title">Power In Pink</h4><div class="bp-mega__promo-copy"><p>Wearing pink never felt so powerful. Shop our curated Power in Pink Collection and join us in supporting the Lynn Cancer Institute.</p></div><span class="bp-mega__promo-cta">Shop With Purpose</span> 12696 </a> 12697</div> 12698 12699<div class="bp-mega__promo"> 12700 <a href="/collections/knit-tops" class="bp-mega__promo-link"> 12701 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Clothing_Image-8.jpg?v=1790707168&width=400" alt="Knit Tops" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Clothing_Image-8.jpg?v=1790707168&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Clothing_Image-8.jpg?v=1790707168&width=400 400w" width="400" height="500" loading="lazy"> 12702</div> 12703 <h4 class="bp-mega__promo-title">Knit Tops</h4><div class="bp-mega__promo-copy"><p>From everyday to unforgettable. Flattering knits made for denim, jackets, and everything after.</p></div><span class="bp-mega__promo-cta">Shop Now</span> 12704 </a> 12705</div> 12706 12707</div></div> 12708 </div> 12709 </div></li><li class="bp-mm-item" data-bp-mm-id="AUjR2VlEyVFlTbXhaM__bp_menu_clothing-1"><button type="button" class="bp-mm-item__row" data-open-view="AUjR2VlEyVFlTbXhaM__bp_menu_clothing-1"> 12710 <span class="bp-mm-item__label">Clothing</span> 12711 <span class="bp-mm-item__chevron" aria-hidden="true"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 12712 <path d="M11 5.5L7 9.5L3 5.5" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 12713</svg> 12714</span> 12715 </button> 12716 12717 <section class="bp-mm-view" data-view="AUjR2VlEyVFlTbXhaM__bp_menu_clothing-1" hidden> 12718 <header class="bp-mm-view__header"> 12719 <button type="button" class="bp-mm-view__back" data-back> 12720 <span class="bp-mm-view__back-icon" aria-hidden="true"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 12721 <path d="M11 5.5L7 9.5L3 5.5" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 12722</svg> 12723</span>
12724 <span>Clothing</span> 12725 </button> 12726 <button type="button" class="bp-mm-view__close" data-close aria-label="Close menu"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 12727 <path d="M12 2L2 12" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 12728 <path d="M12 12L2 2" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 12729</svg> 12730</button> 12731 </header><div class="bp-mm-view__sections"><section class="bp-mm-section"> 12732 <p class="bp-mm-section__heading"><a href="/collections/clothing">Clothing</a></p><ul class="bp-mm-section__links"><li><a href="/collections/clothing">All Clothing</a></li><li><a href="/collections/clothing-athleisure">Athleisure</a></li><li><a href="/collections/clothing-bottoms">Bottoms</a></li><li><a href="/collections/clothing-jeans">Denim & Jeans</a></li><li><a href="/collections/clothing-dresses">Dresses</a></li><li><a href="/collections/clothing-intimates">Intimates</a></li><li><a href="/collections/clothing-jackets-blazers">Jackets & Blazers</a></li><li><a href="/collections/clothing-loungewear">Loungewear</a></li><li><a href="/collections/clothing-pants-leggings">Pants & Leggings</a></li><li><a href="/collections/clothing-shirts-blouses">Shirts & Blouses</a></li><li><a href="/collections/clothing-skirts-shorts">Skirts & Shorts</a></li><li><a href="/collections/clothing-sweaters">Sweaters</a></li><li><a href="/collections/clothing-swimsuits-swimwear">Swimwear</a></li><li><a href="/collections/clothing-tops-blouses-tees">Tops</a></li></ul></section><section class="bp-mm-section"> 12733 <p class="bp-mm-section__heading"><a href="/pages/all-occasions">Occasions</a></p><ul class="bp-mm-section__links"><li><a href="/collections/occasions-everyday-style">Casual Chic</a></li><li><a href="/collections/occasions-night-out">Own the Evening</a></li><li><a href="/collections/occasions-special-occasion">RSVP Ready</a></li><li><a href="/collections/occasions-vacation">Escape in Style</a></li><li><a href="/collections/occasions-work">Power Moves</a></li><li><a href="/collections/occasions-wedding">Celebrate Beautifully</a></li></ul></section><section class="bp-mm-section"> 12734 <p class="bp-mm-section__heading"><a href="/collections/clothing">Shops</a></p><ul class="bp-mm-section__links"><li><a href="/collections/clothing-pants-leggings">The Bottoms Guide</a></li><li><a href="/collections/clothing-jeans">The Denim Guide</a></li><li><a href="/pages/essentials-collection">The Essentials Edit</a></li><li><a href="/collections/essentials">The Tops Collection</a></li><li><a href="/collections/the-suiting-shop">The Suiting Shop</a></li><li><a href="/pages/the-dress-code">Style Guide: The Dress Code</a></li><li><a href="/pages/the-jacket-edit">Style Guide: The Jacket Edit</a></li><li><a href="/pages/the-sweater-edit">Style Guide: The Sweater Edit</a></li></ul></section><section class="bp-mm-section"> 12735 <p class="bp-mm-section__heading"><a href="/collections/all-brands-we-love">Brands We Love</a></p><ul class="bp-mm-section__links"><li><a href="/collections/brands-we-love-becca">Becca By Rebecca Virtue</a></li><li><a href="/collections/brands-we-love-carmen-marc-valvo">Carmen Marc Valvo</a></li><li><a href="/collections/brands-we-love-change-of-scenery">Change of Scenery</a></li><li><a href="/collections/brands-we-love-mimosa-lane">Mimosa Lane</a></li><li><a href="/collections/brands-we-love-pickletini">Pickletini X Boston Proper</a></li><li><a href="/collections/brands-we-love-saint-jo">Saint JÅ</a></li><li><a href="/collections/brands-we-love-sum-of-all">Sum Of All</a></li><li><a href="/collections/brands-we-love-walter-baker">Walter Baker</a></li><li><a href="/collections/brands-we-love-w-y-e-t-h">WYETH</a></li><li><a href="/collections/brands-we-love-jardin-jewelry">Jardin Jewelry</a></li></ul></section></div><div class="bp-mm-view__promos"><div class="bp-mega__promo"> 12736 <a href="/collections/breast-cancer-awareness" class="bp-mega__promo-link"> 12737 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/Image_69.jpg?v=1790780283&width=400" alt="Power In Pink" srcset="//www.bostonproper.com/cdn/shop/files/Image_69.jpg?v=1790780283&width=200 200w, //www.bostonproper.com/cdn/shop/files/Image_69.jpg?v=1790780283&width=400 400w" width="400" height="500" loading="lazy"> 12738</div> 12739 <h4 class="bp-mega__promo-title">Power In Pink</h4><div class="bp-mega__promo-copy"><p>Wearing pink never felt so powerful. Shop our curated Power in Pink Collection and join us in supporting the Lynn Cancer Institute.</p></div><span class="bp-mega__promo-cta">Shop With Purpose</span> 12740 </a> 12741</div> 12742 12743<div class="bp-mega__promo"> 12744 <a href="/collections/knit-tops" class="bp-mega__promo-link"> 12745 <div class="bp-mega__promo-image">
12745<img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Clothing_Image-8.jpg?v=1790707168&width=400" alt="Knit Tops" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Clothing_Image-8.jpg?v=1790707168&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Clothing_Image-8.jpg?v=1790707168&width=400 400w" width="400" height="500" loading="lazy"> 12746</div> 12747 <h4 class="bp-mega__promo-title">Knit Tops</h4><div class="bp-mega__promo-copy"><p>From everyday to unforgettable. Flattering knits made for denim, jackets, and everything after.</p></div><span class="bp-mega__promo-cta">Shop Now</span> 12748 </a> 12749</div> 12750 12751</div></section></li> 12752 12753 12754 12755<li 12756 role="presentation" 12757 class="menu-list__list-item bp-menu-item-li" 12758 data-bp-menu-item 12759 12760><a 12761 href="/pages/all-occasions" 12762 data-skip-node-update="true" 12763 class="menu-list__link" 12764 aria-controls="bp-submenu-AWlVkd2t4OFJocXpVU__bp_menu_occasions-1" 12765 aria-haspopup="true" 12766 aria-expanded="false" 12767 ref="menuitem" 12768 > 12769 <span class="menu-list__link-title">Occasions</span> 12770 </a> 12771 <div class="bp-mega-submenu" id="bp-submenu-AWlVkd2t4OFJocXpVU__bp_menu_occasions-1"> 12772 <div class="bp-mega-submenu__inner"> 12773 <div class="bp-mega bp-mega--image-tiles"><div class="bp-mega__tiles"><a class="bp-mega__tile" href="/collections/occasions-everyday-style"> 12774 <div class="bp-mega__tile-image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-10.jpg?v=1790701815&width=400" alt="Casual Chic" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-10.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-10.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 12775</div> 12776 <p class="bp-mega__tile-title">Casual Chic</p> 12777 <span class="bp-mega__tile-cta">Shop Now</span> 12778 </a><a class="bp-mega__tile" href="/collections/occasions-vacation"> 12779 <div class="bp-mega__tile-image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-14.jpg?v=1790701815&width=400" alt="Escape in Style" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-14.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-14.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 12780</div> 12781 <p class="bp-mega__tile-title">Escape in Style</p> 12782 <span class="bp-mega__tile-cta">Shop Now</span> 12783 </a><a class="bp-mega__tile" href="/collections/occasions-special-occasion"> 12784 <div class="bp-mega__tile-image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-16.jpg?v=1790701815&width=400" alt="RSVP Ready" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-16.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-16.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 12785</div> 12786 <p class="bp-mega__tile-title">RSVP Ready</p> 12787 <span class="bp-mega__tile-cta">Shop Now</span> 12788 </a><a class="bp-mega__tile" href="/collections/occasions-work"> 12789 <div class="bp-mega__tile-image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-12.jpg?v=1790701815&width=400" alt="Power Moves" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-12.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-12.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 12790</div> 12791 <p class="bp-mega__tile-title">Power Moves</p> 12792 <span class="bp-mega__tile-cta">Shop Now</span> 12793 </a><a class="bp-mega__tile" href="/collections/occasions-night-out"> 12794 <div class="bp-mega__tile-image">
12794<img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-18_9dd5998e-01cb-46c4-a182-cd9be31c2c67.jpg?v=1790701815&width=400" alt="Own the Evening" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-18_9dd5998e-01cb-46c4-a182-cd9be31c2c67.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-18_9dd5998e-01cb-46c4-a182-cd9be31c2c67.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 12795</div> 12796 <p class="bp-mega__tile-title">Own the Evening</p> 12797 <span class="bp-mega__tile-cta">Shop Now</span> 12798 </a><a class="bp-mega__tile" href="/collections/occasions-wedding"> 12799 <div class="bp-mega__tile-image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-20_6043da00-6dae-4bd6-9c2c-e935f58f8b4c.jpg?v=1790701815&width=400" alt="Celebrate Beautifully" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-20_6043da00-6dae-4bd6-9c2c-e935f58f8b4c.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-20_6043da00-6dae-4bd6-9c2c-e935f58f8b4c.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 12800</div> 12801 <p class="bp-mega__tile-title">Celebrate Beautifully</p> 12802 <span class="bp-mega__tile-cta">Shop Now</span> 12803 </a></div></div> 12804 </div> 12805 </div></li><li class="bp-mm-item" data-bp-mm-id="AWlVkd2t4OFJocXpVU__bp_menu_occasions-1"><button type="button" class="bp-mm-item__row" data-open-view="AWlVkd2t4OFJocXpVU__bp_menu_occasions-1"> 12806 <span class="bp-mm-item__label">Occasions</span> 12807 <span class="bp-mm-item__chevron" aria-hidden="true"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 12808 <path d="M11 5.5L7 9.5L3 5.5" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 12809</svg> 12810</span> 12811 </button> 12812 12813 <section class="bp-mm-view" data-view="AWlVkd2t4OFJocXpVU__bp_menu_occasions-1" hidden> 12814 <header class="bp-mm-view__header"> 12815 <button type="button" class="bp-mm-view__back" data-back> 12816 <span class="bp-mm-view__back-icon" aria-hidden="true"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 12817 <path d="M11 5.5L7 9.5L3 5.5" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 12818</svg> 12819</span> 12820 <span>Occasions</span> 12821 </button> 12822 <button type="button" class="bp-mm-view__close" data-close aria-label="Close menu"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 12823 <path d="M12 2L2 12" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 12824 <path d="M12 12L2 2" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 12825</svg> 12826</button> 12827 </header><div class="bp-mm-view__tiles"><a class="bp-mm-tile" href="/collections/occasions-everyday-style"> 12828 <span class="bp-mm-tile__image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-10.jpg?v=1790701815&width=400" alt="Casual Chic" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-10.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-10.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 12829</span> 12830 <span class="bp-mm-tile__title">Casual Chic</span> 12831 <span class="bp-mm-tile__cta">Shop Now</span> 12832 </a><a class="bp-mm-tile" href="/collections/occasions-vacation"> 12833 <span class="bp-mm-tile__image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-14.jpg?v=1790701815&width=400" alt="Escape in Style" srcset="//www.bostonproper.com/cdn/shop/files/Fl
12833y_Downs_Occasions_Image-14.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-14.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 12834</span> 12835 <span class="bp-mm-tile__title">Escape in Style</span> 12836 <span class="bp-mm-tile__cta">Shop Now</span> 12837 </a><a class="bp-mm-tile" href="/collections/occasions-special-occasion"> 12838 <span class="bp-mm-tile__image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-16.jpg?v=1790701815&width=400" alt="RSVP Ready" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-16.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-16.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 12839</span> 12840 <span class="bp-mm-tile__title">RSVP Ready</span> 12841 <span class="bp-mm-tile__cta">Shop Now</span> 12842 </a><a class="bp-mm-tile" href="/collections/occasions-work"> 12843 <span class="bp-mm-tile__image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-12.jpg?v=1790701815&width=400" alt="Power Moves" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-12.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-12.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 12844</span> 12845 <span class="bp-mm-tile__title">Power Moves</span> 12846 <span class="bp-mm-tile__cta">Shop Now</span> 12847 </a><a class="bp-mm-tile" href="/collections/occasions-night-out"> 12848 <span class="bp-mm-tile__image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-18_9dd5998e-01cb-46c4-a182-cd9be31c2c67.jpg?v=1790701815&width=400" alt="Own the Evening" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-18_9dd5998e-01cb-46c4-a182-cd9be31c2c67.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-18_9dd5998e-01cb-46c4-a182-cd9be31c2c67.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 12849</span> 12850 <span class="bp-mm-tile__title">Own the Evening</span> 12851 <span class="bp-mm-tile__cta">Shop Now</span> 12852 </a><a class="bp-mm-tile" href="/collections/occasions-wedding"> 12853 <span class="bp-mm-tile__image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-20_6043da00-6dae-4bd6-9c2c-e935f58f8b4c.jpg?v=1790701815&width=400" alt="Celebrate Beautifully" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-20_6043da00-6dae-4bd6-9c2c-e935f58f8b4c.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-20_6043da00-6dae-4bd6-9c2c-e935f58f8b4c.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 12854</span> 12855 <span class="bp-mm-tile__title">Celebrate Beautifully</span> 12856 <span class="bp-mm-tile__cta">Shop Now</span> 12857 </a></div></section></li> 12858 12859 12860 12861<li 12862 role="presentation" 12863 class="menu-list__list-item bp-menu-item-li" 12864 data-bp-menu-item 12865 12866><a 12867 href="/pages/essentials-collection" 12868 data-skip-node-update="true" 12869 class="menu-list__link" 12870 aria-controls="bp-submenu-ARTdKY3Jpczg4VVVHb__bp_menu_collections-1" 12871 aria-haspopup="true" 12872 aria-expanded="false" 12873 ref="menuitem" 12874 > 12875 <span class="menu-list__link-title">Collections</span> 12876 </a> 12877 <div class="bp-mega-submenu" id="bp-submenu-ARTdKY3Jpczg4VVVHb__bp_menu_collections-1"> 12878 <div class="bp-mega-submenu__inner"> 12879 <div class="bp-mega bp-mega--image-tiles"><div class="bp-mega__tiles"><a class="bp-mega__tile" href="/collections/charmeuse"> 12880 <div class="bp-mega__tile-image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-11.jpg?v=1790701815&width=400" alt="Charmeuse" srcset="//www.bostonproper.com/cdn/shop/files/Fl
12880y_Downs_Collections_Image-11.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-11.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 12881</div> 12882 <p class="bp-mega__tile-title">Charmeuse</p> 12883 <span class="bp-mega__tile-cta">Shop Now</span> 12884 </a><a class="bp-mega__tile" href="/collections/chic-zip"> 12885 <div class="bp-mega__tile-image"><img src="//www.bostonproper.com/cdn/shop/files/Image_76_c1e341e6-f12c-49d5-bc88-54e2673f3c30.jpg?v=1790802160&width=400" alt="Chic Zip" srcset="//www.bostonproper.com/cdn/shop/files/Image_76_c1e341e6-f12c-49d5-bc88-54e2673f3c30.jpg?v=1790802160&width=200 200w, //www.bostonproper.com/cdn/shop/files/Image_76_c1e341e6-f12c-49d5-bc88-54e2673f3c30.jpg?v=1790802160&width=400 400w" width="400" height="500" loading="lazy"> 12886</div> 12887 <p class="bp-mega__tile-title">Chic Zip</p> 12888 <span class="bp-mega__tile-cta">Shop Now</span> 12889 </a><a class="bp-mega__tile" href="/collections/clothing-jeans"> 12890 <div class="bp-mega__tile-image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-17.jpg?v=1790701815&width=400" alt="Denim" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-17.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-17.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 12891</div> 12892 <p class="bp-mega__tile-title">Denim</p> 12893 <span class="bp-mega__tile-cta">Shop Now</span> 12894 </a><a class="bp-mega__tile" href="/collections/the-suiting-shop"> 12895 <div class="bp-mega__tile-image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-13.jpg?v=1790701815&width=400" alt="Modern Suiting" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-13.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-13.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 12896</div> 12897 <p class="bp-mega__tile-title">Modern Suiting</p> 12898 <span class="bp-mega__tile-cta">Shop Now</span> 12899 </a><a class="bp-mega__tile" href="/collections/travel"> 12900 <div class="bp-mega__tile-image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-19.jpg?v=1790701815&width=400" alt="Beyond Travel" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-19.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-19.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 12901</div> 12902 <p class="bp-mega__tile-title">Beyond Travel</p> 12903 <span class="bp-mega__tile-cta">Shop Now</span> 12904 </a><a class="bp-mega__tile" href="/collections/athleisure-luxe-lounge"> 12905 <div class="bp-mega__tile-image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-21.jpg?v=1790701815&width=400" alt="Luxe Lounge" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-21.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-21.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 12906</div> 12907 <p class="bp-mega__tile-title">Luxe Lounge</p> 12908 <span class="bp-mega__tile-cta">Shop Now</span> 12909 </a></div></div> 12910 </div> 12911 </div></li><li class="bp-mm-item" data-bp-mm-id="ARTdKY3Jpczg4VVVHb__bp_menu_collections-1"><button type="button" class="bp-mm-item__row" data-open-view="ARTdKY3Jpczg4VVVHb__bp_menu_collections-1"> 12912 <span class="bp-mm-item__label">Collections</span> 12913 <span class="bp-mm-item__chevron" aria-hidden="true"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 12914 <path d="M11 5.5L7 9.5L3 5.5" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 12915</svg> 12916</span> 12917 </button> 12918 12919 <section class="bp-mm-view" data-view="ARTdKY3Jpczg4VVVHb__bp_menu_collections-1" hidden> 12920 <header class="bp-mm-view__header"> 12921 <button type="button" class="bp-mm-view__back" data-back>
12922 <span class="bp-mm-view__back-icon" aria-hidden="true"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 12923 <path d="M11 5.5L7 9.5L3 5.5" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 12924</svg> 12925</span> 12926 <span>Collections</span> 12927 </button> 12928 <button type="button" class="bp-mm-view__close" data-close aria-label="Close menu"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 12929 <path d="M12 2L2 12" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 12930 <path d="M12 12L2 2" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 12931</svg> 12932</button> 12933 </header><div class="bp-mm-view__tiles"><a class="bp-mm-tile" href="/collections/charmeuse"> 12934 <span class="bp-mm-tile__image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-11.jpg?v=1790701815&width=400" alt="Charmeuse" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-11.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-11.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 12935</span> 12936 <span class="bp-mm-tile__title">Charmeuse</span> 12937 <span class="bp-mm-tile__cta">Shop Now</span> 12938 </a><a class="bp-mm-tile" href="/collections/chic-zip"> 12939 <span class="bp-mm-tile__image"><img src="//www.bostonproper.com/cdn/shop/files/Image_76_c1e341e6-f12c-49d5-bc88-54e2673f3c30.jpg?v=1790802160&width=400" alt="Chic Zip" srcset="//www.bostonproper.com/cdn/shop/files/Image_76_c1e341e6-f12c-49d5-bc88-54e2673f3c30.jpg?v=1790802160&width=200 200w, //www.bostonproper.com/cdn/shop/files/Image_76_c1e341e6-f12c-49d5-bc88-54e2673f3c30.jpg?v=1790802160&width=400 400w" width="400" height="500" loading="lazy"> 12940</span> 12941 <span class="bp-mm-tile__title">Chic Zip</span> 12942 <span class="bp-mm-tile__cta">Shop Now</span> 12943 </a><a class="bp-mm-tile" href="/collections/clothing-jeans"> 12944 <span class="bp-mm-tile__image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-17.jpg?v=1790701815&width=400" alt="Denim" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-17.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-17.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 12945</span> 12946 <span class="bp-mm-tile__title">Denim</span> 12947 <span class="bp-mm-tile__cta">Shop Now</span> 12948 </a><a class="bp-mm-tile" href="/collections/the-suiting-shop"> 12949 <span class="bp-mm-tile__image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-13.jpg?v=1790701815&width=400" alt="Modern Suiting" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-13.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-13.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 12950</span> 12951 <span class="bp-mm-tile__title">Modern Suiting</span> 12952 <span class="bp-mm-tile__cta">Shop Now</span> 12953 </a><a class="bp-mm-tile" href="/collections/travel"> 12954 <span class="bp-mm-tile__image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-19.jpg?v=1790701815&width=400" alt="Beyond Travel" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-19.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-19.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 12955</span> 12956 <span class="bp-mm-tile__title">Beyond Travel</span>
12957 <span class="bp-mm-tile__cta">Shop Now</span> 12958 </a><a class="bp-mm-tile" href="/collections/athleisure-luxe-lounge"> 12959 <span class="bp-mm-tile__image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-21.jpg?v=1790701815&width=400" alt="Luxe Lounge" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-21.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-21.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 12960</span> 12961 <span class="bp-mm-tile__title">Luxe Lounge</span> 12962 <span class="bp-mm-tile__cta">Shop Now</span> 12963 </a></div></section></li> 12964 12965 12966 12967<li 12968 role="presentation" 12969 class="menu-list__list-item bp-menu-item-li" 12970 data-bp-menu-item 12971 12972><a 12973 href="/collections/shoes-accessories" 12974 data-skip-node-update="true" 12975 class="menu-list__link" 12976 aria-controls="bp-submenu-AdVdsY2tEV2JFWlVvV__bp_menu_shoes-1" 12977 aria-haspopup="true" 12978 aria-expanded="false" 12979 ref="menuitem" 12980 > 12981 <span class="menu-list__link-title">Shoes & Accessories</span> 12982 </a> 12983 <div class="bp-mega-submenu" id="bp-submenu-AdVdsY2tEV2JFWlVvV__bp_menu_shoes-1"> 12984 <div class="bp-mega-submenu__inner"> 12985 <div class="bp-mega bp-mega--columns-promos"><div class="bp-mega__columns"><div class="bp-mega__column"> 12986 <p class="bp-mega__heading"><a href="/collections/shoes">Shoes</a></p><ul class="bp-mega__links"><li><a href="/collections/shoes">All Shoes</a></li><li><a href="/collections/shoes-boots">Boots & Booties</a></li><li><a href="/collections/shoes-heels">Heels</a></li><li><a href="/collections/shoes-sandals">Sandals</a></li><li><a href="/collections/shoes-sneakers">Sneakers</a></li><li><a href="/collections/shoes-wedges">Wedges</a></li></ul></div><div class="bp-mega__column"> 12987 <p class="bp-mega__heading"><a href="/collections/accessories">Accessories</a></p><ul class="bp-mega__links"><li><a href="/collections/accessories">All Accessories</a></li><li><a href="/collections/accessories-bags">Bags</a></li><li><a href="/collections/accessories-belts">Belts</a></li><li><a href="/collections/accessories-hats">Hats</a></li><li><a href="/collections/accessories-jewelry">Jewelry</a></li><li><a href="/collections/brands-we-love-fifth-and-ninth">Sunglasses</a></li></ul></div><div class="bp-mega__column"> 12988 <p class="bp-mega__heading"><a href="/collections/all-brands-we-love">Brands We Love</a></p><ul class="bp-mega__links"><li><a href="/collections/brands-we-love-amsterdam-heritage">Amsterdam Heritage</a></li><li><a href="/collections/brands-we-love-ana-luisa">Ana Luisa</a></li><li><a href="/collections/brands-we-love-ettika">Ettika</a></li><li><a href="/collections/brands-we-love-fifth-and-ninth">Fifth & Ninth</a></li><li><a href="/collections/brands-we-love-hanky-panky">Hanky Panky</a></li><li><a href="/collections/brands-we-love-hat-attack-new-york">Hat Attack</a></li><li><a href="/collections/brands-we-love-jardin-jewelry">Jardin Jewelry</a></li><li><a href="/collections/brands-we-love-miami-chains-co">Miami Chains & Co.</a></li><li><a href="/collections/brands-we-love-st-moran">St. Moran</a></li><li><a href="/collections/brands-we-love-the-sak">The Sak</a></li><li><a href="/collections/brands-we-love-t-jazelle">T. Jazelle</a></li><li><a href="/collections/brands-we-love-w-y-e-t-h">WYETH</a></li></ul></div></div><div class="bp-mega__promos"><div class="bp-mega__promo"> 12989 <a href="/collections/shoes-boots" class="bp-mega__promo-link"> 12990 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/Image_63.jpg?v=1790780283&width=400" alt="Boots" srcset="//www.bostonproper.com/cdn/shop/files/Image_63.jpg?v=1790780283&width=200 200w, //www.bostonproper.com/cdn/shop/files/Image_63.jpg?v=1790780283&width=400 400w" width="400" height="500" loading="lazy"> 12991</div> 12992 <h4 class="bp-mega__promo-title">Boots</h4><div class="bp-mega__promo-copy"><p>These boots are going places. Wild prints. Rich color stories. Versatile lengths. A new look for every mood.<br/><br/></p></div><span class="bp-mega__promo-cta">Shop Now</span> 12993 </a> 12994</div> 12995 12996<div class="bp-mega__promo"> 12997 <a href="/collections/accessories-jewelry" class="bp-mega__promo-link"> 12998 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/Image_74.jpg?v=1790800759&width=400" alt="Jewelry" srcset="//www.bostonproper.com/cdn/shop/files/Image_74.jpg?v=1790800759&width=200 200w, //www.bostonproper.com/cdn/shop/files/Image_74.jpg?v=1790800759&width=400 400w" width="400" height="500" loading="lazy"> 12999</div> 13000 <h4 class="bp-mega__promo-title">Jewelry</h4><div class="bp-mega__promo-copy"><p>Drenched in the details that define Boston Proper. Layered jewels and accent pieces that set the tone. </p></div><span class="bp-mega__promo-cta">Shop Now</span> 13001 </a> 13002</div> 13003 13004</div></div> 13005 </div> 13006 </div></li><li class="bp-mm-item" data-bp-mm-id="AdVdsY2tEV2JFWlVvV__bp_menu_shoes-1"><button type="button" class="bp-mm-item__row" data-open-view="AdVdsY2tEV2JFWlVvV__bp_menu_shoes-1">
13007 <span class="bp-mm-item__label">Shoes & Accessories</span> 13008 <span class="bp-mm-item__chevron" aria-hidden="true"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 13009 <path d="M11 5.5L7 9.5L3 5.5" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 13010</svg> 13011</span> 13012 </button> 13013 13014 <section class="bp-mm-view" data-view="AdVdsY2tEV2JFWlVvV__bp_menu_shoes-1" hidden> 13015 <header class="bp-mm-view__header"> 13016 <button type="button" class="bp-mm-view__back" data-back> 13017 <span class="bp-mm-view__back-icon" aria-hidden="true"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 13018 <path d="M11 5.5L7 9.5L3 5.5" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 13019</svg> 13020</span> 13021 <span>Shoes & Accessories</span> 13022 </button> 13023 <button type="button" class="bp-mm-view__close" data-close aria-label="Close menu"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 13024 <path d="M12 2L2 12" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 13025 <path d="M12 12L2 2" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 13026</svg> 13027</button> 13028 </header><div class="bp-mm-view__sections"><section class="bp-mm-section"> 13029 <p class="bp-mm-section__heading"><a href="/collections/shoes">Shoes</a></p><ul class="bp-mm-section__links"><li><a href="/collections/shoes">All Shoes</a></li><li><a href="/collections/shoes-boots">Boots & Booties</a></li><li><a href="/collections/shoes-heels">Heels</a></li><li><a href="/collections/shoes-sandals">Sandals</a></li><li><a href="/collections/shoes-sneakers">Sneakers</a></li><li><a href="/collections/shoes-wedges">Wedges</a></li></ul></section><section class="bp-mm-section"> 13030 <p class="bp-mm-section__heading"><a href="/collections/accessories">Accessories</a></p><ul class="bp-mm-section__links"><li><a href="/collections/accessories">All Accessories</a></li><li><a href="/collections/accessories-bags">Bags</a></li><li><a href="/collections/accessories-belts">Belts</a></li><li><a href="/collections/accessories-hats">Hats</a></li><li><a href="/collections/accessories-jewelry">Jewelry</a></li><li><a href="/collections/brands-we-love-fifth-and-ninth">Sunglasses</a></li></ul></section><section class="bp-mm-section"> 13031 <p class="bp-mm-section__heading"><a href="/collections/all-brands-we-love">Brands We Love</a></p><ul class="bp-mm-section__links"><li><a href="/collections/brands-we-love-amsterdam-heritage">Amsterdam Heritage</a></li><li><a href="/collections/brands-we-love-ana-luisa">Ana Luisa</a></li><li><a href="/collections/brands-we-love-ettika">Ettika</a></li><li><a href="/collections/brands-we-love-fifth-and-ninth">Fifth & Ninth</a></li><li><a href="/collections/brands-we-love-hanky-panky">Hanky Panky</a></li><li><a href="/collections/brands-we-love-hat-attack-new-york">Hat Attack</a></li><li><a href="/collections/brands-we-love-jardin-jewelry">Jardin Jewelry</a></li><li><a href="/collections/brands-we-love-miami-chains-co">Miami Chains & Co.</a></li><li><a href="/collections/brands-we-love-st-moran">St. Moran</a></li><li><a href="/collections/brands-we-love-the-sak">The Sak</a></li><li><a href="/collections/brands-we-love-t-jazelle">T. Jazelle</a></li><li><a href="/collections/brands-we-love-w-y-e-t-h">WYETH</a></li></ul></section></div><div class="bp-mm-view__promos"><div class="bp-mega__promo"> 13032 <a href="/collections/shoes-boots" class="bp-mega__promo-link"> 13033 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/Image_63.jpg?v=1790780283&width=400" alt="Boots" srcset="//www.bostonproper.com/cdn/shop/files/Image_63.jpg?v=1790780283&width=200 200w, //www.bostonproper.com/cdn/shop/files/Image_63.jpg?v=1790780283&width=400 400w" width="400" height="500" loading="lazy"> 13034</div> 13035 <h4 class="bp-mega__promo-title">Boots</h4><div class="bp-mega__promo-copy"><p>These boots are going places. Wild prints. Rich color stories. Versatile lengths. A new look for every mood.<br/><br/></p></div><span class="bp-mega__promo-cta">Shop Now</span> 13036 </a> 13037</div> 13038 13039<div class="bp-mega__promo"> 13040 <a href="/collections/accessories-jewelry" class="bp-mega__promo-link"> 13041 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/Image_74.jpg?v=1790800759&width=400" alt="Jewelry" srcset="//www.bostonproper.com/cdn/shop/files/Image_74.jpg?v=1790800759&width=200 200w, //www.bostonproper.com/cdn/shop/files/Image_74.jpg?v=1790800759&width=400 400w" width="400" height="500" loading="lazy"> 13042</div> 13043 <h4 class="bp-mega__promo-title">Jewelry</h4><div class="bp-mega__promo-copy"><p>Drenched in the details that define Boston Proper. Layered jewels and accent pieces that set the tone. </p></div><span class="bp-mega__promo-cta">Shop Now</span> 13044 </a> 13045</div> 13046 13047</div></section></li> 13048 13049 13050 13051<li 13052 role="presentation" 13053 class="menu-list__list-item bp-menu-item-li" 13054 data-bp-menu-item 13055 13056><a 13057 href="/collections/sale-clearance" 13058 data-skip-node-update="true" 13059 class="menu-list__link" 13060 aria-controls="bp-submenu-AVFlZUDZaR3FYUVAyd__bp_menu_sale-1" 13061 aria-haspopup="true" 13062 aria-expanded="false" 13063 ref="menuitem" 13064 >
13065 <span class="menu-list__link-title">Sale & Clearance</span> 13066 </a> 13067 <div class="bp-mega-submenu" id="bp-submenu-AVFlZUDZaR3FYUVAyd__bp_menu_sale-1"> 13068 <div class="bp-mega-submenu__inner"> 13069 <div class="bp-mega bp-mega--columns-promos"><div class="bp-mega__columns"><div class="bp-mega__column"> 13070 <p class="bp-mega__heading"><a href="/collections/sale-clearance">Sale & Clearance</a></p><ul class="bp-mega__links"><li><a href="/collections/sale-clearance">Shop All</a></li><li><a href="/collections/new-markdowns">New Markdowns</a></li><li><a href="/collections/sale">Sale</a></li><li><a href="/collections/clearance">Clearance</a></li></ul></div><div class="bp-mega__column"> 13071 <p class="bp-mega__heading"><a href="/collections/sale-clearance">Shop By Category</a></p><ul class="bp-mega__links"><li><a href="/collections/sale-clearance-accessories">Accessories</a></li><li><a href="/collections/sale-clearance-athleisure">Athleisure</a></li><li><a href="/collections/sale-clearance-dresses">Dresses</a></li><li><a href="/collections/sale-clearance-jackets">Jackets & Blazers</a></li><li><a href="/collections/sale-clearance-jeans">Jeans</a></li><li><a href="/collections/sale-clearance-outerwear">Outerwear</a></li><li><a href="/collections/sale-clearance-pants-leggings">Pants & Leggings</a></li><li><a href="/collections/sale-clearance-shoes">Shoes</a></li><li><a href="/collections/sale-clearance-shorts">Shorts</a></li><li><a href="/collections/sale-clearance-skirts">Skirts</a></li><li><a href="/collections/sale-clearance-sweaters">Sweaters</a></li><li><a href="/collections/sale-clearance-swimwear">Swimwear</a></li><li><a href="/collections/sale-clearance-tops">Tops</a></li></ul></div><div class="bp-mega__column"> 13072 <p class="bp-mega__heading"><a href="/collections/sale-clearance">Promotions</a></p><ul class="bp-mega__links"><li><a href="/collections/new-arrivals">20% off $175+ plus free shipping</a></li><li><a href="/collections/winter-classic-rib-styles">Save $45 on 3 or $25 on 2</a></li><li><a href="/products/boston-proper-printed-scarf-with-love-bp">Free gift with purchase of $400+</a></li></ul></div></div><div class="bp-mega__promos"><div class="bp-mega__promo"> 13073 <a href="/collections/new-markdowns" class="bp-mega__promo-link"> 13074 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/Image_b520536c-2111-43e4-b1e8-166793f8fe43.jpg?v=1790704244&width=400" alt="New Markdowns" srcset="//www.bostonproper.com/cdn/shop/files/Image_b520536c-2111-43e4-b1e8-166793f8fe43.jpg?v=1790704244&width=200 200w, //www.bostonproper.com/cdn/shop/files/Image_b520536c-2111-43e4-b1e8-166793f8fe43.jpg?v=1790704244&width=400 400w" width="400" height="500" loading="lazy"> 13075</div> 13076 <h4 class="bp-mega__promo-title">New Markdowns</h4><div class="bp-mega__promo-copy"><p>Rare finds. Newly reduced.</p></div><span class="bp-mega__promo-cta">Shop Now</span> 13077 </a> 13078</div> 13079 13080<div class="bp-mega__promo"> 13081 <a href="/collections/clearance" class="bp-mega__promo-link"> 13082 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/Image-1.jpg?v=1790780282&width=400" alt="Clearance" srcset="//www.bostonproper.com/cdn/shop/files/Image-1.jpg?v=1790780282&width=200 200w, //www.bostonproper.com/cdn/shop/files/Image-1.jpg?v=1790780282&width=400 400w" width="400" height="500" loading="lazy"> 13083</div> 13084 <h4 class="bp-mega__promo-title">Clearance</h4><div class="bp-mega__promo-copy"><p>Your size wonât stay here long.</p></div><span class="bp-mega__promo-cta">Shop Now</span> 13085 </a> 13086</div> 13087 13088</div></div> 13089 </div> 13090 </div></li><li class="bp-mm-item" data-bp-mm-id="AVFlZUDZaR3FYUVAyd__bp_menu_sale-1"><button type="button" class="bp-mm-item__row" data-open-view="AVFlZUDZaR3FYUVAyd__bp_menu_sale-1"> 13091 <span class="bp-mm-item__label">Sale & Clearance</span> 13092 <span class="bp-mm-item__chevron" aria-hidden="true"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 13093 <path d="M11 5.5L7 9.5L3 5.5" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 13094</svg> 13095</span> 13096 </button> 13097 13098 <section class="bp-mm-view" data-view="AVFlZUDZaR3FYUVAyd__bp_menu_sale-1" hidden> 13099 <header class="bp-mm-view__header"> 13100 <button type="button" class="bp-mm-view__back" data-back>
13101 <span class="bp-mm-view__back-icon" aria-hidden="true"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 13102 <path d="M11 5.5L7 9.5L3 5.5" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 13103</svg> 13104</span> 13105 <span>Sale & Clearance</span> 13106 </button> 13107 <button type="button" class="bp-mm-view__close" data-close aria-label="Close menu"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 13108 <path d="M12 2L2 12" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 13109 <path d="M12 12L2 2" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 13110</svg> 13111</button> 13112 </header><div class="bp-mm-view__sections"><section class="bp-mm-section"> 13113 <p class="bp-mm-section__heading"><a href="/collections/sale-clearance">Sale & Clearance</a></p><ul class="bp-mm-section__links"><li><a href="/collections/sale-clearance">Shop All</a></li><li><a href="/collections/new-markdowns">New Markdowns</a></li><li><a href="/collections/sale">Sale</a></li><li><a href="/collections/clearance">Clearance</a></li></ul></section><section class="bp-mm-section"> 13114 <p class="bp-mm-section__heading"><a href="/collections/sale-clearance">Shop By Category</a></p><ul class="bp-mm-section__links"><li><a href="/collections/sale-clearance-accessories">Accessories</a></li><li><a href="/collections/sale-clearance-athleisure">Athleisure</a></li><li><a href="/collections/sale-clearance-dresses">Dresses</a></li><li><a href="/collections/sale-clearance-jackets">Jackets & Blazers</a></li><li><a href="/collections/sale-clearance-jeans">Jeans</a></li><li><a href="/collections/sale-clearance-outerwear">Outerwear</a></li><li><a href="/collections/sale-clearance-pants-leggings">Pants & Leggings</a></li><li><a href="/collections/sale-clearance-shoes">Shoes</a></li><li><a href="/collections/sale-clearance-shorts">Shorts</a></li><li><a href="/collections/sale-clearance-skirts">Skirts</a></li><li><a href="/collections/sale-clearance-sweaters">Sweaters</a></li><li><a href="/collections/sale-clearance-swimwear">Swimwear</a></li><li><a href="/collections/sale-clearance-tops">Tops</a></li></ul></section><section class="bp-mm-section"> 13115 <p class="bp-mm-section__heading"><a href="/collections/sale-clearance">Promotions</a></p><ul class="bp-mm-section__links"><li><a href="/collections/new-arrivals">20% off $175+ plus free shipping</a></li><li><a href="/collections/winter-classic-rib-styles">Save $45 on 3 or $25 on 2</a></li><li><a href="/products/boston-proper-printed-scarf-with-love-bp">Free gift with purchase of $400+</a></li></ul></section></div><div class="bp-mm-view__promos"><div class="bp-mega__promo"> 13116 <a href="/collections/new-markdowns" class="bp-mega__promo-link"> 13117 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/Image_b520536c-2111-43e4-b1e8-166793f8fe43.jpg?v=1790704244&width=400" alt="New Markdowns" srcset="//www.bostonproper.com/cdn/shop/files/Image_b520536c-2111-43e4-b1e8-166793f8fe43.jpg?v=1790704244&width=200 200w, //www.bostonproper.com/cdn/shop/files/Image_b520536c-2111-43e4-b1e8-166793f8fe43.jpg?v=1790704244&width=400 400w" width="400" height="500" loading="lazy"> 13118</div> 13119 <h4 class="bp-mega__promo-title">New Markdowns</h4><div class="bp-mega__promo-copy"><p>Rare finds. Newly reduced.</p></div><span class="bp-mega__promo-cta">Shop Now</span> 13120 </a> 13121</div> 13122 13123<div class="bp-mega__promo"> 13124 <a href="/collections/clearance" class="bp-mega__promo-link"> 13125 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/Image-1.jpg?v=1790780282&width=400" alt="Clearance" srcset="//www.bostonproper.com/cdn/shop/files/Image-1.jpg?v=1790780282&width=200 200w, //www.bostonproper.com/cdn/shop/files/Image-1.jpg?v=1790780282&width=400 400w" width="400" height="500" loading="lazy"> 13126</div> 13127 <h4 class="bp-mega__promo-title">Clearance</h4><div class="bp-mega__promo-copy"><p>Your size wonât stay here long.</p></div><span class="bp-mega__promo-cta">Shop Now</span> 13128 </a> 13129</div> 13130 13131</div></section></li> 13132 13133 13134 13135<li 13136 role="presentation" 13137 class="menu-list__list-item bp-menu-item-li" 13138 data-bp-menu-item 13139 13140><a 13141 href="/blogs/styleedit" 13142 data-skip-node-update="true" 13143 class="menu-list__link" 13144 aria-controls="bp-submenu-ATVRxYnVkRFJjc0I3R__bp_menu_styleedit-1" 13145 aria-haspopup="true" 13146 aria-expanded="false" 13147 ref="menuitem" 13148 >
13149 <span class="menu-list__link-title">styleEDIT</span> 13150 </a> 13151 <div class="bp-mega-submenu" id="bp-submenu-ATVRxYnVkRFJjc0I3R__bp_menu_styleedit-1"> 13152 <div class="bp-mega-submenu__inner"> 13153 <div class="bp-mega bp-mega--columns-promos"><div class="bp-mega__columns"><div class="bp-mega__column"> 13154 <p class="bp-mega__heading"><a href="/blogs/styleedit">styleEDIT: What's New</a></p><ul class="bp-mega__links"><li><a href="/pages/new-to-boston-proper">New to Boston Proper? Start Here</a></li><li><a href="/blogs/styleedit/defy-all-expectations-why-style-doesn-t-expire-it-evolves">Why Style Doesn't Expire. It Evolves.</a></li><li><a href="https://www.bostonproper.com/blogs/styleedit/denim-dresses-are-back-here-s-how-to-wear-them-now">How to Wear Denim Dresses</a></li><li><a href="/blogs/styleedit">Read All</a></li></ul></div><div class="bp-mega__column"> 13155 <p class="bp-mega__heading"><a href="/collections/new-arrivals">Trending Now</a></p><ul class="bp-mega__links"><li><a href="/collections/featured-shop-animal-prints">Animal Prints</a></li><li><a href="/collections/featured-shops-matching-sets">Matching Sets</a></li><li><a href="/collections/color-crush-wine">Trending Color: Wine</a></li><li><a href="/collections/featured-shop-intricate-details">Intricate Details</a></li><li><a href="/pages/the-jacket-edit">Style Guide: The Jacket Edit</a></li></ul></div><div class="bp-mega__column"> 13156 <p class="bp-mega__heading"><a href="/blogs/styleedit">Discover</a></p><ul class="bp-mega__links"><li><a href="/pages/behind-the-seams">Behind the Seams</a></li><li><a href="/pages/sheryls-loves">Sheryl's Loves</a></li><li><a href="/pages/digital-catalog">Digital Catalog</a></li></ul></div></div><div class="bp-mega__promos"><div class="bp-mega__promo"> 13157 <a href="/pages/sheryls-loves" class="bp-mega__promo-link"> 13158 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/Image_65.jpg?v=1790780283&width=400" alt="Sherylâs Loves" srcset="//www.bostonproper.com/cdn/shop/files/Image_65.jpg?v=1790780283&width=200 200w, //www.bostonproper.com/cdn/shop/files/Image_65.jpg?v=1790780283&width=400 400w" width="400" height="500" loading="lazy"> 13159</div> 13160 <h4 class="bp-mega__promo-title">Sherylâs Loves</h4><div class="bp-mega__promo-copy"><p>âThese pieces feel so me. Feminine, confident, and exactly how I want to show up right now.â â¨âSheryl, Editor-in-Chic </p></div><span class="bp-mega__promo-cta">Shop Now</span> 13161 </a> 13162</div> 13163 13164<div class="bp-mega__promo"> 13165 <a href="/pages/digital-catalog" class="bp-mega__promo-link"> 13166 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/Image_75.jpg?v=1790800820&width=400" alt="Digital Catalog" srcset="//www.bostonproper.com/cdn/shop/files/Image_75.jpg?v=1790800820&width=200 200w, //www.bostonproper.com/cdn/shop/files/Image_75.jpg?v=1790800820&width=400 400w" width="400" height="500" loading="lazy"> 13167</div> 13168 <h4 class="bp-mega__promo-title">Digital Catalog</h4><div class="bp-mega__promo-copy"><p>Turn the page. Your best style era starts here. </p></div><span class="bp-mega__promo-cta">Shop Now</span> 13169 </a> 13170</div> 13171 13172</div></div> 13173 </div> 13174 </div></li><li class="bp-mm-item" data-bp-mm-id="ATVRxYnVkRFJjc0I3R__bp_menu_styleedit-1"><button type="button" class="bp-mm-item__row" data-open-view="ATVRxYnVkRFJjc0I3R__bp_menu_styleedit-1"> 13175 <span class="bp-mm-item__label">styleEDIT</span> 13176 <span class="bp-mm-item__chevron" aria-hidden="true"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 13177 <path d="M11 5.5L7 9.5L3 5.5" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 13178</svg> 13179</span> 13180 </button> 13181 13182 <section class="bp-mm-view" data-view="ATVRxYnVkRFJjc0I3R__bp_menu_styleedit-1" hidden> 13183 <header class="bp-mm-view__header"> 13184 <button type="button" class="bp-mm-view__back" data-back> 13185 <span class="bp-mm-view__back-icon" aria-hidden="true"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 13186 <path d="M11 5.5L7 9.5L3 5.5" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 13187</svg> 13188</span>
13189 <span>styleEDIT</span> 13190 </button> 13191 <button type="button" class="bp-mm-view__close" data-close aria-label="Close menu"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 13192 <path d="M12 2L2 12" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 13193 <path d="M12 12L2 2" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 13194</svg> 13195</button> 13196 </header><div class="bp-mm-view__sections"><section class="bp-mm-section"> 13197 <p class="bp-mm-section__heading"><a href="/blogs/styleedit">styleEDIT: What's New</a></p><ul class="bp-mm-section__links"><li><a href="/pages/new-to-boston-proper">New to Boston Proper? Start Here</a></li><li><a href="/blogs/styleedit/defy-all-expectations-why-style-doesn-t-expire-it-evolves">Why Style Doesn't Expire. It Evolves.</a></li><li><a href="https://www.bostonproper.com/blogs/styleedit/denim-dresses-are-back-here-s-how-to-wear-them-now">How to Wear Denim Dresses</a></li><li><a href="/blogs/styleedit">Read All</a></li></ul></section><section class="bp-mm-section"> 13198 <p class="bp-mm-section__heading"><a href="/collections/new-arrivals">Trending Now</a></p><ul class="bp-mm-section__links"><li><a href="/collections/featured-shop-animal-prints">Animal Prints</a></li><li><a href="/collections/featured-shops-matching-sets">Matching Sets</a></li><li><a href="/collections/color-crush-wine">Trending Color: Wine</a></li><li><a href="/collections/featured-shop-intricate-details">Intricate Details</a></li><li><a href="/pages/the-jacket-edit">Style Guide: The Jacket Edit</a></li></ul></section><section class="bp-mm-section"> 13199 <p class="bp-mm-section__heading"><a href="/blogs/styleedit">Discover</a></p><ul class="bp-mm-section__links"><li><a href="/pages/behind-the-seams">Behind the Seams</a></li><li><a href="/pages/sheryls-loves">Sheryl's Loves</a></li><li><a href="/pages/digital-catalog">Digital Catalog</a></li></ul></section></div><div class="bp-mm-view__promos"><div class="bp-mega__promo"> 13200 <a href="/pages/sheryls-loves" class="bp-mega__promo-link"> 13201 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/Image_65.jpg?v=1790780283&width=400" alt="Sherylâs Loves" srcset="//www.bostonproper.com/cdn/shop/files/Image_65.jpg?v=1790780283&width=200 200w, //www.bostonproper.com/cdn/shop/files/Image_65.jpg?v=1790780283&width=400 400w" width="400" height="500" loading="lazy"> 13202</div> 13203 <h4 class="bp-mega__promo-title">Sherylâs Loves</h4><div class="bp-mega__promo-copy"><p>âThese pieces feel so me. Feminine, confident, and exactly how I want to show up right now.â â¨âSheryl, Editor-in-Chic </p></div><span class="bp-mega__promo-cta">Shop Now</span> 13204 </a> 13205</div> 13206 13207<div class="bp-mega__promo"> 13208 <a href="/pages/digital-catalog" class="bp-mega__promo-link"> 13209 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/Image_75.jpg?v=1790800820&width=400" alt="Digital Catalog" srcset="//www.bostonproper.com/cdn/shop/files/Image_75.jpg?v=1790800820&width=200 200w, //www.bostonproper.com/cdn/shop/files/Image_75.jpg?v=1790800820&width=400 400w" width="400" height="500" loading="lazy"> 13210</div> 13211 <h4 class="bp-mega__promo-title">Digital Catalog</h4><div class="bp-mega__promo-copy"><p>Turn the page. Your best style era starts here. </p></div><span class="bp-mega__promo-cta">Shop Now</span> 13212 </a> 13213</div> 13214 13215</div></section></li> 13216 13217 13218 13219<li 13220 role="presentation" 13221 class="menu-list__list-item bp-menu-item-li" 13222 data-bp-menu-item 13223 13224><a 13225 href="/collections/brands-we-love" 13226 data-skip-node-update="true" 13227 class="menu-list__link" 13228 aria-controls="bp-submenu-AUm5TVW95VENzMmkzb__bp_menu_brands-1" 13229 aria-haspopup="true" 13230 aria-expanded="false" 13231 ref="menuitem" 13232 > 13233 <span class="menu-list__link-title">Brands We Love</span> 13234 </a> 13235 <div class="bp-mega-submenu" id="bp-submenu-AUm5TVW95VENzMmkzb__bp_menu_brands-1"> 13236 <div class="bp-mega-submenu__inner"> 13237 <div class="bp-mega bp-mega--tabs-promos"><div class="bp-mega__tabs" data-bp-mega-tabs data-bp-tab-columns="2"> 13238 <ul class="bp-mega__tabs-list" role="tablist"><li role="presentation"> 13239 <button 13240 type="button" 13241 role="tab" 13242 id="bp-submenu-AUm5TVW95VENzMmkzb__bp_menu_brands-1-tab-0" 13243 class="bp-mega__tab is-active" 13244 aria-controls="bp-submenu-AUm5TVW95VENzMmkzb__bp_menu_brands-1-panel-0" 13245 aria-selected="true" 13246 data-bp-mega-tab 13247 >
13248 <span class="bp-mega__tab-label">BRANDS WE LOVE</span> 13249 <span class="bp-mega__tab-chevron" aria-hidden="true"> 13250 <svg width="20" height="20" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg"> 13251 <path d="M23.8674 11.8674C23.9116 11.8244 23.9558 11.7814 24 11.7385C24.0872 11.8256 24.1744 11.9128 24.2615 12C24.2186 12.0442 24.1756 12.0884 24.1326 12.1326C23.8696 12.4029 23.6066 12.6733 23.3436 12.9436C20.9768 15.3768 18.6099 17.8099 16.2431 20.2431V19.7569C18.6762 22.1238 21.1094 24.4906 23.5425 26.8575C23.8129 27.1205 24.0832 27.3835 24.3536 27.6464C24.473 27.7627 24.5925 27.8789 24.712 27.9951C24.473 28.2341 24.2341 28.473 23.9951 28.712C23.8789 28.5925 23.7627 28.473 23.6464 28.3536C23.3835 28.0832 23.1205 27.8129 22.8575 27.5425C20.4906 25.1094 18.1238 22.6762 15.7569 20.2431L15.514 19.9933L15.7569 19.7569C18.1901 17.3901 20.6232 15.0232 23.0564 12.6564C23.3267 12.3934 23.5971 12.1304 23.8674 11.8674Z" fill="currentColor"/> 13252 <path d="M23.6464 11.6464C23.7627 11.527 23.8789 11.4075 23.9951 11.288C24.2341 11.527 24.473 11.7659 24.712 12.0049C24.5925 12.1211 24.473 12.2373 24.3536 12.3536C24.0832 12.6165 23.8129 12.8795 23.5425 13.1425C21.1094 15.5094 18.6762 17.8762 16.2431 20.2431V19.7569C18.6099 22.1901 20.9768 24.6232 23.3436 27.0564C23.6066 27.3267 23.8696 27.5971 24.1326 27.8674C24.1756 27.9116 24.2186 27.9558 24.2615 28C24.1744 28.0872 24.0872 28.1744 24 28.2615C23.9558 28.2186 23.9116 28.1756 23.8674 28.1326C23.5971 27.8696 23.3267 27.6066 23.0564 27.3436C20.6232 24.9768 18.1901 22.6099 15.7569 20.2431L15.514 19.9933L15.7569 19.7569C18.1238 17.3238 20.4906 14.8906 22.8575 12.4575C23.1205 12.1871 23.3835 11.9168 23.6464 11.6464Z" fill="currentColor"/> 13253 </svg> 13254 </span> 13255 </button> 13256 </li><li role="presentation"> 13257 <button 13258 type="button" 13259 role="tab" 13260 id="bp-submenu-AUm5TVW95VENzMmkzb__bp_menu_brands-1-tab-1" 13261 class="bp-mega__tab" 13262 aria-controls="bp-submenu-AUm5TVW95VENzMmkzb__bp_menu_brands-1-panel-1" 13263 aria-selected="false" 13264 data-bp-mega-tab 13265 > 13266 <span class="bp-mega__tab-label">JEWELRY & ACCESSORIES</span> 13267 <span class="bp-mega__tab-chevron" aria-hidden="true"> 13268 <svg width="20" height="20" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg"> 13269 <path d="M23.8674 11.8674C23.9116 11.8244 23.9558 11.7814 24 11.7385C24.0872 11.8256 24.1744 11.9128 24.2615 12C24.2186 12.0442 24.1756 12.0884 24.1326 12.1326C23.8696 12.4029 23.6066 12.6733 23.3436 12.9436C20.9768 15.3768 18.6099 17.8099 16.2431 20.2431V19.7569C18.6762 22.1238 21.1094 24.4906 23.5425 26.8575C23.8129 27.1205 24.0832 27.3835 24.3536 27.6464C24.473 27.7627 24.5925 27.8789 24.712 27.9951C24.473 28.2341 24.2341 28.473 23.9951 28.712C23.8789 28.5925 23.7627 28.473 23.6464 28.3536C23.3835 28.0832 23.1205 27.8129 22.8575 27.5425C20.4906 25.1094 18.1238 22.6762 15.7569 20.2431L15.514 19.9933L15.7569 19.7569C18.1901 17.3901 20.6232 15.0232 23.0564 12.6564C23.3267 12.3934 23.5971 12.1304 23.8674 11.8674Z" fill="currentColor"/> 13270 <path d="M23.6464 11.6464C23.7627 11.527 23.8789 11.4075 23.9951 11.288C24.2341 11.527 24.473 11.7659 24.712 12.0049C24.5925 12.1211 24.473 12.2373 24.3536 12.3536C24.0832 12.6165 23.8129 12.8795 23.5425 13.1425C21.1094 15.5094 18.6762 17.8762 16.2431 20.2431V19.7569C18.6099 22.1901 20.9768 24.6232 23.3436 27.0564C23.6066 27.3267 23.8696 27.5971 24.1326 27.8674C24.1756 27.9116 24.2186 27.9558 24.2615 28C24.1744 28.0872 24.0872 28.1744 24 28.2615C23.9558 28.2186 23.9116 28.1756 23.8674 28.1326C23.5971 27.8696 23.3267 27.6066 23.0564 27.3436C20.6232 24.9768 18.1901 22.6099 15.7569 20.2431L15.514 19.9933L15.7569 19.7569C18.1238 17.3238 20.4906 14.8906 22.8575 12.4575C23.1205 12.1871 23.3835 11.9168 23.6464 11.6464Z" fill="currentColor"/> 13271 </svg> 13272 </span> 13273 </button> 13274 </li><li role="presentation"> 13275 <button 13276 type="button" 13277 role="tab" 13278 id="bp-submenu-AUm5TVW95VENzMmkzb__bp_menu_brands-1-tab-2" 13279 class="bp-mega__tab" 13280 aria-controls="bp-submenu-AUm5TVW95VENzMmkzb__bp_menu_brands-1-panel-2" 13281 aria-selected="false" 13282 data-bp-mega-tab 13283 >
13284 <span class="bp-mega__tab-label">ATHLEISURE</span> 13285 <span class="bp-mega__tab-chevron" aria-hidden="true"> 13286 <svg width="20" height="20" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg"> 13287 <path d="M23.8674 11.8674C23.9116 11.8244 23.9558 11.7814 24 11.7385C24.0872 11.8256 24.1744 11.9128 24.2615 12C24.2186 12.0442 24.1756 12.0884 24.1326 12.1326C23.8696 12.4029 23.6066 12.6733 23.3436 12.9436C20.9768 15.3768 18.6099 17.8099 16.2431 20.2431V19.7569C18.6762 22.1238 21.1094 24.4906 23.5425 26.8575C23.8129 27.1205 24.0832 27.3835 24.3536 27.6464C24.473 27.7627 24.5925 27.8789 24.712 27.9951C24.473 28.2341 24.2341 28.473 23.9951 28.712C23.8789 28.5925 23.7627 28.473 23.6464 28.3536C23.3835 28.0832 23.1205 27.8129 22.8575 27.5425C20.4906 25.1094 18.1238 22.6762 15.7569 20.2431L15.514 19.9933L15.7569 19.7569C18.1901 17.3901 20.6232 15.0232 23.0564 12.6564C23.3267 12.3934 23.5971 12.1304 23.8674 11.8674Z" fill="currentColor"/> 13288 <path d="M23.6464 11.6464C23.7627 11.527 23.8789 11.4075 23.9951 11.288C24.2341 11.527 24.473 11.7659 24.712 12.0049C24.5925 12.1211 24.473 12.2373 24.3536 12.3536C24.0832 12.6165 23.8129 12.8795 23.5425 13.1425C21.1094 15.5094 18.6762 17.8762 16.2431 20.2431V19.7569C18.6099 22.1901 20.9768 24.6232 23.3436 27.0564C23.6066 27.3267 23.8696 27.5971 24.1326 27.8674C24.1756 27.9116 24.2186 27.9558 24.2615 28C24.1744 28.0872 24.0872 28.1744 24 28.2615C23.9558 28.2186 23.9116 28.1756 23.8674 28.1326C23.5971 27.8696 23.3267 27.6066 23.0564 27.3436C20.6232 24.9768 18.1901 22.6099 15.7569 20.2431L15.514 19.9933L15.7569 19.7569C18.1238 17.3238 20.4906 14.8906 22.8575 12.4575C23.1205 12.1871 23.3835 11.9168 23.6464 11.6464Z" fill="currentColor"/> 13289 </svg> 13290 </span> 13291 </button> 13292 </li><li role="presentation"> 13293 <button 13294 type="button" 13295 role="tab" 13296 id="bp-submenu-AUm5TVW95VENzMmkzb__bp_menu_brands-1-tab-3" 13297 class="bp-mega__tab" 13298 aria-controls="bp-submenu-AUm5TVW95VENzMmkzb__bp_menu_brands-1-panel-3" 13299 aria-selected="false" 13300 data-bp-mega-tab 13301 > 13302 <span class="bp-mega__tab-label">HEALTH & BEAUTY</span> 13303 <span class="bp-mega__tab-chevron" aria-hidden="true"> 13304 <svg width="20" height="20" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg"> 13305 <path d="M23.8674 11.8674C23.9116 11.8244 23.9558 11.7814 24 11.7385C24.0872 11.8256 24.1744 11.9128 24.2615 12C24.2186 12.0442 24.1756 12.0884 24.1326 12.1326C23.8696 12.4029 23.6066 12.6733 23.3436 12.9436C20.9768 15.3768 18.6099 17.8099 16.2431 20.2431V19.7569C18.6762 22.1238 21.1094 24.4906 23.5425 26.8575C23.8129 27.1205 24.0832 27.3835 24.3536 27.6464C24.473 27.7627 24.5925 27.8789 24.712 27.9951C24.473 28.2341 24.2341 28.473 23.9951 28.712C23.8789 28.5925 23.7627 28.473 23.6464 28.3536C23.3835 28.0832 23.1205 27.8129 22.8575 27.5425C20.4906 25.1094 18.1238 22.6762 15.7569 20.2431L15.514 19.9933L15.7569 19.7569C18.1901 17.3901 20.6232 15.0232 23.0564 12.6564C23.3267 12.3934 23.5971 12.1304 23.8674 11.8674Z" fill="currentColor"/> 13306 <path d="M23.6464 11.6464C23.7627 11.527 23.8789 11.4075 23.9951 11.288C24.2341 11.527 24.473 11.7659 24.712 12.0049C24.5925 12.1211 24.473 12.2373 24.3536 12.3536C24.0832 12.6165 23.8129 12.8795 23.5425 13.1425C21.1094 15.5094 18.6762 17.8762 16.2431 20.2431V19.7569C18.6099 22.1901 20.9768 24.6232 23.3436 27.0564C23.6066 27.3267 23.8696 27.5971 24.1326 27.8674C24.1756 27.9116 24.2186 27.9558 24.2615 28C24.1744 28.0872 24.0872 28.1744 24 28.2615C23.9558 28.2186 23.9116 28.1756 23.8674 28.1326C23.5971 27.8696 23.3267 27.6066 23.0564 27.3436C20.6232 24.9768 18.1901 22.6099 15.7569 20.2431L15.514 19.9933L15.7569 19.7569C18.1238 17.3238 20.4906 14.8906 22.8575 12.4575C23.1205 12.1871 23.3835 11.9168 23.6464 11.6464Z" fill="currentColor"/> 13307 </svg> 13308 </span> 13309 </button> 13310 </li></ul> 13311 13312 <div class="bp-mega__tabs-panels"><div 13313 id="bp-submenu-AUm5TVW95VENzMmkzb__bp_menu_brands-1-panel-0" 13314 role="tabpanel" 13315 aria-labelledby="bp-submenu-AUm5TVW95VENzMmkzb__bp_menu_brands-1-tab-0" 13316 class="bp-mega__tab-panel is-active" 13317 data-bp-mega-panel 13318 13319 > 13320 <p class="bp-mega__heading"><a href="/pages/brands-we-love">BRANDS WE LOVE</a></p><ul class="bp-mega__links"><li><a href="/pages/brands-we-love">View All Brands</a></li><li><a href="/collections/all-brands-we-love">Shop All</a></li><li><a href="/collections/brands-we-love-amsterdam-heritage">Amsterdam Heritage</a></li><li><a href="/collections/brands-we-love-a-rose">A.Rose</a></li><li><a href="/collections/brands-we-love-ana-luisa">Ana Luisa</a></li><li><a href="/collections/brands-we-love-anatomie">Anatomie</a></li><li><a href="/collections/brands-we-love-becca">Becca By Rebecca Virtue</a></li><li><a href="/collections/brands-we-love-carmen-marc-valvo">Carmen Marc Valvo</a></li><li><a href="/collections/brands-we-love-change-of-scenery">
13320Change of Scenery</a></li><li><a href="/collections/brands-we-love-ettika">Ettika</a></li><li><a href="/collections/brands-we-love-fifth-and-ninth">Fifth & Ninth</a></li><li><a href="/collections/brands-we-love-hanky-panky">Hanky Panky</a></li><li><a href="/collections/brands-we-love-hat-attack-new-york">Hat Attack</a></li><li><a href="/collections/brands-we-love-jardin-jewelry">Jardin Jewelry</a></li><li><a href="/collections/brands-we-love-mdaire">MDAiRE</a></li><li><a href="/collections/brands-we-love-miami-chains-co">Miami Chains & Co.</a></li><li><a href="/collections/brands-we-love-mimosa-lane">Mimosa Lane</a></li><li><a href="/collections/brands-we-love-pickletini">Pickletini x Boston Proper</a></li><li><a href="/collections/brands-we-love-prolon-life">Prolon Life</a></li><li><a href="/collections/brands-we-love-pq-swim">PQ Swim X Boston Proper</a></li><li><a href="/collections/brands-we-love-saint-jo">Saint JÅ</a></li><li><a href="/collections/brands-we-love-st-moran">St. Moran</a></li><li><a href="/collections/brands-we-love-sum-of-all">Sum Of All</a></li><li><a href="/collections/brands-we-love-the-sak">The Sak</a></li><li><a href="/collections/brands-we-love-t-jazelle">T. Jazelle</a></li><li><a href="/collections/brands-we-love-trina-turk">Trina Turk</a></li><li><a href="/collections/brands-we-love-walter-baker">Walter Baker</a></li><li><a href="/collections/brands-we-love-w-y-e-t-h">WYETH</a></li></ul></div><div 13321 id="bp-submenu-AUm5TVW95VENzMmkzb__bp_menu_brands-1-panel-1" 13322 role="tabpanel" 13323 aria-labelledby="bp-submenu-AUm5TVW95VENzMmkzb__bp_menu_brands-1-tab-1" 13324 class="bp-mega__tab-panel" 13325 data-bp-mega-panel 13326 hidden 13327 > 13328 <p class="bp-mega__heading"><a href="/collections/brands-we-love-jewelry">JEWELRY & ACCESSORIES</a></p><ul class="bp-mega__links"><li><a href="/collections/brands-we-love-jewelry">Shop All</a></li><li><a href="/collections/brands-we-love-ana-luisa">Ana Luisa</a></li><li><a href="/collections/brands-we-love-amsterdam-heritage">Amsterdam Heritage</a></li><li><a href="/collections/brands-we-love-ettika">Ettika</a></li><li><a href="/collections/brands-we-love-st-moran">St. Moran</a></li><li><a href="/collections/brands-we-love-t-jazelle">T. Jazelle</a></li><li><a href="/collections/brands-we-love-fifth-and-ninth">Fifth & Ninth</a></li><li><a href="/collections/brands-we-love-hat-attack-new-york">Hat Attack</a></li><li><a href="/collections/brands-we-love-jardin-jewelry">Jardin Jewelry</a></li><li><a href="/collections/brands-we-love-miami-chains-co">Miami Chains & Co.</a></li><li><a href="/collections/brands-we-love-the-sak">The Sak</a></li><li><a href="/collections/brands-we-love-w-y-e-t-h">WYETH</a></li></ul></div><div 13329 id="bp-submenu-AUm5TVW95VENzMmkzb__bp_menu_brands-1-panel-2" 13330 role="tabpanel" 13331 aria-labelledby="bp-submenu-AUm5TVW95VENzMmkzb__bp_menu_brands-1-tab-2" 13332 class="bp-mega__tab-panel" 13333 data-bp-mega-panel 13334 hidden 13335 > 13336 <p class="bp-mega__heading"><a href="/collections/all-brands-we-love">ATHLEISURE</a></p><ul class="bp-mega__links"><li><a href="/collections/brands-we-love-anatomie">Anatomie</a></li><li><a href="/collections/brands-we-love-pickletini">Pickletini X Boston Proper</a></li></ul></div><div 13337 id="bp-submenu-AUm5TVW95VENzMmkzb__bp_menu_brands-1-panel-3" 13338 role="tabpanel" 13339 aria-labelledby="bp-submenu-AUm5TVW95VENzMmkzb__bp_menu_brands-1-tab-3" 13340 class="bp-mega__tab-panel" 13341 data-bp-mega-panel 13342 hidden 13343 > 13344 <p class="bp-mega__heading"><a href="/collections/brands-we-love-health-and-beauty">HEALTH & BEAUTY</a></p><ul class="bp-mega__links"><li><a href="/collections/brands-we-love-health-and-beauty">Shop All</a></li><li><a href="/collections/brands-we-love-mdaire">MDAiRE</a></li><li><a href="/collections/brands-we-love-mimosa-lane">Mimosa Lane</a></li><li><a href="/collections/brands-we-love-prolon-life">Prolon Life</a></li><li><a href="/collections/brands-we-love-saint-jo">SAINT JÅ</a></li><li><a href="/collections/brands-we-love-sum-of-all">Sum Of All</a></li></ul></div></div><div class="bp-mega__promos bp-mega__promos--tabs"><div class="bp-mega__promo"> 13345 <a href="/collections/brands-we-love-ettika" class="bp-mega__promo-link"> 13346 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/Ettika_flydown.jpg?v=1733336269&width=400" alt="Ettika" srcset="//www.bostonproper.com/cdn/shop/files/Ettika_flydown.jpg?v=1733336269&width=200 200w, //www.bostonproper.com/cdn/shop/files/Ettika_flydown.jpg?v=1733336269&width=400 400w" width="400" height="625" loading="lazy"> 13347</div> 13348 <h4 class="bp-mega__promo-title">Ettika</h4><span class="bp-mega__promo-cta">Shop Now</span> 13349 </a> 13350</div> 13351 13352<div class="bp-mega__promo"> 13353 <a href="/collections/brands-we-love-saint-jo" class="bp-mega__promo-link"> 13354 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/10271_Saint-Jo_flydown_3.jpg?v=1785254177&width=400" alt="SAINT JÅ" srcset="//www.bostonproper.com/cdn/shop/files/10271_Saint-Jo_flydown_3.jpg?v=1785254177&width=200 200w, //www.bostonproper.com/cdn/shop/files/10271_Saint-Jo_flydown_3.jpg?v=1785254177&width=400 400w" width="400" height="501" loading="lazy" style="object-position:50.0% 75.781%;"> 13355</div> 13356 <h4 class="bp-mega__promo-title">SAINT JÅ</h4><span class="bp-mega__promo-cta">Shop Now</span> 13357 </a> 13358</div> 13359 13360</div></div></div> 13361 </div> 13362 </div></li><li class="bp-mm-item" data-bp-mm-id="AUm5TVW95VENzMmkzb__bp_menu_brands-1"><button type="button" class="bp-mm-item__row" data-open-view="AUm5TVW95VENzMmkzb__bp_menu_brands-1">
13363 <span class="bp-mm-item__label">Brands We Love</span> 13364 <span class="bp-mm-item__chevron" aria-hidden="true"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 13365 <path d="M11 5.5L7 9.5L3 5.5" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 13366</svg> 13367</span> 13368 </button> 13369 13370 <section class="bp-mm-view" data-view="AUm5TVW95VENzMmkzb__bp_menu_brands-1" hidden> 13371 <header class="bp-mm-view__header"> 13372 <button type="button" class="bp-mm-view__back" data-back> 13373 <span class="bp-mm-view__back-icon" aria-hidden="true"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 13374 <path d="M11 5.5L7 9.5L3 5.5" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 13375</svg> 13376</span> 13377 <span>Brands We Love</span> 13378 </button> 13379 <button type="button" class="bp-mm-view__close" data-close aria-label="Close menu"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 13380 <path d="M12 2L2 12" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 13381 <path d="M12 12L2 2" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 13382</svg> 13383</button> 13384 </header><div class="bp-mm-view__sections"><section class="bp-mm-section"> 13385 <p class="bp-mm-section__heading"><a href="/pages/brands-we-love">BRANDS WE LOVE</a></p><ul class="bp-mm-section__links"><li><a href="/pages/brands-we-love">View All Brands</a></li><li><a href="/collections/all-brands-we-love">Shop All</a></li><li><a href="/collections/brands-we-love-amsterdam-heritage">Amsterdam Heritage</a></li><li><a href="/collections/brands-we-love-a-rose">A.Rose</a></li><li><a href="/collections/brands-we-love-ana-luisa">Ana Luisa</a></li><li><a href="/collections/brands-we-love-anatomie">Anatomie</a></li><li><a href="/collections/brands-we-love-becca">Becca By Rebecca Virtue</a></li><li><a href="/collections/brands-we-love-carmen-marc-valvo">Carmen Marc Valvo</a></li><li><a href="/collections/brands-we-love-change-of-scenery">Change of Scenery</a></li><li><a href="/collections/brands-we-love-ettika">Ettika</a></li><li><a href="/collections/brands-we-love-fifth-and-ninth">Fifth & Ninth</a></li><li><a href="/collections/brands-we-love-hanky-panky">Hanky Panky</a></li><li><a href="/collections/brands-we-love-hat-attack-new-york">Hat Attack</a></li><li><a href="/collections/brands-we-love-jardin-jewelry">Jardin Jewelry</a></li><li><a href="/collections/brands-we-love-mdaire">MDAiRE</a></li><li><a href="/collections/brands-we-love-miami-chains-co">Miami Chains & Co.</a></li><li><a href="/collections/brands-we-love-mimosa-lane">Mimosa Lane</a></li><li><a href="/collections/brands-we-love-pickletini">Pickletini x Boston Proper</a></li><li><a href="/collections/brands-we-love-prolon-life">Prolon Life</a></li><li><a href="/collections/brands-we-love-pq-swim">PQ Swim X Boston Proper</a></li><li><a href="/collections/brands-we-love-saint-jo">Saint JÅ</a></li><li><a href="/collections/brands-we-love-st-moran">St. Moran</a></li><li><a href="/collections/brands-we-love-sum-of-all">Sum Of All</a></li><li><a href="/collections/brands-we-love-the-sak">The Sak</a></li><li><a href="/collections/brands-we-love-t-jazelle">T. Jazelle</a></li><li><a href="/collections/brands-we-love-trina-turk">Trina Turk</a></li><li><a href="/collections/brands-we-love-walter-baker">Walter Baker</a></li><li><a href="/collections/brands-we-love-w-y-e-t-h">WYETH</a></li></ul></section><section class="bp-mm-section"> 13386 <p class="bp-mm-section__heading"><a href="/collections/brands-we-love-jewelry">JEWELRY & ACCESSORIES</a></p><ul class="bp-mm-section__links"><li><a href="/collections/brands-we-love-jewelry">Shop All</a></li><li><a href="/collections/brands-we-love-ana-luisa">Ana Luisa</a></li><li><a href="/collections/brands-we-love-amsterdam-heritage">Amsterdam Heritage</a></li><li><a href="/collections/brands-we-love-ettika">Ettika</a></li><li><a href="/collections/brands-we-love-st-moran">St. Moran</a></li><li><a href="/collections/brands-we-love-t-jazelle">T. Jazelle</a></li><li><a href="/collections/brands-we-love-fifth-and-ninth">Fifth & Ninth</a></li><li><a href="/collections/brands-we-love-hat-attack-new-york">Hat Attack</a></li><li><a href="/collections/brands-we-love-jardin-jewelry">Jardin Jewelry</a></li><li><a href="/collections/brands-we-love-miami-chains-co">Miami Chains & Co.</a></li><li><a href="/collections/brands-we-love-the-sak">The Sak</a></li><li><a href="/collections/brands-we-love-w-y-e-t-h">WYETH</a></li></ul></section><section class="bp-mm-section"> 13387 <p class="bp-mm-section__heading"><a href="/collections/all-brands-we-love">ATHLEISURE</a></p><ul class="bp-mm-section__links"><li><a href="/collections/brands-we-love-anatomie">Anatomie</a></li><li><a href="/collections/brands-we-love-pickletini">Pickletini X Boston Proper</a></li></ul></section><section class="bp-mm-section"> 13388 <p class="bp-mm-section__heading"><a href="/collections/brands-we-love-health-and-beauty">HEALTH & BEAUTY</a></p><ul class="bp-mm-section__links"><li><a href="/collections/brands-we-love-health-and-beauty">Shop All</a></li><li><a href="/collections/brands-we-love-mdaire">MDAiRE</a></li><li><a href="/collections/brands-we-love-mimosa-lane">Mimosa Lane</a></li><li><a href="/collections/brands-we-love-prolon-life">Prolon Life</a></li><li><a href="/collections/brands-we-love-saint-jo">SAINT JÅ</a></li><li><a href="/collections/brands-we-love-sum-of-all">Sum Of All</a></li></ul></section></div><div class="bp-mm-view__promos"><div class="bp-mega__promo"> 13389 <a href="/collections/brands-we-love-ettika" class="bp-mega__promo-link"> 13390 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/Ettika_flydown.jpg?v=1733336269&width=400" alt="Ettika" srcset="//www.bostonproper.com/cdn/shop/files/Ettika_flydown.jpg?v=1733336269&width=200 200w, //www.bostonproper.com/cdn/shop/files/Ettika_flydown.jpg?v=1733336269&width=400 400w" width="400" height="625" loading="lazy"> 13391</div> 13392 <h4 class="bp-mega__promo-title">Ettika</h4><span class="bp-mega__promo-cta">Shop Now</span> 13393 </a> 13394</div> 13395 13396<div class="bp-mega__promo"> 13397 <a href="/collections/brands-we-love-saint-jo" class="bp-mega__promo-link"> 13398 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/10271_Saint-Jo_flydown_3.jpg?v=1785254177&width=400" alt="SAINT JÅ" srcset="//www.bostonproper.com/cdn/shop/files/10271_Saint-Jo_flydown_3.jpg?v=1785254177&width=200 200w, //www.bostonproper.com/cdn/shop/files/10271_Saint-Jo_flydown_3.jpg?v=1785254177&width=400 400w" width="400" height="501" loading="lazy" style="object-position:50.0% 75.781%;"> 13399</div> 13400 <h4 class="bp-mega__promo-title">SAINT JÅ</h4><span class="bp-mega__promo-cta">Shop Now</span> 13401 </a> 13402</div> 13403 13404</div></section></li> 13405 13406 13407 13408 13409 13410 </ul> 13411 13412 <ul class="bp-mobile-drawer__rail"> 13413 <li> 13414 <a class="bp-mobile-drawer__rail-link" href="/account">
13415 <span class="bp-mobile-drawer__rail-icon" aria-hidden="true"><svg width="44" height="44" viewBox="0 0 44 44" fill="none" xmlns="http://www.w3.org/2000/svg"> 13416<path d="M16.2227 15.2379C16.2134 12.9677 17.7898 11.0652 19.936 10.3449C23.4796 9.10175 27.7591 11.331 27.7783 15.2379C27.7587 19.1448 23.4796 21.374 19.936 20.1309C17.7903 19.4105 16.2134 17.5081 16.2227 15.2379ZM18.0005 15.2379C17.992 16.8309 18.828 18.4907 20.3231 19.2146C21.3583 19.7286 22.6431 19.7286 23.6783 19.2146C25.1729 18.4907 26.0094 16.8309 26.0009 15.2379C26.0094 13.6448 25.1734 11.985 23.6783 11.2612C22.6431 10.7472 21.3583 10.7472 20.3231 11.2612C18.8285 11.985 17.992 13.6448 18.0005 15.2379Z" fill="currentColor"/> 13417<path d="M12 34C12.28 29.7933 13.4618 25.0731 17.7702 23.2244C19.1058 22.6506 20.5636 22.4433 22 22.4372C24.3076 22.4241 26.7129 23.0455 28.4364 24.6324C29.9582 26.0034 30.8698 27.9263 31.3693 29.8649C31.6809 31.0482 31.8529 32.2665 31.9596 33.4808C31.972 33.6536 31.9853 33.8268 32 34H29.7778C29.7956 33.733 29.8182 33.3246 29.824 33.051C29.8591 30.5425 29.4138 27.8399 27.6716 25.9069C26.6804 24.8318 25.348 24.1738 23.8938 23.9146C22.0573 23.6027 20.0453 23.6973 18.336 24.4605C17.4333 24.8698 16.6467 25.4863 16.0364 26.2503L15.9004 26.431C15.784 26.5771 15.6858 26.7421 15.5796 26.8965C15.5347 26.9698 15.4516 27.1186 15.408 27.191C15.3351 27.308 15.2636 27.4738 15.1973 27.5951C14.3849 29.2863 14.1489 31.1922 14.176 33.0514C14.1871 33.3176 14.1987 33.7352 14.2222 34.0004H12V34Z" fill="currentColor"/> 13418</svg> 13419</span> 13420 <span>Login</span> 13421 </a> 13422 </li> 13423 <li> 13424 <a class="bp-mobile-drawer__rail-link" href="/pages/favorites"> 13425 <span class="bp-mobile-drawer__rail-icon" aria-hidden="true"><svg width="44" height="44" viewBox="0 0 44 44" fill="none" xmlns="http://www.w3.org/2000/svg"> 13426<path d="M22.1364 34C22.0452 33.9982 21.9449 33.964 21.8658 33.9141L21.8316 33.8952L21.7642 33.8575C21.4558 33.6826 21.1514 33.4983 20.8515 33.3099C15.6957 30.0059 10.1181 24.467 10.0003 17.9623C9.97376 14.3139 11.7681 10.6318 15.693 10.0815C16.2307 10.0038 16.7679 9.98353 17.311 10.0127C19.188 10.1018 20.6434 11.2765 21.4931 12.8915C21.8946 13.6378 22.1791 14.4241 22.3608 15.2504L21.9202 14.8926H22.3527L21.9121 15.2504C22.0937 14.4245 22.3783 13.6378 22.7802 12.8919C23.393 11.7378 24.3673 10.7119 25.6338 10.2771C26.372 10.011 27.1588 9.97409 27.9316 10.0163C28.1434 10.0262 28.37 10.0537 28.5804 10.0829C32.3843 10.6147 34.184 14.0775 34.273 17.6341C34.3472 24.275 28.6604 29.9519 23.4218 33.3094C23.1646 33.4704 22.9057 33.6291 22.6427 33.7815L22.5096 33.857L22.4422 33.8948C22.3527 33.951 22.2416 33.9955 22.1373 33.9995L22.1364 34ZM22.1364 33.1008C22.0735 33.0891 21.9665 33.1211 21.9359 33.1426L21.9431 33.1377C21.9728 33.1175 22.0982 33.0248 22.1333 32.9997C27.2231 29.1984 33.9673 22.4766 32.1775 15.5337C31.6438 13.4328 30.4057 11.8228 28.2045 11.3727C27.4753 11.2325 26.6975 11.1511 25.975 11.3507C24.5561 11.732 23.6255 13.0434 23.1179 14.3621C22.9371 14.8166 22.8113 15.3147 22.7164 15.7926H22.3527C22.1558 15.7926 21.7525 15.7926 21.5569 15.7926C21.4621 15.3152 21.3362 14.8166 21.155 14.3621C20.3821 12.3938 18.9893 10.9663 16.7391 11.2711C15.8701 11.3597 14.9934 11.6223 14.2727 12.1182C12.8008 13.1113 12.1241 14.893 11.8872 16.596C10.9885 23.1456 17.307 29.3895 22.14 32.9997L22.2574 33.0855L22.3158 33.1278L22.3446 33.1485L22.3518 33.1534C22.3482 33.1521 22.3738 33.166 22.3316 33.1426C22.274 33.1125 22.2007 33.0972 22.1373 33.1017L22.1364 33.1008Z" fill="currentColor"/> 13427</svg> 13428</span> 13429 <span>Favorites</span> 13430 </a> 13431 </li> 13432 </ul> 13433 </section> 13434 </div> 13435 13436 <div class="bp-mobile-drawer__backdrop" data-close></div> 13437 </details> 13438 </bp-mobile-drawer>
13438<script type="module"> 13439 if (!customElements.get('bp-mobile-drawer')) { 13440 class BPMobileDrawer extends HTMLElement { 13441 connectedCallback() { 13442 // a11y: mark the panel as a modal dialog with an accessible name. 13443 const panel = this.querySelector('.bp-mobile-drawer__panel'); 13444 if (panel) { 13445 panel.setAttribute('role', 'dialog'); 13446 panel.setAttribute('aria-modal', 'true'); 13447 if (!panel.hasAttribute('aria-label')) panel.setAttribute('aria-label', 'Menu'); 13448 panel.tabIndex = -1; 13449 // Move the sub-views to be DIRECT children of the panel. They 13450 // render nested inside the scrolling root <section> (as siblings 13451 // of their .bp-mm-item opener); as a direct panel child a 13452 // sub-view positions cleanly against the panel (full-width 13453 // overlay) and doesn't scroll with the root list. The opener 13454 // still targets it by data-view/data-open-view (block id), not 13455 // DOM nesting, so navigation is unaffected. 13456 panel.querySelectorAll('.bp-mm-view').forEach((view) => { 13457 if (view.parentElement !== panel) panel.appendChild(view); 13458 }); 13459 } 13460 this.addEventListener('click', (e) => { 13461 const opener = e.target.closest('[data-open-view]'); 13462 const back = e.target.closest('[data-back]'); 13463 const close = e.target.closest('[data-close]'); 13464 if (opener) { e.preventDefault(); this.showView(opener.dataset.openView); this._focusActiveView(); return; } 13465 if (back) { this.showView('root'); this._focusActiveView(); return; } 13466 if (close) { this.close(); return; } 13467 }); 13468 const details = this.querySelector('details'); 13469 this._summary = details?.querySelector('summary') || null; 13470 // a11y: Escape closes the drawer. 13471 this._onKeydown = (e) => { 13472 if (e.key === 'Escape' && this.querySelector('details')?.open) { 13473 e.preventDefault(); 13474 this.close(); 13475 } 13476 }; 13477 details?.addEventListener('toggle', () => { 13478 if (details.open) this._onOpen(); 13479 else this._onClose(); 13480 }); 13481 } 13482 _onOpen() { 13483 this.showView('root'); 13484 document.documentElement.style.overflow = 'hidden'; 13485 // a11y: make the rest of the page inert so focus + AT stay in the 13486 // drawer instead of tabbing into the hidden page behind it. Track 13487 // only the elements WE inert (skip any already inert from another 13488 // overlay) so _onClose doesn't un-inert something it didn't set. 13489 this._inerted = []; 13490 document.querySelectorAll('#MainContent, footer, .footer, .bp-footer').forEach((el) => { 13491 if (!el.hasAttribute('inert')) { el.setAttribute('inert', ''); this._inerted.push(el); } 13492 }); 13493 document.addEventListener('keydown', this._onKeydown); 13494 const panel = this.querySelector('.bp-mobile-drawer__panel'); 13495 requestAnimationFrame(() => { 13496 (panel?.querySelector('[data-close], a[href], button') || panel)?.focus(); 13497 }); 13498 } 13499 _onClose() { 13500 // Reset to the top-level nav so reopening never lands mid-sub-view. 13501 this.showView('root'); 13502 document.documentElement.style.overflow = ''; 13503 (this._inerted || []).forEach((el) => el.removeAttribute('inert')); 13504 this._inerted = []; 13505 document.removeEventListener('keydown', this._onKeydown); 13506 // a11y: return focus to the hamburger that opened the drawer. 13507 if (this._summary && typeof this._summary.focus === 'function') this._summary.focus(); 13508 } 13509 // a11y: after a sub-view / Back navigation, move focus into the newly 13510 // active view â otherwise the inert we set on the outgoing view drops 13511 // focus to <body> (the opener/back button that was just focused is 13512 // inside the now-inert view). 13513 _focusActiveView() { 13514 const panel = this.querySelector('.bp-mobile-drawer__panel'); 13515 if (!panel) return; 13516 const active = panel.querySelector('.bp-mm-view:not([hidden])') 13517 || panel.querySelector('.bp-mobile-drawer__view[data-view="root"]'); 13518 if (!active) return; 13519 const target = active.querySelector('[data-back], a[href], button, [tabindex]:not
13519([tabindex="-1"])'); 13520 requestAnimationFrame(() => { (target || panel).focus(); }); 13521 } 13522 showView(name) { 13523 const panel = this.querySelector('.bp-mobile-drawer__panel'); 13524 if (!panel) return; 13525 panel.dataset.activeView = name; 13526 // Simple in-place swap: exactly ONE view is shown at a time, filling 13527 // the whole panel at full width. The native `hidden` attribute both 13528 // removes the inactive views from layout AND drops them from the 13529 // focus order â so there's no overlay slide, no transform, and no 13530 // `inert` bookkeeping (all of which caused the side-by-side / 13531 // trapped-focus bugs). Root and every sub-view are position:absolute 13532 // inset:0 in the panel, so the shown one covers it completely. 13533 const root = this.querySelector('.bp-mobile-drawer__view[data-view="root"]'); 13534 if (root) root.hidden = name !== 'root'; 13535 this.querySelectorAll('.bp-mm-view').forEach((view) => { 13536 view.hidden = view.dataset.view !== name; 13537 }); 13538 panel.scrollTop = 0; 13539 } 13540 close() { 13541 const details = this.querySelector('details'); 13542 if (details) details.open = false; 13543 } 13544 } 13545 customElements.define('bp-mobile-drawer', BPMobileDrawer); 13546 } 13547</script>
13547 13548 13549 13550 13551 </div> 13552 13553 13554 13555<style data-shopify> 13556 .header__underlay { 13557 --color-submenu: rgb(254 255 250 / 1.0); 13558 } 13559</style> 13560 13561 13562 13563 13564 13565 13566 13567 13568 13569 13570 <search-button 13571 class="search-action search-action--hidden-on-menu" 13572 > 13573 <button 13574 on:click="#search-modal/showDialog" 13575 class="button-unstyled header-actions__action" 13576 aria-label="Search" 13577 aria-haspopup="dialog" 13578 > 13579 <span class="hidden"> 13580 Search 13581 </span> 13582 13583 <span class="svg-wrapper "> 13584 <svg width="44" height="44" viewBox="0 0 44 44" fill="none" xmlns="http://www.w3.org/2000/svg"> 13585<path d="M30.6967 19.7055C30.7183 23.8405 27.932 27.3926 24.0949 28.7508C17.6703 31.1232 10.0267 26.8687 10 19.7055C10.0272 12.5414 17.6703 8.28779 24.0953 10.6602C27.932 12.0184 30.7187 15.5704 30.6967 19.7055ZM28.5408 19.7055C28.5477 16.9739 27.3344 14.2088 25.1509 12.5164C22.4003 10.326 18.2959 10.3256 15.5454 12.5164C13.3619 14.2088 12.1486 16.9735 12.1555 19.7055C12.1486 22.437 13.3619 25.2022 15.5454 26.8946C18.2959 29.085 22.4003 29.0854 25.1509 26.8946C27.3344 25.2022 28.5477 22.4374 28.5408 19.7055Z" fill="currentColor"/> 13586<path d="M33.1718 33.9998C31.5945 32.7679 30.2255 31.2984 28.9126 29.7966C28.4814 29.2934 28.0606 28.7816 27.6699 28.2439L28.9794 27.1216C29.4511 27.5898 29.8927 28.084 30.3238 28.5872C31.3971 29.854 32.4207 31.1661 33.3132 32.5696C33.4917 32.8528 33.6659 33.1396 33.8263 33.4388L33.1714 34.0002L33.1718 33.9998Z" fill="currentColor"/> 13587</svg> 13588 13589 </span> 13590 </button> 13591 </search-button> 13592 13593 13594 13595 13596 13597 13598 13599 13600 13601 13602 13603 13604 13605 13606<a 13607 13608 href="/" 13609 class="size-style spacing-style header-logo" 13610 style=" 13611 --size-style-width: ;--size-style-height: ; 13612 --padding-block-start: 0px;--padding-block-end: 0px; 13613 --font-family: var(--font-body--family); 13614 --font-style: var(--font-body--style); 13615 --font-weight: 600; 13616 " 13617 13618> 13619 13620 13621 <span 13622 class="header-logo__image-container header-logo__image-container--original" 13623 data-testid="header-logo" 13624 > 13625 13626 13627 13628 13629 13630 13631 13632 13633 13634 13635 13636 13637 13638 <img src="//www.bostonproper.com/cdn/shop/files/BOS-Logotype-Charcoal_1.png?height=24&v=1785187523" alt="Translation missing: en.Boston Proper accessibility.home" width="267" height="24" class="header-logo__image" srcset="//www.bostonproper.com/cdn/shop/files/BOS-Logotype-Charcoal_1.png?height=24&v=1785187523 1x, //www.bostonproper.com/cdn/shop/files/BOS-Logotype-Charcoal_1.png?height=48&v=1785187523 2x, //www.bostonproper.com/cdn/shop/files/BOS-Logotype-Charcoal_1.png?height=72&v=1785187523 3x" style="object-position:3.613% 50.0%;--header-logo-image-width: 267px;--header-logo-image-width-mobile: 312px; --header-logo-image-height: 24px; --header-logo-image-height-mobile: 28px;"> 13639 13640 13641 13642 </span> 13643 13644 13645 13646 13647 <span 13648 class="header-logo__image-container header-logo__image-container--scroll" 13649 data-testid="header-logo-scroll" 13650 > 13651 13652 <span 13653 class="header-logo__image header-logo__image--svg" 13654 role="img" 13655 aria-label="Translation missing: en.Boston Proper accessibility.home" 13656 style="--header-logo-image-height: 24px; --header-logo-image-height-mobile: 18px; --header-logo-image-width: 30px; --header-logo-image-width-mobile: 23px;" 13657 ><svg width="30" height="24" viewBox="0 0 30 24" fill="none" xmlns="http://www.w3.org/2000/svg"> 13658<g clip-path="url(#clip0_152_25204)"> 13659<g clip-path="url(#clip1_152_25204)"> 13660<path d="M15 0C6.71573 0 0 6.83029 0 15.2558C0 18.509 1.00189 21.5238 2.70797 24H5.08833C3.0014 21.6099 1.92308 18.4465 1.92308 14.9768C1.92308 7.52731 7.77782 1.4884 15 1.4884C22.2222 1.4884 28.0769 7.52732 28.0769 14.9768C28.0769 18.4465 26.9986 21.6099 24.9117 24H27.2921C28.9981 21.5238 30 18.509 30 15.2558C30 6.83029 23.2843 0 15 0Z" fill="#171609"/> 13661<path d="M18.2832 13.1981C19.393 12.568 20.0718 11.4106 20.16 10.3019C20.3637 7.74079 18.2279 5.49321 15.0642 5.25773C11.9005 5.02219 9.43446 6.92717 9.23075 9.48835L8.0769 24H10.1923L11.3461 9.48835C11.5091 7.43939 12.8193 6.0236 15.0891 6.19257C17.1955 6.34941 18.2138 8.09599 18.0508 10.145C17.9311 11.6507 17.2051 12.6304 15.7692 13.3677C13.6619 14.4498 12.2037 15.6336 11.997 18.2326L11.5384 24H13.6538L14.1 18.3891C14.2996 15.8792 15.8461 13.9474 18.2092 14.1234C20.5723 14.2993 21.592 16.422 21.3923 18.9319C21.1927 21.4419 19.7263 23.0698 17.3816 23.0698L17.3077 24C21.25 24 23.2611 22.1112 23.5015 19.089C23.7396 16.0945 21.8614 13.509 18.2832 13.1981Z" fill="#171609"/> 13662</g> 13663</g> 13664<defs> 13665<clipPath id="clip0_152_25204"> 13666<rect width="30" height="24" fill="white"/> 13667</clipPath> 13668<clipPath id="clip1_152_25204"> 13669<rect width="30" height="24" fill="white"/> 13670</clipPath> 13671</defs> 13672</svg> 13673</span> 13674 13675 </span> 13676</a> 13677 13678 13679 13680 13681 13682 13683 13684 13685 </div><div 13686 class="header__column header__column--center" 13687 data-testid="header-top-center" 13688 > 13689 13690 13691 13692 13693 13694 13695 13696 13697 13698 13699 <header-menu 13700 ref="headerMenu" 13701 class="header-menu" 13702 13703 data-hydration-key="header-menu" 13704 > 13705 <div class="header-menu__inner"> 13706 <nav 13707 class="menu-list" 13708 aria-label="Primary" 13709 style="--menu-top-level-font-family: var(--font-body--family); 13710--menu-top-level-font-size-desktop: 0.875rem; --menu-top-level-font-style: var(--font-body--style);
13710 --menu-top-level-font-weight: var(--font-body--weight); --menu-top-level-font-case:none; 13711 13712 --menu-top-level-font-size: var(--menu-font-sm--size); --menu-top-level-font-line-height: 13713 var(--menu-font-sm--line-height); 13714 13715--menu-top-level-font-color: var(--color-foreground); --menu-top-level-font-color-rgb: var(--color-foreground-rgb); 13716" 13717 > 13718 13719 <overflow-list 13720 13721 ref="overflowMenu" 13722 13723 13724 class="overflow-menu color-scheme-matches-parent" 13725 13726 13727 minimum-items="2" 13728 13729 13730 data-testid="header-menu-overflow-list" 13731 13732 13733 data-skip-node-update 13734 13735> 13736 <template shadowrootmode="open"> 13737 <link href="//www.bostonproper.com/cdn/shop/t/1521/assets/overflow-list.css?v=16727044177065489451790713714" rel="stylesheet" type="text/css" media="all" /> 13738 13739 <ul part="list"> 13740 <slot></slot> 13741 <slot 13742 name="more" 13743 part="more" 13744 hidden 13745 13746 > 13747 <li 13748 part="more" 13749 > 13750 <button 13751 class="button" 13752 type="button" 13753 tabindex="0" 13754 13755 > 13756 More 13757 </button> 13758 </li> 13759 </slot> 13760 <li 13761 part="placeholder" 13762 hidden 13763 ></li> 13764 </ul> 13765 13766 <div part="overflow"> 13767 <ul part="overflow-list"> 13768 <slot name="overflow"></slot> 13769 </ul> 13770 </div> 13771 </template> 13772 13773 13774 13775 <li 13776 role="presentation" 13777 class="menu-list__list-item bp-menu-item-li" 13778 data-bp-menu-item 13779 13780><a 13781 href="/collections/new-arrivals" 13782 data-skip-node-update="true" 13783 class="menu-list__link" 13784 aria-controls="bp-submenu-AR2Y1MTEyN25uOTEyM__bp_menu_new_arrivals" 13785 aria-haspopup="true" 13786 aria-expanded="false" 13787 ref="menuitem" 13788 > 13789 <span class="menu-list__link-title">New Arrivals</span> 13790 </a> 13791 <div class="bp-mega-submenu" id="bp-submenu-AR2Y1MTEyN25uOTEyM__bp_menu_new_arrivals"> 13792 <div class="bp-mega-submenu__inner"> 13793 <div class="bp-mega bp-mega--columns-promos"><div class="bp-mega__columns"><div class="bp-mega__column"> 13794 <p class="bp-mega__heading"><a href="/collections/new-arrivals">New Arrivals</a></p><ul class="bp-mega__links"><li><a href="/collections/new-arrivals">Shop All</a></li><li><a href="/pages/digital-catalog">Digital Catalog</a></li><li><a href="/collections/new-arrivals-athleisure">Athleisure</a></li><li><a href="/collections/new-arrivals-coats">Coats</a></li><li><a href="/collections/new-arrivals-dresses">Dresses</a></li><li><a href="/collections/new-arrivals-jackets-blazers">Jackets & Blazers</a></li><li><a href="/collections/new-arrivals-jeans">Jeans</a></li><li><a href="/collections/new-arrivals-loungewear">Loungewear</a></li><li><a href="/collections/new-arrivals-pants-leggings">Pants & Leggings</a></li><li><a href="/collections/new-arrivals-shoes-accessories">Shoes & Accessories</a></li><li><a href="/collections/new-arrivals-skirts-shorts">Skirts & Shorts</a></li><li><a href="/collections/new-arrivals-sweaters">Sweaters</a></li><li><a href="/collections/new-arrivals-swimsuits-swimwear">Swimwear</a></li><li><a href="/collections/new-arrivals-tops-blouses-tees">Tops</a></li></ul></div><div class="bp-mega__column"> 13795 <p class="bp-mega__heading"><a href="/collections/new-arrivals">Featured Shops</a></p><ul class="bp-mega__links"><li><a href="/collections/featured-shop-animal-prints">Animal Prints</a></li><li><a href="/collections/editors-picks">Editor's Picks</a></li><li><a href="/collections/featured-shops-fall-colors">Fall Colors</a></li><li><a href="/collections/featured-shop-intricate-details">Intricate Details</a></li><li><a href="/collections/featured-shops-matching-sets">Matching Sets</a></li><li><a href="/collections/breast-cancer-awareness">Power in Pink</a></li><li><a href="/collections/featured-shops-layering">The Layering Shop</a></li><li><a href="https://www.bostonproper.com/collections/featured-shops-suede-leather">Suede & Leather</a></li><li><a href="/pages/the-jacket-edit">Style Guide: The Jacket Edit</a></li><li><a href="/pages/the-sweater-edit">Style Guide: The Sweater Edit</a></li><li><a href="/pages/new-to-boston-proper">New to Boston Proper? </a></li></ul></div><div class="bp-mega__column"> 13796 <p class="bp-mega__heading"><a href="/pages/the-beyond-travel-collection">Beyond Travel Collection</a></p><ul class="bp-mega__links"><li><a href="/collections/travel">Shop All </a></li><li><a href="/pages/the-beyond-travel-collection">The Travel Collection</a></li><li><a href="/collections/travel-bottoms">Beyond Travel Bottoms</a></li><li><a href="/collections/travel-dresses">Beyond Travel Dresses</a></li><li><a href="/collections/travel-jackets">Beyond Travel Jackets</a></li><li><a href="/collections/travel-tops">Beyond Travel Tops</a></li><li><a href="/collections/travel-sets">Beyond Travel Sets</a></li></ul></div><div class="bp-mega__column"> 13797 <p class="bp-mega__heading"><a href="/collections/all-brands-we-love">Brands We Love</a></p><ul class="bp-mega__links"><li><a href="/collections/brands-we-love-ana-luisa">Ana Luisa</a></li><li><a href="/collections/brands-we-love-becca">Becca By Rebecca Virtue</a></li><li><a href="/collections/brands-we-love-carmen-marc-valvo">Carmen Marc Valvo</a></li><li><a href="/collections/brands-we-love-change-of-scenery">
13797Change of Scenery</a></li><li><a href="/collections/brands-we-love-fifth-and-ninth">Fifth & Ninth</a></li><li><a href="/collections/brands-we-love-jardin-jewelry">Jardin Jewelry</a></li><li><a href="/collections/brands-we-love-pickletini">Pickletini X Boston Proper</a></li><li><a href="/collections/brands-we-love-saint-jo">Saint JÅ</a></li><li><a href="/collections/brands-we-love-sum-of-all">Sum Of All</a></li><li><a href="/collections/brands-we-love-walter-baker">Walter Baker</a></li><li><a href="/collections/brands-we-love-w-y-e-t-h">WYETH</a></li></ul></div></div><div class="bp-mega__promos"><div class="bp-mega__promo"> 13798 <a href="/collections/new-arrivals" class="bp-mega__promo-link"> 13799 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/Image_73.jpg?v=1790714514&width=400" alt="New Season. New Statements." srcset="//www.bostonproper.com/cdn/shop/files/Image_73.jpg?v=1790714514&width=200 200w, //www.bostonproper.com/cdn/shop/files/Image_73.jpg?v=1790714514&width=400 400w" width="400" height="500" loading="lazy"> 13800</div> 13801 <h4 class="bp-mega__promo-title">New Season. New Statements.</h4><div class="bp-mega__promo-copy"><p>Enjoy 20% Off $175+ Plus Free Shipping. Limited Time. Use code: STYLE20</p></div><span class="bp-mega__promo-cta">Shop Now</span> 13802 </a> 13803</div> 13804 13805<div class="bp-mega__promo"> 13806 <a href="/pages/the-dress-code" class="bp-mega__promo-link"> 13807 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_New_Arrivals_Image-6.jpg?v=1790707073&width=400" alt="The Dress Code" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_New_Arrivals_Image-6.jpg?v=1790707073&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_New_Arrivals_Image-6.jpg?v=1790707073&width=400 400w" width="400" height="500" loading="lazy"> 13808</div> 13809 <h4 class="bp-mega__promo-title">The Dress Code</h4><div class="bp-mega__promo-copy"><p>The one-piece solution. Simple to style, flattering to wear, and ready for every plan.</p></div><span class="bp-mega__promo-cta">Shop Now</span> 13810 </a> 13811</div> 13812 13813</div></div> 13814 </div> 13815 </div></li><li class="bp-mm-item" data-bp-mm-id="AR2Y1MTEyN25uOTEyM__bp_menu_new_arrivals"><button type="button" class="bp-mm-item__row" data-open-view="AR2Y1MTEyN25uOTEyM__bp_menu_new_arrivals"> 13816 <span class="bp-mm-item__label">New Arrivals</span> 13817 <span class="bp-mm-item__chevron" aria-hidden="true"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 13818 <path d="M11 5.5L7 9.5L3 5.5" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 13819</svg> 13820</span> 13821 </button> 13822 13823 <section class="bp-mm-view" data-view="AR2Y1MTEyN25uOTEyM__bp_menu_new_arrivals" hidden> 13824 <header class="bp-mm-view__header"> 13825 <button type="button" class="bp-mm-view__back" data-back> 13826 <span class="bp-mm-view__back-icon" aria-hidden="true"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 13827 <path d="M11 5.5L7 9.5L3 5.5" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 13828</svg> 13829</span> 13830 <span>New Arrivals</span> 13831 </button> 13832 <button type="button" class="bp-mm-view__close" data-close aria-label="Close menu"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 13833 <path d="M12 2L2 12" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 13834 <path d="M12 12L2 2" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 13835</svg> 13836</button> 13837 </header><div class="bp-mm-view__sections"><section class="bp-mm-section"> 13838 <p class="bp-mm-section__heading"><a href="/collections/new-arrivals">New Arrivals</a></p><ul class="bp-mm-section__links"><li><a href="/collections/new-arrivals">Shop All</a></li><li><a href="/pages/digital-catalog">Digital Catalog</a></li><li><a href="/collections/new-arrivals-athleisure">Athleisure</a></li><li><a href="/collections/new-arrivals-coats">Coats</a></li><li><a href="/collections/new-arrivals-dresses">Dresses</a></li><li><a href="/collections/new-arrivals-jackets-blazers">Jackets & Blazers</a></li><li><a href="/collections/new-arrivals-jeans">Jeans</a></li><li><a href="/collections/new-arrivals-loungewear">Loungewear</a></li><li><a href="/collections/new-arrivals-pants-leggings">Pants & Leggings</a></li><li><a href="/collections/new-arrivals-shoes-accessories">Shoes & Accessories</a></li><li><a href="/collections/new-arrivals-skirts-shorts">Skirts & Shorts</a></li><li><a href="/collections/new-arrivals-sweaters">Sweaters</a></li><li><a href="/collections/new-arrivals-swimsuits-swimwear">Swimwear</a></li><li><a href="/collections/new-arrivals-tops-blouses-tees">Tops</a></li></ul></section><section class="bp-mm-section"> 13839 <p class="bp-mm-section__heading"><a href="/collections/new-arrivals">Featured Shops</a></p><ul class="bp-mm-section__links"><li><a href="/collections/featured-shop-animal-prints">Animal Prints</a></li><li><a href="/collections/editors-picks">Editor's Picks</a></li><li><a href="/collections/featured-shops-fall-colors">Fall Colors</a></li><li><a href="/collections/featured-shop-intricate-details">Intricate Details</a></li><li><a href="/collections/featured-shops-matching-sets">Matching Sets</a></li><li><a href="/collections/breast-cancer-awareness">Power in Pink</a></li><li><a href="/collections/featured-shops-layering">The Layering Shop</a></li><li><a href="https://www.bostonproper.com/collections/featured-shops-suede-leather">Suede & Leather</a></li><li><a href="/pages/the-jacket-edit">Style Guide: The Jacket Edit</a></li><li><a href="/pages/the-sweater-edit">Style Guide: The Sweater Edit</a></li><li><a href="/pages/new-to-boston-proper">New to Boston Proper? </a></li></ul></section><section class="bp-mm-section"> 13840 <p class="bp-mm-section__heading"><a href="/pages/the-beyond-travel-collection">Beyond Travel Collection</a></p><ul class="bp-mm-section__links"><li><a href="/collections/travel">Shop All </a></li><li><a href="/pages/the-beyond-travel-collection">The Travel Collection</a></li><li><a href="/collections/travel-bottoms">Beyond Travel Bottoms</a></li><li><a href="/collections/travel-dresses">Beyond Travel Dresses</a></li><li><a href="/collections/travel-jackets">Beyond Travel Jackets</a></li><li><a href="/collections/travel-tops">Beyond Travel Tops</a></li><li><a href="/collections/travel-sets">Beyond Travel Sets</a></li></ul></section><section class="bp-mm-section"> 13841 <p class="bp-mm-section__heading"><a href="/collections/all-brands-we-love">Brands We Love</a></p><ul class="bp-mm-section__links"><li><a href="/collections/brands-we-love-ana-luisa">Ana Luisa</a></li><li><a href="/collections/brands-we-love-becca">Becca By Rebecca Virtue</a></li><li><a href="/collections/brands-we-love-carmen-marc-valvo">Carmen Marc Valvo</a></li><li><a href="/collections/brands-we-love-change-of-scenery">
13841Change of Scenery</a></li><li><a href="/collections/brands-we-love-fifth-and-ninth">Fifth & Ninth</a></li><li><a href="/collections/brands-we-love-jardin-jewelry">Jardin Jewelry</a></li><li><a href="/collections/brands-we-love-pickletini">Pickletini X Boston Proper</a></li><li><a href="/collections/brands-we-love-saint-jo">Saint JÅ</a></li><li><a href="/collections/brands-we-love-sum-of-all">Sum Of All</a></li><li><a href="/collections/brands-we-love-walter-baker">Walter Baker</a></li><li><a href="/collections/brands-we-love-w-y-e-t-h">WYETH</a></li></ul></section></div><div class="bp-mm-view__promos"><div class="bp-mega__promo"> 13842 <a href="/collections/new-arrivals" class="bp-mega__promo-link"> 13843 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/Image_73.jpg?v=1790714514&width=400" alt="New Season. New Statements." srcset="//www.bostonproper.com/cdn/shop/files/Image_73.jpg?v=1790714514&width=200 200w, //www.bostonproper.com/cdn/shop/files/Image_73.jpg?v=1790714514&width=400 400w" width="400" height="500" loading="lazy"> 13844</div> 13845 <h4 class="bp-mega__promo-title">New Season. New Statements.</h4><div class="bp-mega__promo-copy"><p>Enjoy 20% Off $175+ Plus Free Shipping. Limited Time. Use code: STYLE20</p></div><span class="bp-mega__promo-cta">Shop Now</span> 13846 </a> 13847</div> 13848 13849<div class="bp-mega__promo"> 13850 <a href="/pages/the-dress-code" class="bp-mega__promo-link"> 13851 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_New_Arrivals_Image-6.jpg?v=1790707073&width=400" alt="The Dress Code" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_New_Arrivals_Image-6.jpg?v=1790707073&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_New_Arrivals_Image-6.jpg?v=1790707073&width=400 400w" width="400" height="500" loading="lazy"> 13852</div> 13853 <h4 class="bp-mega__promo-title">The Dress Code</h4><div class="bp-mega__promo-copy"><p>The one-piece solution. Simple to style, flattering to wear, and ready for every plan.</p></div><span class="bp-mega__promo-cta">Shop Now</span> 13854 </a> 13855</div> 13856 13857</div></section></li> 13858 13859 13860 13861<li 13862 role="presentation" 13863 class="menu-list__list-item bp-menu-item-li" 13864 data-bp-menu-item 13865 13866><a 13867 href="/collections/clothing" 13868 data-skip-node-update="true" 13869 class="menu-list__link" 13870 aria-controls="bp-submenu-AUjR2VlEyVFlTbXhaM__bp_menu_clothing" 13871 aria-haspopup="true" 13872 aria-expanded="false" 13873 ref="menuitem" 13874 > 13875 <span class="menu-list__link-title">Clothing</span> 13876 </a> 13877 <div class="bp-mega-submenu" id="bp-submenu-AUjR2VlEyVFlTbXhaM__bp_menu_clothing"> 13878 <div class="bp-mega-submenu__inner"> 13879 <div class="bp-mega bp-mega--columns-promos"><div class="bp-mega__columns"><div class="bp-mega__column"> 13880 <p class="bp-mega__heading"><a href="/collections/clothing">Clothing</a></p><ul class="bp-mega__links"><li><a href="/collections/clothing">All Clothing</a></li><li><a href="/collections/clothing-athleisure">Athleisure</a></li><li><a href="/collections/clothing-bottoms">Bottoms</a></li><li><a href="/collections/clothing-jeans">Denim & Jeans</a></li><li><a href="/collections/clothing-dresses">Dresses</a></li><li><a href="/collections/clothing-intimates">Intimates</a></li><li><a href="/collections/clothing-jackets-blazers">Jackets & Blazers</a></li><li><a href="/collections/clothing-loungewear">Loungewear</a></li><li><a href="/collections/clothing-pants-leggings">Pants & Leggings</a></li><li><a href="/collections/clothing-shirts-blouses">Shirts & Blouses</a></li><li><a href="/collections/clothing-skirts-shorts">Skirts & Shorts</a></li><li><a href="/collections/clothing-sweaters">Sweaters</a></li><li><a href="/collections/clothing-swimsuits-swimwear">Swimwear</a></li><li><a href="/collections/clothing-tops-blouses-tees">Tops</a></li></ul></div><div class="bp-mega__column"> 13881 <p class="bp-mega__heading"><a href="/pages/all-occasions">Occasions</a></p><ul class="bp-mega__links"><li><a href="/collections/occasions-everyday-style">Casual Chic</a></li><li><a href="/collections/occasions-night-out">Own the Evening</a></li><li><a href="/collections/occasions-special-occasion">RSVP Ready</a></li><li><a href="/collections/occasions-vacation">Escape in Style</a></li><li><a href="/collections/occasions-work">Power Moves</a></li><li><a href="/collections/occasions-wedding">Celebrate Beautifully</a></li></ul></div><div class="bp-mega__column"> 13882 <p class="bp-mega__heading"><a href="/collections/clothing">Shops</a></p><ul class="bp-mega__links"><li><a href="/collections/clothing-pants-leggings">
13882The Bottoms Guide</a></li><li><a href="/collections/clothing-jeans">The Denim Guide</a></li><li><a href="/pages/essentials-collection">The Essentials Edit</a></li><li><a href="/collections/essentials">The Tops Collection</a></li><li><a href="/collections/the-suiting-shop">The Suiting Shop</a></li><li><a href="/pages/the-dress-code">Style Guide: The Dress Code</a></li><li><a href="/pages/the-jacket-edit">Style Guide: The Jacket Edit</a></li><li><a href="/pages/the-sweater-edit">Style Guide: The Sweater Edit</a></li></ul></div><div class="bp-mega__column"> 13883 <p class="bp-mega__heading"><a href="/collections/all-brands-we-love">Brands We Love</a></p><ul class="bp-mega__links"><li><a href="/collections/brands-we-love-becca">Becca By Rebecca Virtue</a></li><li><a href="/collections/brands-we-love-carmen-marc-valvo">Carmen Marc Valvo</a></li><li><a href="/collections/brands-we-love-change-of-scenery">Change of Scenery</a></li><li><a href="/collections/brands-we-love-mimosa-lane">Mimosa Lane</a></li><li><a href="/collections/brands-we-love-pickletini">Pickletini X Boston Proper</a></li><li><a href="/collections/brands-we-love-saint-jo">Saint JÅ</a></li><li><a href="/collections/brands-we-love-sum-of-all">Sum Of All</a></li><li><a href="/collections/brands-we-love-walter-baker">Walter Baker</a></li><li><a href="/collections/brands-we-love-w-y-e-t-h">WYETH</a></li><li><a href="/collections/brands-we-love-jardin-jewelry">Jardin Jewelry</a></li></ul></div></div><div class="bp-mega__promos"><div class="bp-mega__promo"> 13884 <a href="/collections/breast-cancer-awareness" class="bp-mega__promo-link"> 13885 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/Image_69.jpg?v=1790780283&width=400" alt="Power In Pink" srcset="//www.bostonproper.com/cdn/shop/files/Image_69.jpg?v=1790780283&width=200 200w, //www.bostonproper.com/cdn/shop/files/Image_69.jpg?v=1790780283&width=400 400w" width="400" height="500" loading="lazy"> 13886</div> 13887 <h4 class="bp-mega__promo-title">Power In Pink</h4><div class="bp-mega__promo-copy"><p>Wearing pink never felt so powerful. Shop our curated Power in Pink Collection and join us in supporting the Lynn Cancer Institute.</p></div><span class="bp-mega__promo-cta">Shop With Purpose</span> 13888 </a> 13889</div> 13890 13891<div class="bp-mega__promo"> 13892 <a href="/collections/knit-tops" class="bp-mega__promo-link"> 13893 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Clothing_Image-8.jpg?v=1790707168&width=400" alt="Knit Tops" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Clothing_Image-8.jpg?v=1790707168&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Clothing_Image-8.jpg?v=1790707168&width=400 400w" width="400" height="500" loading="lazy"> 13894</div> 13895 <h4 class="bp-mega__promo-title">Knit Tops</h4><div class="bp-mega__promo-copy"><p>From everyday to unforgettable. Flattering knits made for denim, jackets, and everything after.</p></div><span class="bp-mega__promo-cta">Shop Now</span> 13896 </a> 13897</div> 13898 13899</div></div> 13900 </div> 13901 </div></li><li class="bp-mm-item" data-bp-mm-id="AUjR2VlEyVFlTbXhaM__bp_menu_clothing"><button type="button" class="bp-mm-item__row" data-open-view="AUjR2VlEyVFlTbXhaM__bp_menu_clothing"> 13902 <span class="bp-mm-item__label">Clothing</span> 13903 <span class="bp-mm-item__chevron" aria-hidden="true"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 13904 <path d="M11 5.5L7 9.5L3 5.5" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 13905</svg> 13906</span> 13907 </button> 13908 13909 <section class="bp-mm-view" data-view="AUjR2VlEyVFlTbXhaM__bp_menu_clothing" hidden> 13910 <header class="bp-mm-view__header"> 13911 <button type="button" class="bp-mm-view__back" data-back> 13912 <span class="bp-mm-view__back-icon" aria-hidden="true"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 13913 <path d="M11 5.5L7 9.5L3 5.5" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 13914</svg> 13915</span>
13916 <span>Clothing</span> 13917 </button> 13918 <button type="button" class="bp-mm-view__close" data-close aria-label="Close menu"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 13919 <path d="M12 2L2 12" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 13920 <path d="M12 12L2 2" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 13921</svg> 13922</button> 13923 </header><div class="bp-mm-view__sections"><section class="bp-mm-section"> 13924 <p class="bp-mm-section__heading"><a href="/collections/clothing">Clothing</a></p><ul class="bp-mm-section__links"><li><a href="/collections/clothing">All Clothing</a></li><li><a href="/collections/clothing-athleisure">Athleisure</a></li><li><a href="/collections/clothing-bottoms">Bottoms</a></li><li><a href="/collections/clothing-jeans">Denim & Jeans</a></li><li><a href="/collections/clothing-dresses">Dresses</a></li><li><a href="/collections/clothing-intimates">Intimates</a></li><li><a href="/collections/clothing-jackets-blazers">Jackets & Blazers</a></li><li><a href="/collections/clothing-loungewear">Loungewear</a></li><li><a href="/collections/clothing-pants-leggings">Pants & Leggings</a></li><li><a href="/collections/clothing-shirts-blouses">Shirts & Blouses</a></li><li><a href="/collections/clothing-skirts-shorts">Skirts & Shorts</a></li><li><a href="/collections/clothing-sweaters">Sweaters</a></li><li><a href="/collections/clothing-swimsuits-swimwear">Swimwear</a></li><li><a href="/collections/clothing-tops-blouses-tees">Tops</a></li></ul></section><section class="bp-mm-section"> 13925 <p class="bp-mm-section__heading"><a href="/pages/all-occasions">Occasions</a></p><ul class="bp-mm-section__links"><li><a href="/collections/occasions-everyday-style">Casual Chic</a></li><li><a href="/collections/occasions-night-out">Own the Evening</a></li><li><a href="/collections/occasions-special-occasion">RSVP Ready</a></li><li><a href="/collections/occasions-vacation">Escape in Style</a></li><li><a href="/collections/occasions-work">Power Moves</a></li><li><a href="/collections/occasions-wedding">Celebrate Beautifully</a></li></ul></section><section class="bp-mm-section"> 13926 <p class="bp-mm-section__heading"><a href="/collections/clothing">Shops</a></p><ul class="bp-mm-section__links"><li><a href="/collections/clothing-pants-leggings">The Bottoms Guide</a></li><li><a href="/collections/clothing-jeans">The Denim Guide</a></li><li><a href="/pages/essentials-collection">The Essentials Edit</a></li><li><a href="/collections/essentials">The Tops Collection</a></li><li><a href="/collections/the-suiting-shop">The Suiting Shop</a></li><li><a href="/pages/the-dress-code">Style Guide: The Dress Code</a></li><li><a href="/pages/the-jacket-edit">Style Guide: The Jacket Edit</a></li><li><a href="/pages/the-sweater-edit">Style Guide: The Sweater Edit</a></li></ul></section><section class="bp-mm-section"> 13927 <p class="bp-mm-section__heading"><a href="/collections/all-brands-we-love">Brands We Love</a></p><ul class="bp-mm-section__links"><li><a href="/collections/brands-we-love-becca">Becca By Rebecca Virtue</a></li><li><a href="/collections/brands-we-love-carmen-marc-valvo">Carmen Marc Valvo</a></li><li><a href="/collections/brands-we-love-change-of-scenery">Change of Scenery</a></li><li><a href="/collections/brands-we-love-mimosa-lane">Mimosa Lane</a></li><li><a href="/collections/brands-we-love-pickletini">Pickletini X Boston Proper</a></li><li><a href="/collections/brands-we-love-saint-jo">Saint JÅ</a></li><li><a href="/collections/brands-we-love-sum-of-all">Sum Of All</a></li><li><a href="/collections/brands-we-love-walter-baker">Walter Baker</a></li><li><a href="/collections/brands-we-love-w-y-e-t-h">WYETH</a></li><li><a href="/collections/brands-we-love-jardin-jewelry">Jardin Jewelry</a></li></ul></section></div><div class="bp-mm-view__promos"><div class="bp-mega__promo"> 13928 <a href="/collections/breast-cancer-awareness" class="bp-mega__promo-link"> 13929 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/Image_69.jpg?v=1790780283&width=400" alt="Power In Pink" srcset="//www.bostonproper.com/cdn/shop/files/Image_69.jpg?v=1790780283&width=200 200w, //www.bostonproper.com/cdn/shop/files/Image_69.jpg?v=1790780283&width=400 400w" width="400" height="500" loading="lazy"> 13930</div> 13931 <h4 class="bp-mega__promo-title">Power In Pink</h4><div class="bp-mega__promo-copy"><p>Wearing pink never felt so powerful. Shop our curated Power in Pink Collection and join us in supporting the Lynn Cancer Institute.</p></div><span class="bp-mega__promo-cta">Shop With Purpose</span> 13932 </a> 13933</div> 13934 13935<div class="bp-mega__promo"> 13936 <a href="/collections/knit-tops" class="bp-mega__promo-link"> 13937 <div class="bp-mega__promo-image">
13937<img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Clothing_Image-8.jpg?v=1790707168&width=400" alt="Knit Tops" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Clothing_Image-8.jpg?v=1790707168&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Clothing_Image-8.jpg?v=1790707168&width=400 400w" width="400" height="500" loading="lazy"> 13938</div> 13939 <h4 class="bp-mega__promo-title">Knit Tops</h4><div class="bp-mega__promo-copy"><p>From everyday to unforgettable. Flattering knits made for denim, jackets, and everything after.</p></div><span class="bp-mega__promo-cta">Shop Now</span> 13940 </a> 13941</div> 13942 13943</div></section></li> 13944 13945 13946 13947<li 13948 role="presentation" 13949 class="menu-list__list-item bp-menu-item-li" 13950 data-bp-menu-item 13951 13952><a 13953 href="/pages/all-occasions" 13954 data-skip-node-update="true" 13955 class="menu-list__link" 13956 aria-controls="bp-submenu-AWlVkd2t4OFJocXpVU__bp_menu_occasions" 13957 aria-haspopup="true" 13958 aria-expanded="false" 13959 ref="menuitem" 13960 > 13961 <span class="menu-list__link-title">Occasions</span> 13962 </a> 13963 <div class="bp-mega-submenu" id="bp-submenu-AWlVkd2t4OFJocXpVU__bp_menu_occasions"> 13964 <div class="bp-mega-submenu__inner"> 13965 <div class="bp-mega bp-mega--image-tiles"><div class="bp-mega__tiles"><a class="bp-mega__tile" href="/collections/occasions-everyday-style"> 13966 <div class="bp-mega__tile-image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-10.jpg?v=1790701815&width=400" alt="Casual Chic" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-10.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-10.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 13967</div> 13968 <p class="bp-mega__tile-title">Casual Chic</p> 13969 <span class="bp-mega__tile-cta">Shop Now</span> 13970 </a><a class="bp-mega__tile" href="/collections/occasions-vacation"> 13971 <div class="bp-mega__tile-image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-14.jpg?v=1790701815&width=400" alt="Escape in Style" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-14.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-14.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 13972</div> 13973 <p class="bp-mega__tile-title">Escape in Style</p> 13974 <span class="bp-mega__tile-cta">Shop Now</span> 13975 </a><a class="bp-mega__tile" href="/collections/occasions-special-occasion"> 13976 <div class="bp-mega__tile-image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-16.jpg?v=1790701815&width=400" alt="RSVP Ready" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-16.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-16.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 13977</div> 13978 <p class="bp-mega__tile-title">RSVP Ready</p> 13979 <span class="bp-mega__tile-cta">Shop Now</span> 13980 </a><a class="bp-mega__tile" href="/collections/occasions-work"> 13981 <div class="bp-mega__tile-image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-12.jpg?v=1790701815&width=400" alt="Power Moves" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-12.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-12.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 13982</div> 13983 <p class="bp-mega__tile-title">Power Moves</p> 13984 <span class="bp-mega__tile-cta">Shop Now</span> 13985 </a><a class="bp-mega__tile" href="/collections/occasions-night-out"> 13986 <div class="bp-mega__tile-image">
13986<img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-18_9dd5998e-01cb-46c4-a182-cd9be31c2c67.jpg?v=1790701815&width=400" alt="Own the Evening" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-18_9dd5998e-01cb-46c4-a182-cd9be31c2c67.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-18_9dd5998e-01cb-46c4-a182-cd9be31c2c67.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 13987</div> 13988 <p class="bp-mega__tile-title">Own the Evening</p> 13989 <span class="bp-mega__tile-cta">Shop Now</span> 13990 </a><a class="bp-mega__tile" href="/collections/occasions-wedding"> 13991 <div class="bp-mega__tile-image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-20_6043da00-6dae-4bd6-9c2c-e935f58f8b4c.jpg?v=1790701815&width=400" alt="Celebrate Beautifully" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-20_6043da00-6dae-4bd6-9c2c-e935f58f8b4c.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-20_6043da00-6dae-4bd6-9c2c-e935f58f8b4c.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 13992</div> 13993 <p class="bp-mega__tile-title">Celebrate Beautifully</p> 13994 <span class="bp-mega__tile-cta">Shop Now</span> 13995 </a></div></div> 13996 </div> 13997 </div></li><li class="bp-mm-item" data-bp-mm-id="AWlVkd2t4OFJocXpVU__bp_menu_occasions"><button type="button" class="bp-mm-item__row" data-open-view="AWlVkd2t4OFJocXpVU__bp_menu_occasions"> 13998 <span class="bp-mm-item__label">Occasions</span> 13999 <span class="bp-mm-item__chevron" aria-hidden="true"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 14000 <path d="M11 5.5L7 9.5L3 5.5" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 14001</svg> 14002</span> 14003 </button> 14004 14005 <section class="bp-mm-view" data-view="AWlVkd2t4OFJocXpVU__bp_menu_occasions" hidden> 14006 <header class="bp-mm-view__header"> 14007 <button type="button" class="bp-mm-view__back" data-back> 14008 <span class="bp-mm-view__back-icon" aria-hidden="true"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 14009 <path d="M11 5.5L7 9.5L3 5.5" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 14010</svg> 14011</span> 14012 <span>Occasions</span> 14013 </button> 14014 <button type="button" class="bp-mm-view__close" data-close aria-label="Close menu"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 14015 <path d="M12 2L2 12" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 14016 <path d="M12 12L2 2" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 14017</svg> 14018</button> 14019 </header><div class="bp-mm-view__tiles"><a class="bp-mm-tile" href="/collections/occasions-everyday-style"> 14020 <span class="bp-mm-tile__image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-10.jpg?v=1790701815&width=400" alt="Casual Chic" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-10.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-10.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 14021</span> 14022 <span class="bp-mm-tile__title">Casual Chic</span> 14023 <span class="bp-mm-tile__cta">Shop Now</span> 14024 </a><a class="bp-mm-tile" href="/collections/occasions-vacation"> 14025 <span class="bp-mm-tile__image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-14.jpg?v=1790701815&width=400" alt="Escape in Style" srcset="//www.bostonproper.com/cdn/shop/files/Fl
14025y_Downs_Occasions_Image-14.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-14.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 14026</span> 14027 <span class="bp-mm-tile__title">Escape in Style</span> 14028 <span class="bp-mm-tile__cta">Shop Now</span> 14029 </a><a class="bp-mm-tile" href="/collections/occasions-special-occasion"> 14030 <span class="bp-mm-tile__image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-16.jpg?v=1790701815&width=400" alt="RSVP Ready" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-16.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-16.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 14031</span> 14032 <span class="bp-mm-tile__title">RSVP Ready</span> 14033 <span class="bp-mm-tile__cta">Shop Now</span> 14034 </a><a class="bp-mm-tile" href="/collections/occasions-work"> 14035 <span class="bp-mm-tile__image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-12.jpg?v=1790701815&width=400" alt="Power Moves" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-12.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-12.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 14036</span> 14037 <span class="bp-mm-tile__title">Power Moves</span> 14038 <span class="bp-mm-tile__cta">Shop Now</span> 14039 </a><a class="bp-mm-tile" href="/collections/occasions-night-out"> 14040 <span class="bp-mm-tile__image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-18_9dd5998e-01cb-46c4-a182-cd9be31c2c67.jpg?v=1790701815&width=400" alt="Own the Evening" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-18_9dd5998e-01cb-46c4-a182-cd9be31c2c67.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-18_9dd5998e-01cb-46c4-a182-cd9be31c2c67.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 14041</span> 14042 <span class="bp-mm-tile__title">Own the Evening</span> 14043 <span class="bp-mm-tile__cta">Shop Now</span> 14044 </a><a class="bp-mm-tile" href="/collections/occasions-wedding"> 14045 <span class="bp-mm-tile__image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-20_6043da00-6dae-4bd6-9c2c-e935f58f8b4c.jpg?v=1790701815&width=400" alt="Celebrate Beautifully" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-20_6043da00-6dae-4bd6-9c2c-e935f58f8b4c.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Occasions_Image-20_6043da00-6dae-4bd6-9c2c-e935f58f8b4c.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 14046</span> 14047 <span class="bp-mm-tile__title">Celebrate Beautifully</span> 14048 <span class="bp-mm-tile__cta">Shop Now</span> 14049 </a></div></section></li> 14050 14051 14052 14053<li 14054 role="presentation" 14055 class="menu-list__list-item bp-menu-item-li" 14056 data-bp-menu-item 14057 14058><a 14059 href="/pages/essentials-collection" 14060 data-skip-node-update="true" 14061 class="menu-list__link" 14062 aria-controls="bp-submenu-ARTdKY3Jpczg4VVVHb__bp_menu_collections" 14063 aria-haspopup="true" 14064 aria-expanded="false" 14065 ref="menuitem" 14066 > 14067 <span class="menu-list__link-title">Collections</span> 14068 </a> 14069 <div class="bp-mega-submenu" id="bp-submenu-ARTdKY3Jpczg4VVVHb__bp_menu_collections"> 14070 <div class="bp-mega-submenu__inner"> 14071 <div class="bp-mega bp-mega--image-tiles"><div class="bp-mega__tiles"><a class="bp-mega__tile" href="/collections/charmeuse"> 14072 <div class="bp-mega__tile-image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-11.jpg?v=1790701815&width=400" alt="Charmeuse" srcset="//www.bostonproper.com/cdn/shop/files/Fl
14072y_Downs_Collections_Image-11.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-11.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 14073</div> 14074 <p class="bp-mega__tile-title">Charmeuse</p> 14075 <span class="bp-mega__tile-cta">Shop Now</span> 14076 </a><a class="bp-mega__tile" href="/collections/chic-zip"> 14077 <div class="bp-mega__tile-image"><img src="//www.bostonproper.com/cdn/shop/files/Image_76_c1e341e6-f12c-49d5-bc88-54e2673f3c30.jpg?v=1790802160&width=400" alt="Chic Zip" srcset="//www.bostonproper.com/cdn/shop/files/Image_76_c1e341e6-f12c-49d5-bc88-54e2673f3c30.jpg?v=1790802160&width=200 200w, //www.bostonproper.com/cdn/shop/files/Image_76_c1e341e6-f12c-49d5-bc88-54e2673f3c30.jpg?v=1790802160&width=400 400w" width="400" height="500" loading="lazy"> 14078</div> 14079 <p class="bp-mega__tile-title">Chic Zip</p> 14080 <span class="bp-mega__tile-cta">Shop Now</span> 14081 </a><a class="bp-mega__tile" href="/collections/clothing-jeans"> 14082 <div class="bp-mega__tile-image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-17.jpg?v=1790701815&width=400" alt="Denim" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-17.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-17.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 14083</div> 14084 <p class="bp-mega__tile-title">Denim</p> 14085 <span class="bp-mega__tile-cta">Shop Now</span> 14086 </a><a class="bp-mega__tile" href="/collections/the-suiting-shop"> 14087 <div class="bp-mega__tile-image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-13.jpg?v=1790701815&width=400" alt="Modern Suiting" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-13.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-13.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 14088</div> 14089 <p class="bp-mega__tile-title">Modern Suiting</p> 14090 <span class="bp-mega__tile-cta">Shop Now</span> 14091 </a><a class="bp-mega__tile" href="/collections/travel"> 14092 <div class="bp-mega__tile-image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-19.jpg?v=1790701815&width=400" alt="Beyond Travel" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-19.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-19.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 14093</div> 14094 <p class="bp-mega__tile-title">Beyond Travel</p> 14095 <span class="bp-mega__tile-cta">Shop Now</span> 14096 </a><a class="bp-mega__tile" href="/collections/athleisure-luxe-lounge"> 14097 <div class="bp-mega__tile-image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-21.jpg?v=1790701815&width=400" alt="Luxe Lounge" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-21.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-21.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 14098</div> 14099 <p class="bp-mega__tile-title">Luxe Lounge</p> 14100 <span class="bp-mega__tile-cta">Shop Now</span> 14101 </a></div></div> 14102 </div> 14103 </div></li><li class="bp-mm-item" data-bp-mm-id="ARTdKY3Jpczg4VVVHb__bp_menu_collections"><button type="button" class="bp-mm-item__row" data-open-view="ARTdKY3Jpczg4VVVHb__bp_menu_collections"> 14104 <span class="bp-mm-item__label">Collections</span> 14105 <span class="bp-mm-item__chevron" aria-hidden="true"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 14106 <path d="M11 5.5L7 9.5L3 5.5" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 14107</svg> 14108</span> 14109 </button> 14110 14111 <section class="bp-mm-view" data-view="ARTdKY3Jpczg4VVVHb__bp_menu_collections" hidden> 14112 <header class="bp-mm-view__header"> 14113 <button type="button" class="bp-mm-view__back" data-back>
14114 <span class="bp-mm-view__back-icon" aria-hidden="true"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 14115 <path d="M11 5.5L7 9.5L3 5.5" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 14116</svg> 14117</span> 14118 <span>Collections</span> 14119 </button> 14120 <button type="button" class="bp-mm-view__close" data-close aria-label="Close menu"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 14121 <path d="M12 2L2 12" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 14122 <path d="M12 12L2 2" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 14123</svg> 14124</button> 14125 </header><div class="bp-mm-view__tiles"><a class="bp-mm-tile" href="/collections/charmeuse"> 14126 <span class="bp-mm-tile__image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-11.jpg?v=1790701815&width=400" alt="Charmeuse" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-11.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-11.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 14127</span> 14128 <span class="bp-mm-tile__title">Charmeuse</span> 14129 <span class="bp-mm-tile__cta">Shop Now</span> 14130 </a><a class="bp-mm-tile" href="/collections/chic-zip"> 14131 <span class="bp-mm-tile__image"><img src="//www.bostonproper.com/cdn/shop/files/Image_76_c1e341e6-f12c-49d5-bc88-54e2673f3c30.jpg?v=1790802160&width=400" alt="Chic Zip" srcset="//www.bostonproper.com/cdn/shop/files/Image_76_c1e341e6-f12c-49d5-bc88-54e2673f3c30.jpg?v=1790802160&width=200 200w, //www.bostonproper.com/cdn/shop/files/Image_76_c1e341e6-f12c-49d5-bc88-54e2673f3c30.jpg?v=1790802160&width=400 400w" width="400" height="500" loading="lazy"> 14132</span> 14133 <span class="bp-mm-tile__title">Chic Zip</span> 14134 <span class="bp-mm-tile__cta">Shop Now</span> 14135 </a><a class="bp-mm-tile" href="/collections/clothing-jeans"> 14136 <span class="bp-mm-tile__image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-17.jpg?v=1790701815&width=400" alt="Denim" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-17.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-17.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 14137</span> 14138 <span class="bp-mm-tile__title">Denim</span> 14139 <span class="bp-mm-tile__cta">Shop Now</span> 14140 </a><a class="bp-mm-tile" href="/collections/the-suiting-shop"> 14141 <span class="bp-mm-tile__image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-13.jpg?v=1790701815&width=400" alt="Modern Suiting" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-13.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-13.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 14142</span> 14143 <span class="bp-mm-tile__title">Modern Suiting</span> 14144 <span class="bp-mm-tile__cta">Shop Now</span> 14145 </a><a class="bp-mm-tile" href="/collections/travel"> 14146 <span class="bp-mm-tile__image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-19.jpg?v=1790701815&width=400" alt="Beyond Travel" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-19.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-19.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 14147</span> 14148 <span class="bp-mm-tile__title">Beyond Travel</span>
14149 <span class="bp-mm-tile__cta">Shop Now</span> 14150 </a><a class="bp-mm-tile" href="/collections/athleisure-luxe-lounge"> 14151 <span class="bp-mm-tile__image"><img src="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-21.jpg?v=1790701815&width=400" alt="Luxe Lounge" srcset="//www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-21.jpg?v=1790701815&width=200 200w, //www.bostonproper.com/cdn/shop/files/Fly_Downs_Collections_Image-21.jpg?v=1790701815&width=400 400w" width="400" height="500" loading="lazy"> 14152</span> 14153 <span class="bp-mm-tile__title">Luxe Lounge</span> 14154 <span class="bp-mm-tile__cta">Shop Now</span> 14155 </a></div></section></li> 14156 14157 14158 14159<li 14160 role="presentation" 14161 class="menu-list__list-item bp-menu-item-li" 14162 data-bp-menu-item 14163 14164><a 14165 href="/collections/shoes-accessories" 14166 data-skip-node-update="true" 14167 class="menu-list__link" 14168 aria-controls="bp-submenu-AdVdsY2tEV2JFWlVvV__bp_menu_shoes" 14169 aria-haspopup="true" 14170 aria-expanded="false" 14171 ref="menuitem" 14172 > 14173 <span class="menu-list__link-title">Shoes & Accessories</span> 14174 </a> 14175 <div class="bp-mega-submenu" id="bp-submenu-AdVdsY2tEV2JFWlVvV__bp_menu_shoes"> 14176 <div class="bp-mega-submenu__inner"> 14177 <div class="bp-mega bp-mega--columns-promos"><div class="bp-mega__columns"><div class="bp-mega__column"> 14178 <p class="bp-mega__heading"><a href="/collections/shoes">Shoes</a></p><ul class="bp-mega__links"><li><a href="/collections/shoes">All Shoes</a></li><li><a href="/collections/shoes-boots">Boots & Booties</a></li><li><a href="/collections/shoes-heels">Heels</a></li><li><a href="/collections/shoes-sandals">Sandals</a></li><li><a href="/collections/shoes-sneakers">Sneakers</a></li><li><a href="/collections/shoes-wedges">Wedges</a></li></ul></div><div class="bp-mega__column"> 14179 <p class="bp-mega__heading"><a href="/collections/accessories">Accessories</a></p><ul class="bp-mega__links"><li><a href="/collections/accessories">All Accessories</a></li><li><a href="/collections/accessories-bags">Bags</a></li><li><a href="/collections/accessories-belts">Belts</a></li><li><a href="/collections/accessories-hats">Hats</a></li><li><a href="/collections/accessories-jewelry">Jewelry</a></li><li><a href="/collections/brands-we-love-fifth-and-ninth">Sunglasses</a></li></ul></div><div class="bp-mega__column"> 14180 <p class="bp-mega__heading"><a href="/collections/all-brands-we-love">Brands We Love</a></p><ul class="bp-mega__links"><li><a href="/collections/brands-we-love-amsterdam-heritage">Amsterdam Heritage</a></li><li><a href="/collections/brands-we-love-ana-luisa">Ana Luisa</a></li><li><a href="/collections/brands-we-love-ettika">Ettika</a></li><li><a href="/collections/brands-we-love-fifth-and-ninth">Fifth & Ninth</a></li><li><a href="/collections/brands-we-love-hanky-panky">Hanky Panky</a></li><li><a href="/collections/brands-we-love-hat-attack-new-york">Hat Attack</a></li><li><a href="/collections/brands-we-love-jardin-jewelry">Jardin Jewelry</a></li><li><a href="/collections/brands-we-love-miami-chains-co">Miami Chains & Co.</a></li><li><a href="/collections/brands-we-love-st-moran">St. Moran</a></li><li><a href="/collections/brands-we-love-the-sak">The Sak</a></li><li><a href="/collections/brands-we-love-t-jazelle">T. Jazelle</a></li><li><a href="/collections/brands-we-love-w-y-e-t-h">WYETH</a></li></ul></div></div><div class="bp-mega__promos"><div class="bp-mega__promo"> 14181 <a href="/collections/shoes-boots" class="bp-mega__promo-link"> 14182 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/Image_63.jpg?v=1790780283&width=400" alt="Boots" srcset="//www.bostonproper.com/cdn/shop/files/Image_63.jpg?v=1790780283&width=200 200w, //www.bostonproper.com/cdn/shop/files/Image_63.jpg?v=1790780283&width=400 400w" width="400" height="500" loading="lazy"> 14183</div> 14184 <h4 class="bp-mega__promo-title">Boots</h4><div class="bp-mega__promo-copy"><p>These boots are going places. Wild prints. Rich color stories. Versatile lengths. A new look for every mood.<br/><br/></p></div><span class="bp-mega__promo-cta">Shop Now</span> 14185 </a> 14186</div> 14187 14188<div class="bp-mega__promo"> 14189 <a href="/collections/accessories-jewelry" class="bp-mega__promo-link"> 14190 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/Image_74.jpg?v=1790800759&width=400" alt="Jewelry" srcset="//www.bostonproper.com/cdn/shop/files/Image_74.jpg?v=1790800759&width=200 200w, //www.bostonproper.com/cdn/shop/files/Image_74.jpg?v=1790800759&width=400 400w" width="400" height="500" loading="lazy"> 14191</div> 14192 <h4 class="bp-mega__promo-title">Jewelry</h4><div class="bp-mega__promo-copy"><p>Drenched in the details that define Boston Proper. Layered jewels and accent pieces that set the tone. </p></div><span class="bp-mega__promo-cta">Shop Now</span> 14193 </a> 14194</div> 14195 14196</div></div> 14197 </div> 14198 </div></li><li class="bp-mm-item" data-bp-mm-id="AdVdsY2tEV2JFWlVvV__bp_menu_shoes"><button type="button" class="bp-mm-item__row" data-open-view="AdVdsY2tEV2JFWlVvV__bp_menu_shoes">
14199 <span class="bp-mm-item__label">Shoes & Accessories</span> 14200 <span class="bp-mm-item__chevron" aria-hidden="true"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 14201 <path d="M11 5.5L7 9.5L3 5.5" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 14202</svg> 14203</span> 14204 </button> 14205 14206 <section class="bp-mm-view" data-view="AdVdsY2tEV2JFWlVvV__bp_menu_shoes" hidden> 14207 <header class="bp-mm-view__header"> 14208 <button type="button" class="bp-mm-view__back" data-back> 14209 <span class="bp-mm-view__back-icon" aria-hidden="true"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 14210 <path d="M11 5.5L7 9.5L3 5.5" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 14211</svg> 14212</span> 14213 <span>Shoes & Accessories</span> 14214 </button> 14215 <button type="button" class="bp-mm-view__close" data-close aria-label="Close menu"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 14216 <path d="M12 2L2 12" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 14217 <path d="M12 12L2 2" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 14218</svg> 14219</button> 14220 </header><div class="bp-mm-view__sections"><section class="bp-mm-section"> 14221 <p class="bp-mm-section__heading"><a href="/collections/shoes">Shoes</a></p><ul class="bp-mm-section__links"><li><a href="/collections/shoes">All Shoes</a></li><li><a href="/collections/shoes-boots">Boots & Booties</a></li><li><a href="/collections/shoes-heels">Heels</a></li><li><a href="/collections/shoes-sandals">Sandals</a></li><li><a href="/collections/shoes-sneakers">Sneakers</a></li><li><a href="/collections/shoes-wedges">Wedges</a></li></ul></section><section class="bp-mm-section"> 14222 <p class="bp-mm-section__heading"><a href="/collections/accessories">Accessories</a></p><ul class="bp-mm-section__links"><li><a href="/collections/accessories">All Accessories</a></li><li><a href="/collections/accessories-bags">Bags</a></li><li><a href="/collections/accessories-belts">Belts</a></li><li><a href="/collections/accessories-hats">Hats</a></li><li><a href="/collections/accessories-jewelry">Jewelry</a></li><li><a href="/collections/brands-we-love-fifth-and-ninth">Sunglasses</a></li></ul></section><section class="bp-mm-section"> 14223 <p class="bp-mm-section__heading"><a href="/collections/all-brands-we-love">Brands We Love</a></p><ul class="bp-mm-section__links"><li><a href="/collections/brands-we-love-amsterdam-heritage">Amsterdam Heritage</a></li><li><a href="/collections/brands-we-love-ana-luisa">Ana Luisa</a></li><li><a href="/collections/brands-we-love-ettika">Ettika</a></li><li><a href="/collections/brands-we-love-fifth-and-ninth">Fifth & Ninth</a></li><li><a href="/collections/brands-we-love-hanky-panky">Hanky Panky</a></li><li><a href="/collections/brands-we-love-hat-attack-new-york">Hat Attack</a></li><li><a href="/collections/brands-we-love-jardin-jewelry">Jardin Jewelry</a></li><li><a href="/collections/brands-we-love-miami-chains-co">Miami Chains & Co.</a></li><li><a href="/collections/brands-we-love-st-moran">St. Moran</a></li><li><a href="/collections/brands-we-love-the-sak">The Sak</a></li><li><a href="/collections/brands-we-love-t-jazelle">T. Jazelle</a></li><li><a href="/collections/brands-we-love-w-y-e-t-h">WYETH</a></li></ul></section></div><div class="bp-mm-view__promos"><div class="bp-mega__promo"> 14224 <a href="/collections/shoes-boots" class="bp-mega__promo-link"> 14225 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/Image_63.jpg?v=1790780283&width=400" alt="Boots" srcset="//www.bostonproper.com/cdn/shop/files/Image_63.jpg?v=1790780283&width=200 200w, //www.bostonproper.com/cdn/shop/files/Image_63.jpg?v=1790780283&width=400 400w" width="400" height="500" loading="lazy"> 14226</div> 14227 <h4 class="bp-mega__promo-title">Boots</h4><div class="bp-mega__promo-copy"><p>These boots are going places. Wild prints. Rich color stories. Versatile lengths. A new look for every mood.<br/><br/></p></div><span class="bp-mega__promo-cta">Shop Now</span> 14228 </a> 14229</div> 14230 14231<div class="bp-mega__promo"> 14232 <a href="/collections/accessories-jewelry" class="bp-mega__promo-link"> 14233 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/Image_74.jpg?v=1790800759&width=400" alt="Jewelry" srcset="//www.bostonproper.com/cdn/shop/files/Image_74.jpg?v=1790800759&width=200 200w, //www.bostonproper.com/cdn/shop/files/Image_74.jpg?v=1790800759&width=400 400w" width="400" height="500" loading="lazy"> 14234</div> 14235 <h4 class="bp-mega__promo-title">Jewelry</h4><div class="bp-mega__promo-copy"><p>Drenched in the details that define Boston Proper. Layered jewels and accent pieces that set the tone. </p></div><span class="bp-mega__promo-cta">Shop Now</span> 14236 </a> 14237</div> 14238 14239</div></section></li> 14240 14241 14242 14243<li 14244 role="presentation" 14245 class="menu-list__list-item bp-menu-item-li" 14246 data-bp-menu-item 14247 14248><a 14249 href="/collections/sale-clearance" 14250 data-skip-node-update="true" 14251 class="menu-list__link" 14252 aria-controls="bp-submenu-AVFlZUDZaR3FYUVAyd__bp_menu_sale" 14253 aria-haspopup="true" 14254 aria-expanded="false" 14255 ref="menuitem" 14256 >
14257 <span class="menu-list__link-title">Sale & Clearance</span> 14258 </a> 14259 <div class="bp-mega-submenu" id="bp-submenu-AVFlZUDZaR3FYUVAyd__bp_menu_sale"> 14260 <div class="bp-mega-submenu__inner"> 14261 <div class="bp-mega bp-mega--columns-promos"><div class="bp-mega__columns"><div class="bp-mega__column"> 14262 <p class="bp-mega__heading"><a href="/collections/sale-clearance">Sale & Clearance</a></p><ul class="bp-mega__links"><li><a href="/collections/sale-clearance">Shop All</a></li><li><a href="/collections/new-markdowns">New Markdowns</a></li><li><a href="/collections/sale">Sale</a></li><li><a href="/collections/clearance">Clearance</a></li></ul></div><div class="bp-mega__column"> 14263 <p class="bp-mega__heading"><a href="/collections/sale-clearance">Shop By Category</a></p><ul class="bp-mega__links"><li><a href="/collections/sale-clearance-accessories">Accessories</a></li><li><a href="/collections/sale-clearance-athleisure">Athleisure</a></li><li><a href="/collections/sale-clearance-dresses">Dresses</a></li><li><a href="/collections/sale-clearance-jackets">Jackets & Blazers</a></li><li><a href="/collections/sale-clearance-jeans">Jeans</a></li><li><a href="/collections/sale-clearance-outerwear">Outerwear</a></li><li><a href="/collections/sale-clearance-pants-leggings">Pants & Leggings</a></li><li><a href="/collections/sale-clearance-shoes">Shoes</a></li><li><a href="/collections/sale-clearance-shorts">Shorts</a></li><li><a href="/collections/sale-clearance-skirts">Skirts</a></li><li><a href="/collections/sale-clearance-sweaters">Sweaters</a></li><li><a href="/collections/sale-clearance-swimwear">Swimwear</a></li><li><a href="/collections/sale-clearance-tops">Tops</a></li></ul></div><div class="bp-mega__column"> 14264 <p class="bp-mega__heading"><a href="/collections/sale-clearance">Promotions</a></p><ul class="bp-mega__links"><li><a href="/collections/new-arrivals">20% off $175+ plus free shipping</a></li><li><a href="/collections/winter-classic-rib-styles">Save $45 on 3 or $25 on 2</a></li><li><a href="/products/boston-proper-printed-scarf-with-love-bp">Free gift with purchase of $400+</a></li></ul></div></div><div class="bp-mega__promos"><div class="bp-mega__promo"> 14265 <a href="/collections/new-markdowns" class="bp-mega__promo-link"> 14266 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/Image_b520536c-2111-43e4-b1e8-166793f8fe43.jpg?v=1790704244&width=400" alt="New Markdowns" srcset="//www.bostonproper.com/cdn/shop/files/Image_b520536c-2111-43e4-b1e8-166793f8fe43.jpg?v=1790704244&width=200 200w, //www.bostonproper.com/cdn/shop/files/Image_b520536c-2111-43e4-b1e8-166793f8fe43.jpg?v=1790704244&width=400 400w" width="400" height="500" loading="lazy"> 14267</div> 14268 <h4 class="bp-mega__promo-title">New Markdowns</h4><div class="bp-mega__promo-copy"><p>Rare finds. Newly reduced.</p></div><span class="bp-mega__promo-cta">Shop Now</span> 14269 </a> 14270</div> 14271 14272<div class="bp-mega__promo"> 14273 <a href="/collections/clearance" class="bp-mega__promo-link"> 14274 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/Image-1.jpg?v=1790780282&width=400" alt="Clearance" srcset="//www.bostonproper.com/cdn/shop/files/Image-1.jpg?v=1790780282&width=200 200w, //www.bostonproper.com/cdn/shop/files/Image-1.jpg?v=1790780282&width=400 400w" width="400" height="500" loading="lazy"> 14275</div> 14276 <h4 class="bp-mega__promo-title">Clearance</h4><div class="bp-mega__promo-copy"><p>Your size wonât stay here long.</p></div><span class="bp-mega__promo-cta">Shop Now</span> 14277 </a> 14278</div> 14279 14280</div></div> 14281 </div> 14282 </div></li><li class="bp-mm-item" data-bp-mm-id="AVFlZUDZaR3FYUVAyd__bp_menu_sale"><button type="button" class="bp-mm-item__row" data-open-view="AVFlZUDZaR3FYUVAyd__bp_menu_sale"> 14283 <span class="bp-mm-item__label">Sale & Clearance</span> 14284 <span class="bp-mm-item__chevron" aria-hidden="true"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 14285 <path d="M11 5.5L7 9.5L3 5.5" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 14286</svg> 14287</span> 14288 </button> 14289 14290 <section class="bp-mm-view" data-view="AVFlZUDZaR3FYUVAyd__bp_menu_sale" hidden> 14291 <header class="bp-mm-view__header"> 14292 <button type="button" class="bp-mm-view__back" data-back>
14293 <span class="bp-mm-view__back-icon" aria-hidden="true"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 14294 <path d="M11 5.5L7 9.5L3 5.5" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 14295</svg> 14296</span> 14297 <span>Sale & Clearance</span> 14298 </button> 14299 <button type="button" class="bp-mm-view__close" data-close aria-label="Close menu"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 14300 <path d="M12 2L2 12" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 14301 <path d="M12 12L2 2" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 14302</svg> 14303</button> 14304 </header><div class="bp-mm-view__sections"><section class="bp-mm-section"> 14305 <p class="bp-mm-section__heading"><a href="/collections/sale-clearance">Sale & Clearance</a></p><ul class="bp-mm-section__links"><li><a href="/collections/sale-clearance">Shop All</a></li><li><a href="/collections/new-markdowns">New Markdowns</a></li><li><a href="/collections/sale">Sale</a></li><li><a href="/collections/clearance">Clearance</a></li></ul></section><section class="bp-mm-section"> 14306 <p class="bp-mm-section__heading"><a href="/collections/sale-clearance">Shop By Category</a></p><ul class="bp-mm-section__links"><li><a href="/collections/sale-clearance-accessories">Accessories</a></li><li><a href="/collections/sale-clearance-athleisure">Athleisure</a></li><li><a href="/collections/sale-clearance-dresses">Dresses</a></li><li><a href="/collections/sale-clearance-jackets">Jackets & Blazers</a></li><li><a href="/collections/sale-clearance-jeans">Jeans</a></li><li><a href="/collections/sale-clearance-outerwear">Outerwear</a></li><li><a href="/collections/sale-clearance-pants-leggings">Pants & Leggings</a></li><li><a href="/collections/sale-clearance-shoes">Shoes</a></li><li><a href="/collections/sale-clearance-shorts">Shorts</a></li><li><a href="/collections/sale-clearance-skirts">Skirts</a></li><li><a href="/collections/sale-clearance-sweaters">Sweaters</a></li><li><a href="/collections/sale-clearance-swimwear">Swimwear</a></li><li><a href="/collections/sale-clearance-tops">Tops</a></li></ul></section><section class="bp-mm-section"> 14307 <p class="bp-mm-section__heading"><a href="/collections/sale-clearance">Promotions</a></p><ul class="bp-mm-section__links"><li><a href="/collections/new-arrivals">20% off $175+ plus free shipping</a></li><li><a href="/collections/winter-classic-rib-styles">Save $45 on 3 or $25 on 2</a></li><li><a href="/products/boston-proper-printed-scarf-with-love-bp">Free gift with purchase of $400+</a></li></ul></section></div><div class="bp-mm-view__promos"><div class="bp-mega__promo"> 14308 <a href="/collections/new-markdowns" class="bp-mega__promo-link"> 14309 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/Image_b520536c-2111-43e4-b1e8-166793f8fe43.jpg?v=1790704244&width=400" alt="New Markdowns" srcset="//www.bostonproper.com/cdn/shop/files/Image_b520536c-2111-43e4-b1e8-166793f8fe43.jpg?v=1790704244&width=200 200w, //www.bostonproper.com/cdn/shop/files/Image_b520536c-2111-43e4-b1e8-166793f8fe43.jpg?v=1790704244&width=400 400w" width="400" height="500" loading="lazy"> 14310</div> 14311 <h4 class="bp-mega__promo-title">New Markdowns</h4><div class="bp-mega__promo-copy"><p>Rare finds. Newly reduced.</p></div><span class="bp-mega__promo-cta">Shop Now</span> 14312 </a> 14313</div> 14314 14315<div class="bp-mega__promo"> 14316 <a href="/collections/clearance" class="bp-mega__promo-link"> 14317 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/Image-1.jpg?v=1790780282&width=400" alt="Clearance" srcset="//www.bostonproper.com/cdn/shop/files/Image-1.jpg?v=1790780282&width=200 200w, //www.bostonproper.com/cdn/shop/files/Image-1.jpg?v=1790780282&width=400 400w" width="400" height="500" loading="lazy"> 14318</div> 14319 <h4 class="bp-mega__promo-title">Clearance</h4><div class="bp-mega__promo-copy"><p>Your size wonât stay here long.</p></div><span class="bp-mega__promo-cta">Shop Now</span> 14320 </a> 14321</div> 14322 14323</div></section></li> 14324 14325 14326 14327<li 14328 role="presentation" 14329 class="menu-list__list-item bp-menu-item-li" 14330 data-bp-menu-item 14331 14332><a 14333 href="/blogs/styleedit" 14334 data-skip-node-update="true" 14335 class="menu-list__link" 14336 aria-controls="bp-submenu-ATVRxYnVkRFJjc0I3R__bp_menu_styleedit" 14337 aria-haspopup="true" 14338 aria-expanded="false" 14339 ref="menuitem" 14340 >
14341 <span class="menu-list__link-title">styleEDIT</span> 14342 </a> 14343 <div class="bp-mega-submenu" id="bp-submenu-ATVRxYnVkRFJjc0I3R__bp_menu_styleedit"> 14344 <div class="bp-mega-submenu__inner"> 14345 <div class="bp-mega bp-mega--columns-promos"><div class="bp-mega__columns"><div class="bp-mega__column"> 14346 <p class="bp-mega__heading"><a href="/blogs/styleedit">styleEDIT: What's New</a></p><ul class="bp-mega__links"><li><a href="/pages/new-to-boston-proper">New to Boston Proper? Start Here</a></li><li><a href="/blogs/styleedit/defy-all-expectations-why-style-doesn-t-expire-it-evolves">Why Style Doesn't Expire. It Evolves.</a></li><li><a href="https://www.bostonproper.com/blogs/styleedit/denim-dresses-are-back-here-s-how-to-wear-them-now">How to Wear Denim Dresses</a></li><li><a href="/blogs/styleedit">Read All</a></li></ul></div><div class="bp-mega__column"> 14347 <p class="bp-mega__heading"><a href="/collections/new-arrivals">Trending Now</a></p><ul class="bp-mega__links"><li><a href="/collections/featured-shop-animal-prints">Animal Prints</a></li><li><a href="/collections/featured-shops-matching-sets">Matching Sets</a></li><li><a href="/collections/color-crush-wine">Trending Color: Wine</a></li><li><a href="/collections/featured-shop-intricate-details">Intricate Details</a></li><li><a href="/pages/the-jacket-edit">Style Guide: The Jacket Edit</a></li></ul></div><div class="bp-mega__column"> 14348 <p class="bp-mega__heading"><a href="/blogs/styleedit">Discover</a></p><ul class="bp-mega__links"><li><a href="/pages/behind-the-seams">Behind the Seams</a></li><li><a href="/pages/sheryls-loves">Sheryl's Loves</a></li><li><a href="/pages/digital-catalog">Digital Catalog</a></li></ul></div></div><div class="bp-mega__promos"><div class="bp-mega__promo"> 14349 <a href="/pages/sheryls-loves" class="bp-mega__promo-link"> 14350 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/Image_65.jpg?v=1790780283&width=400" alt="Sherylâs Loves" srcset="//www.bostonproper.com/cdn/shop/files/Image_65.jpg?v=1790780283&width=200 200w, //www.bostonproper.com/cdn/shop/files/Image_65.jpg?v=1790780283&width=400 400w" width="400" height="500" loading="lazy"> 14351</div> 14352 <h4 class="bp-mega__promo-title">Sherylâs Loves</h4><div class="bp-mega__promo-copy"><p>âThese pieces feel so me. Feminine, confident, and exactly how I want to show up right now.â â¨âSheryl, Editor-in-Chic </p></div><span class="bp-mega__promo-cta">Shop Now</span> 14353 </a> 14354</div> 14355 14356<div class="bp-mega__promo"> 14357 <a href="/pages/digital-catalog" class="bp-mega__promo-link"> 14358 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/Image_75.jpg?v=1790800820&width=400" alt="Digital Catalog" srcset="//www.bostonproper.com/cdn/shop/files/Image_75.jpg?v=1790800820&width=200 200w, //www.bostonproper.com/cdn/shop/files/Image_75.jpg?v=1790800820&width=400 400w" width="400" height="500" loading="lazy"> 14359</div> 14360 <h4 class="bp-mega__promo-title">Digital Catalog</h4><div class="bp-mega__promo-copy"><p>Turn the page. Your best style era starts here. </p></div><span class="bp-mega__promo-cta">Shop Now</span> 14361 </a> 14362</div> 14363 14364</div></div> 14365 </div> 14366 </div></li><li class="bp-mm-item" data-bp-mm-id="ATVRxYnVkRFJjc0I3R__bp_menu_styleedit"><button type="button" class="bp-mm-item__row" data-open-view="ATVRxYnVkRFJjc0I3R__bp_menu_styleedit"> 14367 <span class="bp-mm-item__label">styleEDIT</span> 14368 <span class="bp-mm-item__chevron" aria-hidden="true"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 14369 <path d="M11 5.5L7 9.5L3 5.5" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 14370</svg> 14371</span> 14372 </button> 14373 14374 <section class="bp-mm-view" data-view="ATVRxYnVkRFJjc0I3R__bp_menu_styleedit" hidden> 14375 <header class="bp-mm-view__header"> 14376 <button type="button" class="bp-mm-view__back" data-back> 14377 <span class="bp-mm-view__back-icon" aria-hidden="true"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 14378 <path d="M11 5.5L7 9.5L3 5.5" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 14379</svg> 14380</span>
14381 <span>styleEDIT</span> 14382 </button> 14383 <button type="button" class="bp-mm-view__close" data-close aria-label="Close menu"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 14384 <path d="M12 2L2 12" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 14385 <path d="M12 12L2 2" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 14386</svg> 14387</button> 14388 </header><div class="bp-mm-view__sections"><section class="bp-mm-section"> 14389 <p class="bp-mm-section__heading"><a href="/blogs/styleedit">styleEDIT: What's New</a></p><ul class="bp-mm-section__links"><li><a href="/pages/new-to-boston-proper">New to Boston Proper? Start Here</a></li><li><a href="/blogs/styleedit/defy-all-expectations-why-style-doesn-t-expire-it-evolves">Why Style Doesn't Expire. It Evolves.</a></li><li><a href="https://www.bostonproper.com/blogs/styleedit/denim-dresses-are-back-here-s-how-to-wear-them-now">How to Wear Denim Dresses</a></li><li><a href="/blogs/styleedit">Read All</a></li></ul></section><section class="bp-mm-section"> 14390 <p class="bp-mm-section__heading"><a href="/collections/new-arrivals">Trending Now</a></p><ul class="bp-mm-section__links"><li><a href="/collections/featured-shop-animal-prints">Animal Prints</a></li><li><a href="/collections/featured-shops-matching-sets">Matching Sets</a></li><li><a href="/collections/color-crush-wine">Trending Color: Wine</a></li><li><a href="/collections/featured-shop-intricate-details">Intricate Details</a></li><li><a href="/pages/the-jacket-edit">Style Guide: The Jacket Edit</a></li></ul></section><section class="bp-mm-section"> 14391 <p class="bp-mm-section__heading"><a href="/blogs/styleedit">Discover</a></p><ul class="bp-mm-section__links"><li><a href="/pages/behind-the-seams">Behind the Seams</a></li><li><a href="/pages/sheryls-loves">Sheryl's Loves</a></li><li><a href="/pages/digital-catalog">Digital Catalog</a></li></ul></section></div><div class="bp-mm-view__promos"><div class="bp-mega__promo"> 14392 <a href="/pages/sheryls-loves" class="bp-mega__promo-link"> 14393 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/Image_65.jpg?v=1790780283&width=400" alt="Sherylâs Loves" srcset="//www.bostonproper.com/cdn/shop/files/Image_65.jpg?v=1790780283&width=200 200w, //www.bostonproper.com/cdn/shop/files/Image_65.jpg?v=1790780283&width=400 400w" width="400" height="500" loading="lazy"> 14394</div> 14395 <h4 class="bp-mega__promo-title">Sherylâs Loves</h4><div class="bp-mega__promo-copy"><p>âThese pieces feel so me. Feminine, confident, and exactly how I want to show up right now.â â¨âSheryl, Editor-in-Chic </p></div><span class="bp-mega__promo-cta">Shop Now</span> 14396 </a> 14397</div> 14398 14399<div class="bp-mega__promo"> 14400 <a href="/pages/digital-catalog" class="bp-mega__promo-link"> 14401 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/Image_75.jpg?v=1790800820&width=400" alt="Digital Catalog" srcset="//www.bostonproper.com/cdn/shop/files/Image_75.jpg?v=1790800820&width=200 200w, //www.bostonproper.com/cdn/shop/files/Image_75.jpg?v=1790800820&width=400 400w" width="400" height="500" loading="lazy"> 14402</div> 14403 <h4 class="bp-mega__promo-title">Digital Catalog</h4><div class="bp-mega__promo-copy"><p>Turn the page. Your best style era starts here. </p></div><span class="bp-mega__promo-cta">Shop Now</span> 14404 </a> 14405</div> 14406 14407</div></section></li> 14408 14409 14410 14411<li 14412 role="presentation" 14413 class="menu-list__list-item bp-menu-item-li" 14414 data-bp-menu-item 14415 14416><a 14417 href="/collections/brands-we-love" 14418 data-skip-node-update="true" 14419 class="menu-list__link" 14420 aria-controls="bp-submenu-AUm5TVW95VENzMmkzb__bp_menu_brands" 14421 aria-haspopup="true" 14422 aria-expanded="false" 14423 ref="menuitem" 14424 > 14425 <span class="menu-list__link-title">Brands We Love</span> 14426 </a> 14427 <div class="bp-mega-submenu" id="bp-submenu-AUm5TVW95VENzMmkzb__bp_menu_brands"> 14428 <div class="bp-mega-submenu__inner"> 14429 <div class="bp-mega bp-mega--tabs-promos"><div class="bp-mega__tabs" data-bp-mega-tabs data-bp-tab-columns="2"> 14430 <ul class="bp-mega__tabs-list" role="tablist"><li role="presentation"> 14431 <button 14432 type="button" 14433 role="tab" 14434 id="bp-submenu-AUm5TVW95VENzMmkzb__bp_menu_brands-tab-0" 14435 class="bp-mega__tab is-active" 14436 aria-controls="bp-submenu-AUm5TVW95VENzMmkzb__bp_menu_brands-panel-0" 14437 aria-selected="true" 14438 data-bp-mega-tab 14439 >
14440 <span class="bp-mega__tab-label">BRANDS WE LOVE</span> 14441 <span class="bp-mega__tab-chevron" aria-hidden="true"> 14442 <svg width="20" height="20" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg"> 14443 <path d="M23.8674 11.8674C23.9116 11.8244 23.9558 11.7814 24 11.7385C24.0872 11.8256 24.1744 11.9128 24.2615 12C24.2186 12.0442 24.1756 12.0884 24.1326 12.1326C23.8696 12.4029 23.6066 12.6733 23.3436 12.9436C20.9768 15.3768 18.6099 17.8099 16.2431 20.2431V19.7569C18.6762 22.1238 21.1094 24.4906 23.5425 26.8575C23.8129 27.1205 24.0832 27.3835 24.3536 27.6464C24.473 27.7627 24.5925 27.8789 24.712 27.9951C24.473 28.2341 24.2341 28.473 23.9951 28.712C23.8789 28.5925 23.7627 28.473 23.6464 28.3536C23.3835 28.0832 23.1205 27.8129 22.8575 27.5425C20.4906 25.1094 18.1238 22.6762 15.7569 20.2431L15.514 19.9933L15.7569 19.7569C18.1901 17.3901 20.6232 15.0232 23.0564 12.6564C23.3267 12.3934 23.5971 12.1304 23.8674 11.8674Z" fill="currentColor"/> 14444 <path d="M23.6464 11.6464C23.7627 11.527 23.8789 11.4075 23.9951 11.288C24.2341 11.527 24.473 11.7659 24.712 12.0049C24.5925 12.1211 24.473 12.2373 24.3536 12.3536C24.0832 12.6165 23.8129 12.8795 23.5425 13.1425C21.1094 15.5094 18.6762 17.8762 16.2431 20.2431V19.7569C18.6099 22.1901 20.9768 24.6232 23.3436 27.0564C23.6066 27.3267 23.8696 27.5971 24.1326 27.8674C24.1756 27.9116 24.2186 27.9558 24.2615 28C24.1744 28.0872 24.0872 28.1744 24 28.2615C23.9558 28.2186 23.9116 28.1756 23.8674 28.1326C23.5971 27.8696 23.3267 27.6066 23.0564 27.3436C20.6232 24.9768 18.1901 22.6099 15.7569 20.2431L15.514 19.9933L15.7569 19.7569C18.1238 17.3238 20.4906 14.8906 22.8575 12.4575C23.1205 12.1871 23.3835 11.9168 23.6464 11.6464Z" fill="currentColor"/> 14445 </svg> 14446 </span> 14447 </button> 14448 </li><li role="presentation"> 14449 <button 14450 type="button" 14451 role="tab" 14452 id="bp-submenu-AUm5TVW95VENzMmkzb__bp_menu_brands-tab-1" 14453 class="bp-mega__tab" 14454 aria-controls="bp-submenu-AUm5TVW95VENzMmkzb__bp_menu_brands-panel-1" 14455 aria-selected="false" 14456 data-bp-mega-tab 14457 > 14458 <span class="bp-mega__tab-label">JEWELRY & ACCESSORIES</span> 14459 <span class="bp-mega__tab-chevron" aria-hidden="true"> 14460 <svg width="20" height="20" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg"> 14461 <path d="M23.8674 11.8674C23.9116 11.8244 23.9558 11.7814 24 11.7385C24.0872 11.8256 24.1744 11.9128 24.2615 12C24.2186 12.0442 24.1756 12.0884 24.1326 12.1326C23.8696 12.4029 23.6066 12.6733 23.3436 12.9436C20.9768 15.3768 18.6099 17.8099 16.2431 20.2431V19.7569C18.6762 22.1238 21.1094 24.4906 23.5425 26.8575C23.8129 27.1205 24.0832 27.3835 24.3536 27.6464C24.473 27.7627 24.5925 27.8789 24.712 27.9951C24.473 28.2341 24.2341 28.473 23.9951 28.712C23.8789 28.5925 23.7627 28.473 23.6464 28.3536C23.3835 28.0832 23.1205 27.8129 22.8575 27.5425C20.4906 25.1094 18.1238 22.6762 15.7569 20.2431L15.514 19.9933L15.7569 19.7569C18.1901 17.3901 20.6232 15.0232 23.0564 12.6564C23.3267 12.3934 23.5971 12.1304 23.8674 11.8674Z" fill="currentColor"/> 14462 <path d="M23.6464 11.6464C23.7627 11.527 23.8789 11.4075 23.9951 11.288C24.2341 11.527 24.473 11.7659 24.712 12.0049C24.5925 12.1211 24.473 12.2373 24.3536 12.3536C24.0832 12.6165 23.8129 12.8795 23.5425 13.1425C21.1094 15.5094 18.6762 17.8762 16.2431 20.2431V19.7569C18.6099 22.1901 20.9768 24.6232 23.3436 27.0564C23.6066 27.3267 23.8696 27.5971 24.1326 27.8674C24.1756 27.9116 24.2186 27.9558 24.2615 28C24.1744 28.0872 24.0872 28.1744 24 28.2615C23.9558 28.2186 23.9116 28.1756 23.8674 28.1326C23.5971 27.8696 23.3267 27.6066 23.0564 27.3436C20.6232 24.9768 18.1901 22.6099 15.7569 20.2431L15.514 19.9933L15.7569 19.7569C18.1238 17.3238 20.4906 14.8906 22.8575 12.4575C23.1205 12.1871 23.3835 11.9168 23.6464 11.6464Z" fill="currentColor"/> 14463 </svg> 14464 </span> 14465 </button> 14466 </li><li role="presentation"> 14467 <button 14468 type="button" 14469 role="tab" 14470 id="bp-submenu-AUm5TVW95VENzMmkzb__bp_menu_brands-tab-2" 14471 class="bp-mega__tab" 14472 aria-controls="bp-submenu-AUm5TVW95VENzMmkzb__bp_menu_brands-panel-2" 14473 aria-selected="false" 14474 data-bp-mega-tab 14475 >
14476 <span class="bp-mega__tab-label">ATHLEISURE</span> 14477 <span class="bp-mega__tab-chevron" aria-hidden="true"> 14478 <svg width="20" height="20" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg"> 14479 <path d="M23.8674 11.8674C23.9116 11.8244 23.9558 11.7814 24 11.7385C24.0872 11.8256 24.1744 11.9128 24.2615 12C24.2186 12.0442 24.1756 12.0884 24.1326 12.1326C23.8696 12.4029 23.6066 12.6733 23.3436 12.9436C20.9768 15.3768 18.6099 17.8099 16.2431 20.2431V19.7569C18.6762 22.1238 21.1094 24.4906 23.5425 26.8575C23.8129 27.1205 24.0832 27.3835 24.3536 27.6464C24.473 27.7627 24.5925 27.8789 24.712 27.9951C24.473 28.2341 24.2341 28.473 23.9951 28.712C23.8789 28.5925 23.7627 28.473 23.6464 28.3536C23.3835 28.0832 23.1205 27.8129 22.8575 27.5425C20.4906 25.1094 18.1238 22.6762 15.7569 20.2431L15.514 19.9933L15.7569 19.7569C18.1901 17.3901 20.6232 15.0232 23.0564 12.6564C23.3267 12.3934 23.5971 12.1304 23.8674 11.8674Z" fill="currentColor"/> 14480 <path d="M23.6464 11.6464C23.7627 11.527 23.8789 11.4075 23.9951 11.288C24.2341 11.527 24.473 11.7659 24.712 12.0049C24.5925 12.1211 24.473 12.2373 24.3536 12.3536C24.0832 12.6165 23.8129 12.8795 23.5425 13.1425C21.1094 15.5094 18.6762 17.8762 16.2431 20.2431V19.7569C18.6099 22.1901 20.9768 24.6232 23.3436 27.0564C23.6066 27.3267 23.8696 27.5971 24.1326 27.8674C24.1756 27.9116 24.2186 27.9558 24.2615 28C24.1744 28.0872 24.0872 28.1744 24 28.2615C23.9558 28.2186 23.9116 28.1756 23.8674 28.1326C23.5971 27.8696 23.3267 27.6066 23.0564 27.3436C20.6232 24.9768 18.1901 22.6099 15.7569 20.2431L15.514 19.9933L15.7569 19.7569C18.1238 17.3238 20.4906 14.8906 22.8575 12.4575C23.1205 12.1871 23.3835 11.9168 23.6464 11.6464Z" fill="currentColor"/> 14481 </svg> 14482 </span> 14483 </button> 14484 </li><li role="presentation"> 14485 <button 14486 type="button" 14487 role="tab" 14488 id="bp-submenu-AUm5TVW95VENzMmkzb__bp_menu_brands-tab-3" 14489 class="bp-mega__tab" 14490 aria-controls="bp-submenu-AUm5TVW95VENzMmkzb__bp_menu_brands-panel-3" 14491 aria-selected="false" 14492 data-bp-mega-tab 14493 > 14494 <span class="bp-mega__tab-label">HEALTH & BEAUTY</span> 14495 <span class="bp-mega__tab-chevron" aria-hidden="true"> 14496 <svg width="20" height="20" viewBox="0 0 40 40" fill="none" xmlns="http://www.w3.org/2000/svg"> 14497 <path d="M23.8674 11.8674C23.9116 11.8244 23.9558 11.7814 24 11.7385C24.0872 11.8256 24.1744 11.9128 24.2615 12C24.2186 12.0442 24.1756 12.0884 24.1326 12.1326C23.8696 12.4029 23.6066 12.6733 23.3436 12.9436C20.9768 15.3768 18.6099 17.8099 16.2431 20.2431V19.7569C18.6762 22.1238 21.1094 24.4906 23.5425 26.8575C23.8129 27.1205 24.0832 27.3835 24.3536 27.6464C24.473 27.7627 24.5925 27.8789 24.712 27.9951C24.473 28.2341 24.2341 28.473 23.9951 28.712C23.8789 28.5925 23.7627 28.473 23.6464 28.3536C23.3835 28.0832 23.1205 27.8129 22.8575 27.5425C20.4906 25.1094 18.1238 22.6762 15.7569 20.2431L15.514 19.9933L15.7569 19.7569C18.1901 17.3901 20.6232 15.0232 23.0564 12.6564C23.3267 12.3934 23.5971 12.1304 23.8674 11.8674Z" fill="currentColor"/> 14498 <path d="M23.6464 11.6464C23.7627 11.527 23.8789 11.4075 23.9951 11.288C24.2341 11.527 24.473 11.7659 24.712 12.0049C24.5925 12.1211 24.473 12.2373 24.3536 12.3536C24.0832 12.6165 23.8129 12.8795 23.5425 13.1425C21.1094 15.5094 18.6762 17.8762 16.2431 20.2431V19.7569C18.6099 22.1901 20.9768 24.6232 23.3436 27.0564C23.6066 27.3267 23.8696 27.5971 24.1326 27.8674C24.1756 27.9116 24.2186 27.9558 24.2615 28C24.1744 28.0872 24.0872 28.1744 24 28.2615C23.9558 28.2186 23.9116 28.1756 23.8674 28.1326C23.5971 27.8696 23.3267 27.6066 23.0564 27.3436C20.6232 24.9768 18.1901 22.6099 15.7569 20.2431L15.514 19.9933L15.7569 19.7569C18.1238 17.3238 20.4906 14.8906 22.8575 12.4575C23.1205 12.1871 23.3835 11.9168 23.6464 11.6464Z" fill="currentColor"/> 14499 </svg> 14500 </span> 14501 </button> 14502 </li></ul> 14503 14504 <div class="bp-mega__tabs-panels"><div 14505 id="bp-submenu-AUm5TVW95VENzMmkzb__bp_menu_brands-panel-0" 14506 role="tabpanel" 14507 aria-labelledby="bp-submenu-AUm5TVW95VENzMmkzb__bp_menu_brands-tab-0" 14508 class="bp-mega__tab-panel is-active" 14509 data-bp-mega-panel 14510 14511 > 14512 <p class="bp-mega__heading"><a href="/pages/brands-we-love">BRANDS WE LOVE</a></p><ul class="bp-mega__links"><li><a href="/pages/brands-we-love">View All Brands</a></li><li><a href="/collections/all-brands-we-love">Shop All</a></li><li><a href="/collections/brands-we-love-amsterdam-heritage">Amsterdam Heritage</a></li><li><a href="/collections/brands-we-love-a-rose">A.Rose</a></li><li><a href="/collections/brands-we-love-ana-luisa">Ana Luisa</a></li><li><a href="/collections/brands-we-love-anatomie">Anatomie</a></li><li><a href="/collections/brands-we-love-becca">Becca By Rebecca Virtue</a></li><li><a href="/collections/brands-we-love-carmen-marc-valvo">Carmen Marc Valvo</a></li><li><a href="/collections/brands-we-love-change-of-scenery">
14512Change of Scenery</a></li><li><a href="/collections/brands-we-love-ettika">Ettika</a></li><li><a href="/collections/brands-we-love-fifth-and-ninth">Fifth & Ninth</a></li><li><a href="/collections/brands-we-love-hanky-panky">Hanky Panky</a></li><li><a href="/collections/brands-we-love-hat-attack-new-york">Hat Attack</a></li><li><a href="/collections/brands-we-love-jardin-jewelry">Jardin Jewelry</a></li><li><a href="/collections/brands-we-love-mdaire">MDAiRE</a></li><li><a href="/collections/brands-we-love-miami-chains-co">Miami Chains & Co.</a></li><li><a href="/collections/brands-we-love-mimosa-lane">Mimosa Lane</a></li><li><a href="/collections/brands-we-love-pickletini">Pickletini x Boston Proper</a></li><li><a href="/collections/brands-we-love-prolon-life">Prolon Life</a></li><li><a href="/collections/brands-we-love-pq-swim">PQ Swim X Boston Proper</a></li><li><a href="/collections/brands-we-love-saint-jo">Saint JÅ</a></li><li><a href="/collections/brands-we-love-st-moran">St. Moran</a></li><li><a href="/collections/brands-we-love-sum-of-all">Sum Of All</a></li><li><a href="/collections/brands-we-love-the-sak">The Sak</a></li><li><a href="/collections/brands-we-love-t-jazelle">T. Jazelle</a></li><li><a href="/collections/brands-we-love-trina-turk">Trina Turk</a></li><li><a href="/collections/brands-we-love-walter-baker">Walter Baker</a></li><li><a href="/collections/brands-we-love-w-y-e-t-h">WYETH</a></li></ul></div><div 14513 id="bp-submenu-AUm5TVW95VENzMmkzb__bp_menu_brands-panel-1" 14514 role="tabpanel" 14515 aria-labelledby="bp-submenu-AUm5TVW95VENzMmkzb__bp_menu_brands-tab-1" 14516 class="bp-mega__tab-panel" 14517 data-bp-mega-panel 14518 hidden 14519 > 14520 <p class="bp-mega__heading"><a href="/collections/brands-we-love-jewelry">JEWELRY & ACCESSORIES</a></p><ul class="bp-mega__links"><li><a href="/collections/brands-we-love-jewelry">Shop All</a></li><li><a href="/collections/brands-we-love-ana-luisa">Ana Luisa</a></li><li><a href="/collections/brands-we-love-amsterdam-heritage">Amsterdam Heritage</a></li><li><a href="/collections/brands-we-love-ettika">Ettika</a></li><li><a href="/collections/brands-we-love-st-moran">St. Moran</a></li><li><a href="/collections/brands-we-love-t-jazelle">T. Jazelle</a></li><li><a href="/collections/brands-we-love-fifth-and-ninth">Fifth & Ninth</a></li><li><a href="/collections/brands-we-love-hat-attack-new-york">Hat Attack</a></li><li><a href="/collections/brands-we-love-jardin-jewelry">Jardin Jewelry</a></li><li><a href="/collections/brands-we-love-miami-chains-co">Miami Chains & Co.</a></li><li><a href="/collections/brands-we-love-the-sak">The Sak</a></li><li><a href="/collections/brands-we-love-w-y-e-t-h">WYETH</a></li></ul></div><div 14521 id="bp-submenu-AUm5TVW95VENzMmkzb__bp_menu_brands-panel-2" 14522 role="tabpanel" 14523 aria-labelledby="bp-submenu-AUm5TVW95VENzMmkzb__bp_menu_brands-tab-2" 14524 class="bp-mega__tab-panel" 14525 data-bp-mega-panel 14526 hidden 14527 > 14528 <p class="bp-mega__heading"><a href="/collections/all-brands-we-love">ATHLEISURE</a></p><ul class="bp-mega__links"><li><a href="/collections/brands-we-love-anatomie">Anatomie</a></li><li><a href="/collections/brands-we-love-pickletini">Pickletini X Boston Proper</a></li></ul></div><div 14529 id="bp-submenu-AUm5TVW95VENzMmkzb__bp_menu_brands-panel-3" 14530 role="tabpanel" 14531 aria-labelledby="bp-submenu-AUm5TVW95VENzMmkzb__bp_menu_brands-tab-3" 14532 class="bp-mega__tab-panel" 14533 data-bp-mega-panel 14534 hidden 14535 > 14536 <p class="bp-mega__heading"><a href="/collections/brands-we-love-health-and-beauty">HEALTH & BEAUTY</a></p><ul class="bp-mega__links"><li><a href="/collections/brands-we-love-health-and-beauty">Shop All</a></li><li><a href="/collections/brands-we-love-mdaire">MDAiRE</a></li><li><a href="/collections/brands-we-love-mimosa-lane">Mimosa Lane</a></li><li><a href="/collections/brands-we-love-prolon-life">Prolon Life</a></li><li><a href="/collections/brands-we-love-saint-jo">SAINT JÅ</a></li><li><a href="/collections/brands-we-love-sum-of-all">Sum Of All</a></li></ul></div></div><div class="bp-mega__promos bp-mega__promos--tabs"><div class="bp-mega__promo"> 14537 <a href="/collections/brands-we-love-ettika" class="bp-mega__promo-link"> 14538 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/Ettika_flydown.jpg?v=1733336269&width=400" alt="Ettika" srcset="//www.bostonproper.com/cdn/shop/files/Ettika_flydown.jpg?v=1733336269&width=200 200w, //www.bostonproper.com/cdn/shop/files/Ettika_flydown.jpg?v=1733336269&width=400 400w" width="400" height="625" loading="lazy"> 14539</div> 14540 <h4 class="bp-mega__promo-title">Ettika</h4><span class="bp-mega__promo-cta">Shop Now</span> 14541 </a> 14542</div> 14543 14544<div class="bp-mega__promo"> 14545 <a href="/collections/brands-we-love-saint-jo" class="bp-mega__promo-link"> 14546 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/10271_Saint-Jo_flydown_3.jpg?v=1785254177&width=400" alt="SAINT JÅ" srcset="//www.bostonproper.com/cdn/shop/files/10271_Saint-Jo_flydown_3.jpg?v=1785254177&width=200 200w, //www.bostonproper.com/cdn/shop/files/10271_Saint-Jo_flydown_3.jpg?v=1785254177&width=400 400w" width="400" height="501" loading="lazy" style="object-position:50.0% 75.781%;"> 14547</div> 14548 <h4 class="bp-mega__promo-title">SAINT JÅ</h4><span class="bp-mega__promo-cta">Shop Now</span> 14549 </a> 14550</div> 14551 14552</div></div></div> 14553 </div> 14554 </div></li><li class="bp-mm-item" data-bp-mm-id="AUm5TVW95VENzMmkzb__bp_menu_brands"><button type="button" class="bp-mm-item__row" data-open-view="AUm5TVW95VENzMmkzb__bp_menu_brands">
14555 <span class="bp-mm-item__label">Brands We Love</span> 14556 <span class="bp-mm-item__chevron" aria-hidden="true"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 14557 <path d="M11 5.5L7 9.5L3 5.5" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 14558</svg> 14559</span> 14560 </button> 14561 14562 <section class="bp-mm-view" data-view="AUm5TVW95VENzMmkzb__bp_menu_brands" hidden> 14563 <header class="bp-mm-view__header"> 14564 <button type="button" class="bp-mm-view__back" data-back> 14565 <span class="bp-mm-view__back-icon" aria-hidden="true"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 14566 <path d="M11 5.5L7 9.5L3 5.5" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 14567</svg> 14568</span> 14569 <span>Brands We Love</span> 14570 </button> 14571 <button type="button" class="bp-mm-view__close" data-close aria-label="Close menu"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 14572 <path d="M12 2L2 12" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 14573 <path d="M12 12L2 2" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 14574</svg> 14575</button> 14576 </header><div class="bp-mm-view__sections"><section class="bp-mm-section"> 14577 <p class="bp-mm-section__heading"><a href="/pages/brands-we-love">BRANDS WE LOVE</a></p><ul class="bp-mm-section__links"><li><a href="/pages/brands-we-love">View All Brands</a></li><li><a href="/collections/all-brands-we-love">Shop All</a></li><li><a href="/collections/brands-we-love-amsterdam-heritage">Amsterdam Heritage</a></li><li><a href="/collections/brands-we-love-a-rose">A.Rose</a></li><li><a href="/collections/brands-we-love-ana-luisa">Ana Luisa</a></li><li><a href="/collections/brands-we-love-anatomie">Anatomie</a></li><li><a href="/collections/brands-we-love-becca">Becca By Rebecca Virtue</a></li><li><a href="/collections/brands-we-love-carmen-marc-valvo">Carmen Marc Valvo</a></li><li><a href="/collections/brands-we-love-change-of-scenery">Change of Scenery</a></li><li><a href="/collections/brands-we-love-ettika">Ettika</a></li><li><a href="/collections/brands-we-love-fifth-and-ninth">Fifth & Ninth</a></li><li><a href="/collections/brands-we-love-hanky-panky">Hanky Panky</a></li><li><a href="/collections/brands-we-love-hat-attack-new-york">Hat Attack</a></li><li><a href="/collections/brands-we-love-jardin-jewelry">Jardin Jewelry</a></li><li><a href="/collections/brands-we-love-mdaire">MDAiRE</a></li><li><a href="/collections/brands-we-love-miami-chains-co">Miami Chains & Co.</a></li><li><a href="/collections/brands-we-love-mimosa-lane">Mimosa Lane</a></li><li><a href="/collections/brands-we-love-pickletini">Pickletini x Boston Proper</a></li><li><a href="/collections/brands-we-love-prolon-life">Prolon Life</a></li><li><a href="/collections/brands-we-love-pq-swim">PQ Swim X Boston Proper</a></li><li><a href="/collections/brands-we-love-saint-jo">Saint JÅ</a></li><li><a href="/collections/brands-we-love-st-moran">St. Moran</a></li><li><a href="/collections/brands-we-love-sum-of-all">Sum Of All</a></li><li><a href="/collections/brands-we-love-the-sak">The Sak</a></li><li><a href="/collections/brands-we-love-t-jazelle">T. Jazelle</a></li><li><a href="/collections/brands-we-love-trina-turk">Trina Turk</a></li><li><a href="/collections/brands-we-love-walter-baker">Walter Baker</a></li><li><a href="/collections/brands-we-love-w-y-e-t-h">WYETH</a></li></ul></section><section class="bp-mm-section"> 14578 <p class="bp-mm-section__heading"><a href="/collections/brands-we-love-jewelry">JEWELRY & ACCESSORIES</a></p><ul class="bp-mm-section__links"><li><a href="/collections/brands-we-love-jewelry">Shop All</a></li><li><a href="/collections/brands-we-love-ana-luisa">Ana Luisa</a></li><li><a href="/collections/brands-we-love-amsterdam-heritage">Amsterdam Heritage</a></li><li><a href="/collections/brands-we-love-ettika">Ettika</a></li><li><a href="/collections/brands-we-love-st-moran">St. Moran</a></li><li><a href="/collections/brands-we-love-t-jazelle">T. Jazelle</a></li><li><a href="/collections/brands-we-love-fifth-and-ninth">Fifth & Ninth</a></li><li><a href="/collections/brands-we-love-hat-attack-new-york">Hat Attack</a></li><li><a href="/collections/brands-we-love-jardin-jewelry">Jardin Jewelry</a></li><li><a href="/collections/brands-we-love-miami-chains-co">Miami Chains & Co.</a></li><li><a href="/collections/brands-we-love-the-sak">The Sak</a></li><li><a href="/collections/brands-we-love-w-y-e-t-h">WYETH</a></li></ul></section><section class="bp-mm-section"> 14579 <p class="bp-mm-section__heading"><a href="/collections/all-brands-we-love">ATHLEISURE</a></p><ul class="bp-mm-section__links"><li><a href="/collections/brands-we-love-anatomie">Anatomie</a></li><li><a href="/collections/brands-we-love-pickletini">Pickletini X Boston Proper</a></li></ul></section><section class="bp-mm-section"> 14580 <p class="bp-mm-section__heading"><a href="/collections/brands-we-love-health-and-beauty">HEALTH & BEAUTY</a></p><ul class="bp-mm-section__links"><li><a href="/collections/brands-we-love-health-and-beauty">Shop All</a></li><li><a href="/collections/brands-we-love-mdaire">MDAiRE</a></li><li><a href="/collections/brands-we-love-mimosa-lane">Mimosa Lane</a></li><li><a href="/collections/brands-we-love-prolon-life">Prolon Life</a></li><li><a href="/collections/brands-we-love-saint-jo">SAINT JÅ</a></li><li><a href="/collections/brands-we-love-sum-of-all">Sum Of All</a></li></ul></section></div><div class="bp-mm-view__promos"><div class="bp-mega__promo"> 14581 <a href="/collections/brands-we-love-ettika" class="bp-mega__promo-link"> 14582 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/Ettika_flydown.jpg?v=1733336269&width=400" alt="Ettika" srcset="//www.bostonproper.com/cdn/shop/files/Ettika_flydown.jpg?v=1733336269&width=200 200w, //www.bostonproper.com/cdn/shop/files/Ettika_flydown.jpg?v=1733336269&width=400 400w" width="400" height="625" loading="lazy"> 14583</div> 14584 <h4 class="bp-mega__promo-title">Ettika</h4><span class="bp-mega__promo-cta">Shop Now</span> 14585 </a> 14586</div> 14587 14588<div class="bp-mega__promo"> 14589 <a href="/collections/brands-we-love-saint-jo" class="bp-mega__promo-link"> 14590 <div class="bp-mega__promo-image"><img src="//www.bostonproper.com/cdn/shop/files/10271_Saint-Jo_flydown_3.jpg?v=1785254177&width=400" alt="SAINT JÅ" srcset="//www.bostonproper.com/cdn/shop/files/10271_Saint-Jo_flydown_3.jpg?v=1785254177&width=200 200w, //www.bostonproper.com/cdn/shop/files/10271_Saint-Jo_flydown_3.jpg?v=1785254177&width=400 400w" width="400" height="501" loading="lazy" style="object-position:50.0% 75.781%;"> 14591</div> 14592 <h4 class="bp-mega__promo-title">SAINT JÅ</h4><span class="bp-mega__promo-cta">Shop Now</span> 14593 </a> 14594</div> 14595 14596</div></section></li> 14597 14598 14599 14600 14601 14602 <li 14603 class="menu-list__list-item" 14604 role="presentation" 14605 slot="more" 14606 on:focus="/activate" 14607 on:blur="/deactivate"
14608 on:pointerenter="/activate" 14609 on:pointerleave="/deactivate" 14610 ><button class="menu-list__link button-unstyled"> 14611 <span class="menu-list__link-title">More</span> 14612 </button> 14613 </li> 14614 14615</overflow-list> 14616 14617 </nav> 14618 </div> 14619 14620
14620<script 14621 src="//www.bostonproper.com/cdn/shop/t/1521/assets/header-menu.js?v=13599797148522725401790713714" 14622 type="module" 14623 fetchpriority="low" 14624 ></script>
14624 14625 </header-menu> 14626 14627 14628<style data-shopify> 14629 .header__underlay { 14630 --color-submenu: rgb(254 255 250 / 1.0); 14631 } 14632</style> 14633 14634 14635 14636 14637 14638 14639 14640 </div><div 14641 class="header__column header__column--right" 14642 data-testid="header-top-right" 14643 > 14644 14645 14646 14647 14648 14649 14650 14651 14652 14653 14654 14655 14656 14657 <search-button 14658 class="search-action search-action--hidden-on-drawer" 14659 > 14660 <button 14661 on:click="#search-modal/showDialog" 14662 class="button-unstyled header-actions__action" 14663 aria-label="Search" 14664 aria-haspopup="dialog" 14665 > 14666 <span class="hidden"> 14667 Search 14668 </span> 14669 14670 <span class="svg-wrapper "> 14671 <svg width="44" height="44" viewBox="0 0 44 44" fill="none" xmlns="http://www.w3.org/2000/svg"> 14672<path d="M30.6967 19.7055C30.7183 23.8405 27.932 27.3926 24.0949 28.7508C17.6703 31.1232 10.0267 26.8687 10 19.7055C10.0272 12.5414 17.6703 8.28779 24.0953 10.6602C27.932 12.0184 30.7187 15.5704 30.6967 19.7055ZM28.5408 19.7055C28.5477 16.9739 27.3344 14.2088 25.1509 12.5164C22.4003 10.326 18.2959 10.3256 15.5454 12.5164C13.3619 14.2088 12.1486 16.9735 12.1555 19.7055C12.1486 22.437 13.3619 25.2022 15.5454 26.8946C18.2959 29.085 22.4003 29.0854 25.1509 26.8946C27.3344 25.2022 28.5477 22.4374 28.5408 19.7055Z" fill="currentColor"/> 14673<path d="M33.1718 33.9998C31.5945 32.7679 30.2255 31.2984 28.9126 29.7966C28.4814 29.2934 28.0606 28.7816 27.6699 28.2439L28.9794 27.1216C29.4511 27.5898 29.8927 28.084 30.3238 28.5872C31.3971 29.854 32.4207 31.1661 33.3132 32.5696C33.4917 32.8528 33.6659 33.1396 33.8263 33.4388L33.1714 34.0002L33.1718 33.9998Z" fill="currentColor"/> 14674</svg> 14675 14676 </span> 14677 </button> 14678 </search-button> 14679 14680 14681 14682 14683 14684 14685 14686 14687 14688 14689
14690<script 14691 type="module" 14692 src="//www.bostonproper.com/cdn/shop/t/1521/assets/cart-icon.js?v=157241182139685326961790713714" 14693 fetchpriority="low" 14694></script>
14694 14695 14696 14697 14698 14699<header-actions 14700 14701 data-bp-mobile-account="hamburger"> 14702 14703<bp-saved-drawer class="bp-saved-drawer" data-hydration-key="bp-saved-drawer"> 14704 <button 14705 type="button" 14706 class="bp-saved-drawer__trigger header-actions__action" 14707 data-bp-saved-open 14708 aria-haspopup="dialog" 14709 aria-label="Saved for later" 14710 ><svg width="24" height="24" viewBox="0 0 44 44" fill="none" xmlns="http://www.w3.org/2000/svg" aria-hidden="true"> 14711 <path d="M22.1364 34C22.0452 33.9982 21.9449 33.964 21.8658 33.9141L21.8316 33.8952L21.7642 33.8575C21.4558 33.6826 21.1514 33.4983 20.8515 33.3099C15.6957 30.0059 10.1181 24.467 10.0003 17.9623C9.97376 14.3139 11.7681 10.6318 15.693 10.0815C16.2307 10.0038 16.7679 9.98353 17.311 10.0127C19.188 10.1018 20.6434 11.2765 21.4931 12.8915C21.8946 13.6378 22.1791 14.4241 22.3608 15.2504L21.9202 14.8926H22.3527L21.9121 15.2504C22.0937 14.4245 22.3783 13.6378 22.7802 12.8919C23.393 11.7378 24.3673 10.7119 25.6338 10.2771C26.372 10.011 27.1588 9.97409 27.9316 10.0163C28.1434 10.0262 28.37 10.0537 28.5804 10.0829C32.3843 10.6147 34.184 14.0775 34.273 17.6341C34.3472 24.275 28.6604 29.9519 23.4218 33.3094C23.1646 33.4704 22.9057 33.6291 22.6427 33.7815L22.5096 33.857L22.4422 33.8948C22.3527 33.951 22.2416 33.9955 22.1373 33.9995L22.1364 34ZM22.1364 33.1008C22.0735 33.0891 21.9665 33.1211 21.9359 33.1426L21.9431 33.1377C21.9728 33.1175 22.0982 33.0248 22.1333 32.9997C27.2231 29.1984 33.9673 22.4766 32.1775 15.5337C31.6438 13.4328 30.4057 11.8228 28.2045 11.3727C27.4753 11.2325 26.6975 11.1511 25.975 11.3507C24.5561 11.732 23.6255 13.0434 23.1179 14.3621C22.9371 14.8166 22.8113 15.3147 22.7164 15.7926H22.3527C22.1558 15.7926 21.7525 15.7926 21.5569 15.7926C21.4621 15.3152 21.3362 14.8166 21.155 14.3621C20.3821 12.3938 18.9893 10.9663 16.7391 11.2711C15.8701 11.3597 14.9934 11.6223 14.2727 12.1182C12.8008 13.1113 12.1241 14.893 11.8872 16.596C10.9885 23.1456 17.307 29.3895 22.14 32.9997L22.2574 33.0855L22.3158 33.1278L22.3446 33.1485L22.3518 33.1534C22.3482 33.1521 22.3738 33.166 22.3316 33.1426C22.274 33.1125 22.2007 33.0972 22.1373 33.1017L22.1364 33.1008Z" fill="currentColor"/> 14712 </svg> 14713 <span class="bp-saved-drawer__trigger-count" data-bp-saved-trigger-count hidden></span> 14714 </button> 14715 14716 <dialog class="bp-saved-drawer__dialog" data-bp-saved-dialog aria-label="Saved for Later"> 14717 <header class="bp-saved-drawer__header"> 14718 <h2 class="bp-saved-drawer__heading"> 14719 <span class="bp-saved-drawer__bubble" data-bp-saved-bubble>0</span> 14720 Saved for Later 14721 </h2> 14722 <button type="button" class="bp-saved-drawer__close" data-bp-saved-close aria-label="Close"> 14723 <svg width="20" height="20" viewBox="0 0 20 20" fill="none" aria-hidden="true"> 14724 <path d="M5 5l10 10M15 5L5 15" stroke="currentColor" stroke-width="1.4" stroke-linecap="round"/> 14725 </svg> 14726 </button> 14727 </header> 14728 14729 <div class="bp-saved-drawer__body"> 14730 <div class="bp-saved-drawer__loading" data-bp-saved-loading hidden> 14731 <span class="bp-saved-drawer__spinner" aria-hidden="true"></span> 14732 <p>Loading your saved itemsâ¦</p> 14733 </div> 14734 14735 <div class="bp-saved-drawer__empty" data-bp-saved-empty> 14736 <p>No saved items yet.</p> 14737 <p class="bp-saved-drawer__empty-help">Tap the bookmark on any bag item to save it for later.</p> 14738 </div> 14739 14740 <ul class="bp-saved-drawer__list" data-bp-saved-list hidden></ul><template data-bp-saved-template> 14741 <li class="bp-saved-drawer__card" data-bp-saved-card> 14742 <a class="bp-saved-drawer__image" data-bp-card-link> 14743 <img alt="" data-bp-card-image> 14744 </a> 14745 <div class="bp-saved-drawer__details"> 14746 <div class="bp-saved-drawer__title-row"> 14747 <h3 class="bp-saved-drawer__name"><a data-bp-card-link></a></h3> 14748 <span class="bp-saved-drawer__price" data-bp-card-price></span> 14749 </div> 14750 <p class="bp-saved-drawer__variant" data-bp-card-variant></p> 14751 <ul class="bp-saved-drawer__props" data-bp-card-props></ul> 14752 <div class="bp-saved-drawer__actions"> 14753 <button type="button" class="bp-saved-drawer__btn bp-saved-drawer__btn--primary" data-bp-card-move>Move to Bag</button> 14754 <button type="button" class="bp-saved-drawer__btn bp-saved-drawer__btn--secondary" data-bp-card-remove>Remove</button> 14755 </div> 14756 </div> 14757 </li> 14758 </template> 14759 </div> 14760 </dialog> 14761</bp-saved-drawer> 14762 14763<style data-shopify> 14764 bp-saved-drawer { display: contents; } 14765 14766 .bp-saved-drawer__trigger { 14767 position: relative; 14768 background: transparent; 14769 border: 0; 14770 padding: 0; 14771 cursor: pointer; 14772 color: var(--color-foreground); 14773 display: inline-flex; 14774 align-items: center; 14775 justify-content: center; 14776 width: 44px; 14777 height: 44px; 14778 } 14779 .bp-saved-drawer__trigger svg { width: 24px; height: 24px; display: block; } 14780 .bp-saved-drawer__trigger-count { 14781 position: absolute; 14782 top: 4px; 14783 right: 4px; 14784 min-width: 18px; 14785 height: 18px; 14786 padding: 0 5px; 14787 /* 18Ã18 circular notification badge â see matching note in 14788 snippets/bp-cart-drawer.liquid. Kept as a circle, not squared. */ 14789 border-radius: 50%; 14790 background: #EBAE55; 14791 /* Dark ink on the gold badge â white on #EBAE55 is only 1.96:1 (fails 14792 WCAG). Matches the cart trigger-count badge. */ 14793 color: var(--bp-color-text); 14794 font-family: var(--bp-font-body); 14795 font-size: 11px; 14796 font-weight: 500; 14797 display: inline-flex; 14798 align-items: center; 14799 justify-content: center; 14800 } 14801 .bp-saved-drawer__trigger-count[hidden] { display: none; } 14802 /* Loading badge â shown while we wait for Swym to reconcile, so the count 14803 never briefly shows a stale/wrong number. A quiet pulse on the empty 14804 badge; the real number replaces it once synced. */ 14805 .bp-saved-drawer__trigger-count.is-loading { 14806 background: rgba(23, 22, 9, 0.18); 14807 min-width: 12px; 14808 height: 12px; 14809 padding: 0; 14810 top: 6px; 14811 right: 6px; 14812 animation: bp-saved-badge-pulse 0.9s ease-in-out infinite; 14813 } 14814 @keyframes bp-saved-badge-pulse { 0%, 100% { opacity: 0.35; } 50% { opacity: 0.9; } } 14815 @media (prefers-reduced-motion: reduce) { 14816 .bp-saved-drawer__trigger-count.is-loading { animation: none; opacity: 0.5; } 14817 } 14818 14819 .bp-saved-drawer__dialog { 14820 position: fixed; 14821 inset: 0 0 0 auto; 14822 margin: 0; 14823 padding: 0; 14824 border: 0; 14825 width: calc(100vw / 3); 14826 min-width: 480px; 14827 max-width: 100vw; 14828 height: 100dvh; 14829 max-height: 100dvh; 14830 background: var(--bp-color-bg-page); 14831 color: var(--bp-color-text);
14832 box-shadow: -8px 0 30px rgb(0 0 0 / 0.12); 14833 overflow: hidden; 14834 font-family: var(--bp-font-body); 14835 z-index: 9999; 14836 } 14837 .bp-saved-drawer__dialog[open] { 14838 display: flex; 14839 flex-direction: column; 14840 animation: bpDrawerSlideIn 0.25s cubic-bezier(0.4, 0, 0.2, 1) forwards; 14841 } 14842 .bp-saved-drawer__dialog.is-closing { 14843 animation: bpDrawerSlideOut 0.2s cubic-bezier(0.4, 0, 0.2, 1) forwards; 14844 } 14845 .bp-saved-drawer__dialog::backdrop { background: rgb(0 0 0 / 0.3); } 14846 @media (max-width: 749px) { 14847 .bp-saved-drawer__dialog { width: 100vw; min-width: 0; } 14848 } 14849 14850 .bp-saved-drawer__header { 14851 flex: 0 0 auto; 14852 display: flex; 14853 align-items: center; 14854 justify-content: space-between; 14855 padding: 20px 24px; 14856 border-bottom: 1px solid rgba(23, 22, 9, 0.08); 14857 background: var(--bp-color-bg-page); 14858 } 14859 .bp-saved-drawer__heading { 14860 margin: 0; 14861 display: inline-flex; 14862 align-items: center; 14863 gap: 12px; 14864 color: var(--bp-color-text); 14865 font-family: var(--bp-font-display); 14866 font-size: 28px; 14867 font-weight: 300; 14868 line-height: 120%; 14869 letter-spacing: -0.56px; 14870 } 14871 .bp-saved-drawer__bubble { 14872 display: inline-flex; 14873 align-items: center; 14874 justify-content: center; 14875 width: 32px; 14876 height: 32px; 14877 border-radius: 50%; 14878 background: #EBAE55; 14879 /* Dark ink on the gold badge â #fff on #EBAE55 is only 1.96:1 (WCAG fail). 14880 #171609 clears AA at 9.3:1. Same fix already applied to the cart count 14881 badge (QA 7/27, contrast audit â last of the gold-badge group). */ 14882 color: var(--bp-color-text, #171609); 14883 font-family: var(--bp-font-body); 14884 font-size: 14px; 14885 font-weight: 500; 14886 } 14887 .bp-saved-drawer__close { 14888 background: transparent; 14889 border: 0; 14890 padding: 0; 14891 width: 32px; 14892 height: 32px; 14893 cursor: pointer; 14894 color: var(--bp-color-text); 14895 display: inline-flex; 14896 align-items: center; 14897 justify-content: center; 14898 } 14899 .bp-saved-drawer__close:hover { opacity: 0.6; } 14900 14901 .bp-saved-drawer__body { 14902 flex: 1 1 auto; 14903 overflow-y: auto; 14904 padding: 0 24px 24px; 14905 } 14906 .bp-saved-drawer__empty { 14907 padding: 48px 0; 14908 text-align: center; 14909 color: var(--bp-color-body-copy); 14910 } 14911 .bp-saved-drawer__loading { 14912 padding: 48px 0; 14913 text-align: center; 14914 color: var(--bp-color-body-copy); 14915 display: flex; 14916 flex-direction: column; 14917 align-items: center; 14918 gap: 12px; 14919 } 14920 .bp-saved-drawer__loading[hidden] { display: none; } 14921 .bp-saved-drawer__spinner { 14922 width: 28px; 14923 height: 28px; 14924 border: 2px solid rgba(23, 22, 9, 0.18); 14925 border-top-color: var(--bp-color-text, #171609); 14926 border-radius: 50%; 14927 animation: bp-saved-spin 0.7s linear infinite; 14928 } 14929 @keyframes bp-saved-spin { to { transform: rotate(360deg); } } 14930 @media (prefers-reduced-motion: reduce) { 14931 .bp-saved-drawer__spinner { animation-duration: 1.6s; } 14932 } 14933 .bp-saved-drawer__empty-help { 14934 margin-top: 4px; 14935 color: var(--bp-color-body-copy); 14936 font-size: 13px; 14937 } 14938 14939 .bp-saved-drawer__list { 14940 list-style: none; 14941 margin: 0; 14942 padding: 0; 14943 display: flex; 14944 flex-direction: column; 14945 } 14946 14947 .bp-saved-drawer__card { 14948 display: grid; 14949 grid-template-columns: 110px 1fr; 14950 gap: 16px; 14951 padding: 20px 0; 14952 border-bottom: 1px solid rgba(23, 22, 9, 0.08); 14953 } 14954 .bp-saved-drawer__card:last-child { border-bottom: 0; } 14955 14956 .bp-saved-drawer__image { 14957 display: block; 14958 width: 110px; 14959 aspect-ratio: 4 / 5; 14960 background: var(--bp-color-bg-cream); 14961 overflow: hidden; 14962 } 14963 .bp-saved-drawer__image img { 14964 width: 100%; 14965 height: 100%; 14966 object-fit: cover; 14967 display: block; 14968 } 14969 .bp-saved-drawer__details { 14970 display: flex; 14971 flex-direction: column; 14972 gap: 6px; 14973 min-width: 0; 14974 } 14975 .bp-saved-drawer__title-row { 14976 display: flex; 14977 justify-content: space-between; 14978 gap: 12px; 14979 align-items: baseline; 14980 } 14981 .bp-saved-drawer__name { 14982 margin: 0; 14983 font-family: var(--bp-font-body); 14984 font-size: 14px; 14985 font-weight: 700; 14986 line-height: 145%; 14987 letter-spacing: -0.14px; 14988 color: var(--bp-color-text); 14989 } 14990 .bp-saved-drawer__name a { color: inherit; text-decoration: none; } 14991 .bp-saved-drawer__price { 14992 flex: 0 0 auto; 14993 font-size: 14px; 14994 font-weight: 700; 14995 color: var(--bp-color-text); 14996 } 14997 .bp-saved-drawer__variant { 14998 margin: 0; 14999 color: var(--bp-color-body-copy); 15000 font-size: 13px; 15001 } 15002 .bp-saved-drawer__props { 15003 list-style: none; 15004 margin: 4px 0 0; 15005 padding: 0; 15006 display: flex; 15007 flex-wrap: wrap; 15008 gap: 6px; 15009 } 15010 .bp-saved-drawer__props li { 15011 background: var(--bp-color-bg-cream); 15012 padding: 2px 8px; 15013 font-size: 11px; 15014 font-weight: 700; 15015 letter-spacing: 0.04em; 15016 text-transform: uppercase; 15017 color: var(--bp-color-text); 15018 } 15019 .bp-saved-drawer__actions { 15020 margin-top: 10px; 15021 display: flex; 15022 gap: 8px; 15023 flex-wrap: wrap; 15024 } 15025 .bp-saved-drawer__btn { 15026 height: 40px; 15027 padding: 0 16px; 15028 border: 1px solid var(--bp-color-text); 15029 cursor: pointer; 15030 font-family: var(--bp-font-body); 15031 font-size: 13px; 15032 font-weight: 500; 15033 letter-spacing: -0.13px; 15034 transition: opacity 0.2s ease, background 0.2s ease; 15035 } 15036 .bp-saved-drawer__btn--primary { 15037 background: var(--bp-color-text); 15038 color: var(--bp-color-cta-text); 15039 } 15040 .bp-saved-drawer__btn--primary:hover { opacity: 0.85; } 15041 .bp-saved-drawer__btn--primary:disabled { opacity: 0.4; cursor: wait; } 15042 /* Sold-out Move button: stays disabled + relabelled, no hover lift. */ 15043 .bp-saved-drawer__btn--soldout, 15044 .bp-saved-drawer__btn--primary.bp-saved-drawer__btn--soldout:disabled { 15045 opacity: 0.4;
15046 cursor: not-allowed; 15047 } 15048 .bp-saved-drawer__btn--secondary { 15049 background: var(--bp-color-bg-page); 15050 color: var(--bp-color-text); 15051 } 15052 .bp-saved-drawer__btn--secondary:hover { background: var(--bp-color-bg-cream); } 15053 /* Sold-out marker â mirrors the cart line's unavailable treatment 15054 (snippets/bp-cart-line-item.liquid), BP promo red. */ 15055 .bp-saved-drawer__unavailable { 15056 margin: 4px 0 0; 15057 font-family: var(--bp-font-body); 15058 font-size: 12px; 15059 font-weight: 700; 15060 letter-spacing: 0.04em; 15061 text-transform: uppercase; 15062 color: var(--bp-color-promo, #B5341B); 15063 } 15064 .bp-saved-drawer__card.is-unavailable .bp-saved-drawer__image, 15065 .bp-saved-drawer__card.is-unavailable .bp-saved-drawer__name { 15066 opacity: 0.55; 15067 } 15068</style> 15069
15070<script type="module"> 15071 if (!customElements.get('bp-saved-drawer')) { 15072 class BpSavedDrawer extends HTMLElement { 15073 connectedCallback() { 15074 this.dialog = this.querySelector('[data-bp-saved-dialog]'); 15075 this.list = this.querySelector('[data-bp-saved-list]'); 15076 this.empty = this.querySelector('[data-bp-saved-empty]'); 15077 this.loading = this.querySelector('[data-bp-saved-loading]'); 15078 this.template = this.querySelector('[data-bp-saved-template]'); 15079 this.bubble = this.querySelector('[data-bp-saved-bubble]'); 15080 this.triggerCount = this.querySelector('[data-bp-saved-trigger-count]'); 15081 15082 // Local trigger inside this component. 15083 const localOpener = this.querySelector('[data-bp-saved-open]'); 15084 if (localOpener) localOpener.addEventListener('click', (e) => { e.preventDefault(); this.open(); }); 15085 15086 // Delegated open for any element anywhere with data-bp-saved-open. 15087 // Using `once: false` lets cart-drawer footer/empty links open us too. 15088 if (!window.__bpSavedOpenWired) { 15089 window.__bpSavedOpenWired = true; 15090 document.addEventListener('click', (e) => { 15091 const trigger = e.target.closest('[data-bp-saved-open]'); 15092 if (!trigger || this.contains(trigger)) return; 15093 e.preventDefault(); 15094 const el = document.querySelector('bp-saved-drawer'); 15095 if (el && typeof el.open === 'function') el.open(); 15096 }); 15097 } 15098 15099 this.dialog.addEventListener('click', (e) => { 15100 if (e.target.closest('[data-bp-saved-close]')) { 15101 e.preventDefault(); 15102 this.close(); 15103 return; 15104 } 15105 if (e.target === this.dialog) this.close(); 15106 }); 15107 15108 this.dialog.addEventListener('cancel', (e) => { 15109 e.preventDefault(); 15110 this.close(); 15111 }); 15112 15113 // Card actions (Move to Bag / Remove). 15114 this.list.addEventListener('click', async (e) => { 15115 const card = e.target.closest('[data-bp-saved-card]'); 15116 if (!card) return; 15117 const key = card.dataset.savedKey; 15118 15119 if (e.target.closest('[data-bp-card-remove]')) { 15120 this.removeSaved(key); 15121 this.render(); 15122 return; 15123 } 15124 if (e.target.closest('[data-bp-card-move]')) { 15125 const moveBtn = e.target.closest('[data-bp-card-move]'); 15126 moveBtn.disabled = true; 15127 moveBtn.textContent = 'Addingâ¦'; 15128 try { 15129 await this.moveToBag(key); 15130 } catch (err) { 15131 // The item is still saved â handleMoveToBag only drops the 15132 // save after /cart/add.js succeeds. Leave the card in the 15133 // list and say why, instead of dropping the shopper back to 15134 // an unchanged button with no explanation. (This branch was 15135 // unreachable until handleMoveToBag stopped swallowing add 15136 // failures: it removed the save first, warned to the console, 15137 // and returned as if it had worked â so the item disappeared 15138 // from Saved for Later without ever reaching the cart.) 15139 console.error('[bp-saved-drawer] move-to-bag failed', err); 15140 if (err && err.status === 422) { 15141 // Genuinely unsellable (sold out). Stamp it persistently 15142 // instead of flickering the label back to "Move to Bag" 15143 // after 2.6s (Dragana QA 7/22) â the item stays saved and 15144 // reads Sold out / Unavailable until stock returns. 15145 this.markUnavailable(card); 15146 if (err.description) moveBtn.title = err.description; 15147 } else { 15148 // Transient failure (network, etc.) â let them retry. 15149 moveBtn.disabled = false; 15150 moveBtn.textContent = 'Try again'; 15151 moveBtn.title = (err && err.description) || 'Could not add to bag'; 15152 setTimeout(() => { 15153 moveBtn.textContent = 'Move to Bag'; 15154 moveBtn.removeAttribute('title'); 15155 }, 2600); 15156 } 15157 return; 15158 } 15159 this.removeSaved(key); 15160 this.render(); 15161 // Close saved drawer so the cart drawer becomes the active dialog, 15162 // then refresh + open the cart drawer. 15163 this.close(); 15164 document.dispatchEvent(new CustomEvent('cart:updated')); 15165 const cartDrawer = document.querySelector('bp-cart-drawer'); 15166 if (cartDrawer && typeof cartDrawer.open === 'function') { 15167 setTimeout(() => cartDrawer.open(), 220); 15168 } 15169 } 15170 }); 15171 15172 // Cross-tab + same-tab sync. We listen on the legacy 15173 // `bp.saved_for_later` key AND the Swym-mirror key 15174 // `save-for-later-items` (bp-swym-helpers). 15175 window.addEventListener('storage', (e) => { 15176 if (e.key === 'bp.saved_for_later' || e.key === 'save-for-later-items') this.render(); 15177 });
15178 document.addEventListener('bp:saved:updated', () => this.render()); 15179 document.addEventListener('wishlist:updated', () => this.render()); 15180 document.addEventListener('wishlist:item-saved', () => this.render()); 15181 // Swym finished its reconcile â leave the loading state and render. 15182 document.addEventListener('bp:saved:synced', () => this.render()); 15183 15184 // Migrate any pre-existing local-only entries (legacy 15185 // `bp.saved_for_later`) into the Swym-compatible mirror so 15186 // the customer doesn't lose what they already saved. 15187 this.migrateLegacy(); 15188 15189 this.render(); 15190 } 15191 15192 // ----- Storage adapter â prefer Swym-backed mirror ----- 15193 // Returns the list of saved items either from the Swym mirror 15194 // (bp-swym-helpers) when available, falling back to the legacy 15195 // single-array `bp.saved_for_later` key for back-compat. 15196 readItems() { 15197 if (window.bpSavedForLater && typeof window.bpSavedForLater.getLocalWishlist === 'function') { 15198 const w = window.bpSavedForLater.getLocalWishlist(); 15199 return Array.isArray(w && w.items) ? w.items : []; 15200 } 15201 try { return JSON.parse(localStorage.getItem('bp.saved_for_later') || '[]'); } 15202 catch (_) { return []; } 15203 } 15204 migrateLegacy() { 15205 if (!window.bpSavedForLater) return; 15206 let legacy = []; 15207 try { legacy = JSON.parse(localStorage.getItem('bp.saved_for_later') || '[]'); } 15208 catch (_) { legacy = []; } 15209 if (!Array.isArray(legacy) || !legacy.length) return; 15210 const customerId = window.bpSavedForLater.getCustomerId(); 15211 const all = window.bpSavedForLater.getSavedItemsLocalStorage() || {}; 15212 const current = all[customerId] || { items: [], count: 0 }; 15213 // De-dupe by variant_id. 15214 const have = new Set(current.items.map((it) => String(it.variant_id))); 15215 const adapted = legacy 15216 .filter((it) => !have.has(String(it.variantId || it.variant_id))) 15217 .map((it) => ({ 15218 id: String(it.productId || it.product_id || it.variantId), 15219 handle: it.handle || '', 15220 variant_id: String(it.variantId || it.variant_id), 15221 variant_price: String(it.price || it.variant_price || ''), 15222 variant_compare_at_price: String(it.compare_at_price || it.variant_compare_at_price || ''), 15223 variant_title: String(it.variantTitle || it.variant_title || ''), 15224 title: it.title || '', 15225 image: it.image || '', 15226 quantity: String(it.quantity || 1), 15227 properties: it.properties || {}, 15228 variant_sku: it.sku || it.variant_sku || '' 15229 })); 15230 if (!adapted.length) return; 15231 const merged = current.items.concat(adapted); 15232 all[customerId] = { ...current, items: merged, count: merged.length }; 15233 window.bpSavedForLater.storeSavedItemsLocalStorage(all); 15234 // Wipe the legacy bucket so the migration is one-shot. 15235 try { localStorage.removeItem('bp.saved_for_later'); } catch (_) {} 15236 } 15237 15238 open() { 15239 this.render(); 15240 if (this.dialog.open) return; 15241 if (typeof this.dialog.showModal === 'function') this.dialog.showModal(); 15242 else this.dialog.setAttribute('open', ''); 15243 } 15244 close() { 15245 if (this._closing || !this.dialog.open) return; 15246 this._closing = true; 15247 this.dialog.classList.add('is-closing'); 15248 const onEnd = () => { 15249 this.dialog.classList.remove('is-closing'); 15250 this._closing = false; 15251 try { this.dialog.close(); } catch (_) { this.dialog.removeAttribute('open'); } 15252 }; 15253 this.dialog.addEventListener('animationend', onEnd, { once: true }); 15254 setTimeout(() => { if (this._closing) onEnd(); }, 350); 15255 } 15256 15257 getSaved() { return this.readItems(); } 15258 removeSaved(key) { 15259 // `key` for Swym-mirror items is variant_id; legacy items used 15260 // a `key` field. We resolve by either. 15261 const items = this.readItems(); 15262 const target = items.find((i) => String(i.variant_id) === String(key) || String(i.key) === String(key)); 15263 if (!target) return; 15264 if (window.bpSavedForLater && window.bpSavedForLater.removeWishlistItem) { 15265 window.bpSavedForLater.removeWishlistItem(target.id, target.variant_id); 15266 } 15267 } 15268 15269 async moveToBag(key) { 15270 const items = this.readItems(); 15271 const item = items.find((i) => String(i.variant_id) === String(key) || String(i.key) === String(key)); 15272 if (!item) return; 15273 const productId = item.id; 15274 const variantId = item.variant_id || item.variantId;
15275 const qty = parseInt(item.quantity, 10) || 1; 15276 const isWishlistItemProductLevel = String(productId) === String(variantId); 15277 if (typeof window.handleMoveToBag === 'function') { 15278 await window.handleMoveToBag(productId, variantId, qty, isWishlistItemProductLevel, item, null); 15279 return; 15280 } 15281 // Last-resort fallback if Swym helpers haven't loaded yet. 15282 const body = { items: [{ id: variantId, quantity: qty, properties: item.properties || {} }] }; 15283 const res = await fetch('/cart/add.js', { 15284 method: 'POST', 15285 headers: { 'Content-Type': 'application/json', Accept: 'application/json' }, 15286 body: JSON.stringify(body) 15287 }); 15288 if (!res.ok) throw new Error('HTTP ' + res.status); 15289 } 15290 15291 formatMoney(cents) { return '$' + (cents / 100).toFixed(2); } 15292 // Atlas's variant_price is a currency-formatted string ("$25.00" or 15293 // "2500" cents string). Normalise to cents for arithmetic. 15294 parsePriceCents(v) { 15295 if (v == null) return 0; 15296 const s = String(v).replace(/[^0-9.-]/g, ''); 15297 if (!s) return 0; 15298 if (s.indexOf('.') >= 0) return Math.round(parseFloat(s) * 100); 15299 return parseInt(s, 10) || 0; 15300 } 15301 15302 // Saved entries store no availability (only id/handle/price/title/â¦), 15303 // so a sold-out save looked identical to an in-stock one and the only 15304 // "unavailable" hint was the transient 422 flicker on Move to Bag. 15305 // Resolve availability from /products/<handle>.js â cached per handle 15306 // (one fetch, reused across re-renders) â so each card can carry a 15307 // stable sold-out marker up front (Dragana QA 7/22). 15308 fetchProductJson(handle) { 15309 if (!handle) return Promise.resolve(null); 15310 if (!this._prodCache) this._prodCache = new Map(); 15311 if (this._prodCache.has(handle)) return this._prodCache.get(handle); 15312 const p = fetch(`/products/${handle}.js`, { credentials: 'same-origin' }) 15313 .then((r) => (r.ok ? r.json() : null)) 15314 .catch(() => null); 15315 this._prodCache.set(handle, p); 15316 return p; 15317 } 15318 15319 // Resolves to false only when we can confirm sold-out. Unknown (fetch 15320 // failed / no handle) resolves true so we never falsely block a save â 15321 // the 422 path still catches a genuine sold-out at add time. 15322 isSavedItemAvailable(item) { 15323 const variantId = item.variant_id || item.variantId; 15324 return this.fetchProductJson(item.handle).then((product) => { 15325 if (!product) return true; 15326 const variants = product.variants || []; 15327 if (variantId) { 15328 const v = variants.find((vv) => String(vv.id) === String(variantId)); 15329 // Exact saved variant known â trust its own availability. 15330 if (v) return !!v.available; 15331 } 15332 // Product-level save, or the saved variant was retired: sellable 15333 // if ANY variant is still in stock. 15334 return variants.some((vv) => vv.available); 15335 }); 15336 } 15337 15338 // Stamp a card as sold out: red marker + a disabled, relabelled Move 15339 // button. Idempotent, and safe to call from both render() (proactive) 15340 // and the 422 catch (reactive) â see the Move to Bag handler. 15341 markUnavailable(node) { 15342 if (!node || node.classList.contains('is-unavailable')) return; 15343 node.classList.add('is-unavailable'); 15344 const moveBtn = node.querySelector('[data-bp-card-move]'); 15345 if (moveBtn) { 15346 moveBtn.disabled = true; 15347 moveBtn.textContent = 'Unavailable'; 15348 moveBtn.classList.add('bp-saved-drawer__btn--soldout'); 15349 } 15350 const details = node.querySelector('.bp-saved-drawer__details'); 15351 if (details && !details.querySelector('[data-bp-card-unavailable]')) { 15352 const marker = document.createElement('p'); 15353 marker.className = 'bp-saved-drawer__unavailable'; 15354 marker.setAttribute('data-bp-card-unavailable', ''); 15355 marker.textContent = 'Sold out'; 15356 const actions = details.querySelector('.bp-saved-drawer__actions'); 15357 if (actions) details.insertBefore(marker, actions); 15358 else details.appendChild(marker); 15359 } 15360 } 15361 15362 // Ready to render for real: Swym has reconciled the wishlist, OR we've 15363 // fallen back to localStorage after a timeout (Swym slow / errored / not 15364 // installed). Until then we show a LOADING state everywhere instead of 15365 // rendering localStorage first and then flashing when Swym swaps it in â 15366 // the single-source approach that killed the appear/vanish/reappear flash. 15367 _ready() { 15368 if (this._localFallback) return true; 15369 return !!(window.bpSavedForLater 15370 && typeof window.bpSavedForLater.isSynced === 'function' 15371 && window.bpSavedForLater.isSynced()); 15372 } 15373 15374 // Kick a sync (in case the drawer mounted before the helper's load-time 15375 // sync ran) and arm a fallback so we're never stuck on the spinner. 15376 _scheduleReady() { 15377 if (this._readyWaiting) return; 15378 this._readyWaiting = true; 15379 if (window.bpSavedForLater && typeof window.bpSavedForLater.checkWishlistSync === 'function') { 15380 try { window.bpSavedForLater.checkWishlistSync(); } catch (_) {} 15381 } 15382 setTimeout(() => { 15383 if (!this._ready()) { this._localFallback = true; this.render(); } 15384 }, 5000); 15385 } 15386 15387 render() { 15388 // Loading state â shown until Swym is synced (or we time out to local). 15389 // Covers the drawer body AND the topnav count so nothing shows a 15390 // premature/for-a-moment-wrong value. 15391 if (!this._ready()) { 15392 if (this.loading) this.loading.hidden = false;
15393 if (this.list) this.list.hidden = true; 15394 if (this.empty) this.empty.hidden = true; 15395 if (this.triggerCount) { 15396 this.triggerCount.hidden = false; 15397 this.triggerCount.classList.add('is-loading'); 15398 this.triggerCount.textContent = ''; 15399 } 15400 this._scheduleReady(); 15401 return; 15402 } 15403 if (this.loading) this.loading.hidden = true; 15404 if (this.triggerCount) this.triggerCount.classList.remove('is-loading'); 15405 15406 const items = this.getSaved(); 15407 15408 // Trigger + header bubble. 15409 if (this.bubble) this.bubble.textContent = items.length; 15410 if (this.triggerCount) { 15411 this.triggerCount.textContent = items.length; 15412 this.triggerCount.toggleAttribute('hidden', items.length === 0); 15413 } 15414 15415 if (!items.length) { 15416 this._renderSig = ''; 15417 this.list.innerHTML = ''; 15418 this.list.hidden = true; 15419 this.empty.hidden = false; 15420 return; 15421 } 15422 this.empty.hidden = true; 15423 this.list.hidden = false; 15424 15425 // Newest-first. Sort by the Swym-proof order map (variant_id â 15426 // timestamp, stored under its own key that Swym's sync never rewrites), 15427 // falling back to the per-item savedAt, then 0. This is what keeps a 15428 // fresh save on top both in-session and after refresh â the per-item 15429 // savedAt alone kept getting dropped/reordered by Swym's async sync, so 15430 // the item slid to the bottom. Items with no timestamp keep their 15431 // relative order below the ranked ones (stable sort). 15432 const order = (window.bpSavedForLater && typeof window.bpSavedForLater.getSavedOrder === 'function') 15433 ? window.bpSavedForLater.getSavedOrder() 15434 : {}; 15435 const rank = (it) => Number(order[String(it.variant_id)]) || Number(it.savedAt) || 0; 15436 const ordered = items.slice().sort((a, b) => rank(b) - rank(a)); 15437 15438 // Skip the rebuild when the visible list is unchanged. render() fires 15439 // several times right after a reload-then-open (open + Swym's 15440 // checkWishlistSync swapping in the remote item objects + storage / 15441 // wishlist events); each innerHTML reset flashed a blank frame between 15442 // paints, so the items appeared, vanished, then reappeared. Key the 15443 // signature on the ordered variant ids only, String-normalized so 15444 // Swym's reformatted objects (numeric vs string ids) don't falsely 15445 // differ from the localStorage ones. 15446 // 15447 // CRUCIAL: only skip when the DOM ACTUALLY reflects that signature 15448 // (childElementCount matches). Otherwise a render that set the signature 15449 // but left the list empty (e.g. an interrupted build) would skip forever 15450 // and the drawer would never show items. 15451 const sig = ordered.map((it) => String(it.variant_id || it.variantId || '')).join('|'); 15452 if (sig === this._renderSig && this.list.childElementCount === ordered.length) return; 15453 this._renderSig = sig; 15454 15455 this.list.innerHTML = ''; 15456 ordered.forEach((item) => { 15457 // Each entry stored by bp-swym-helpers uses Atlas's shape 15458 // (variant_id, variant_title, variant_price, title, image, 15459 // handle, quantity). Legacy entries used variantId/title/price/ 15460 // variantTitle/url â read either form. 15461 const variantId = item.variant_id || item.variantId; 15462 const productId = item.id || item.productId; 15463 const variantTitle = item.variant_title || item.variantTitle || ''; 15464 const url = item.handle ? `/products/${item.handle}` : (item.url || '#'); 15465 const priceCents = this.parsePriceCents(item.variant_price || item.price); 15466 const qty = parseInt(item.quantity, 10) || 1; 15467 15468 const node = this.template.content.firstElementChild.cloneNode(true); 15469 node.dataset.savedKey = variantId || item.key; 15470 15471 const img = node.querySelector('[data-bp-card-image]'); 15472 if (item.image) { 15473 img.src = item.image; 15474 img.alt = item.title || ''; 15475 } else { 15476 img.remove(); 15477 } 15478 node.querySelectorAll('[data-bp-card-link]').forEach((a) => { 15479 if (url) a.href = url; 15480 if (a.tagName === 'A' && a.children.length === 0) a.textContent = item.title || 'Item'; 15481 }); 15482 15483 node.querySelector('[data-bp-card-price]').textContent = 15484 this.formatMoney(priceCents * qty); 15485 15486 const variantEl = node.querySelector('[data-bp-card-variant]'); 15487 if (variantTitle && variantTitle !== 'Default Title') { 15488 variantEl.textContent = variantTitle.replace(' / ', ' - '); 15489 } else { 15490 variantEl.remove(); 15491 } 15492 15493 const propsEl = node.querySelector('[data-bp-card-props]'); 15494 const props = item.properties || {}; 15495 const visible = Object.entries(props).filter(([k, v]) => k && k[0] !== '_' && v); 15496 if (!visible.length) { 15497 propsEl.remove(); 15498 } else { 15499 visible.forEach(([k, v]) => { 15500 const li = document.createElement('li'); 15501 li.textContent = (k === 'Final Sale' || k === 'Final_Sale') ? 'Final Sale' : `${k}: ${v}`; 15502 propsEl.appendChild(li); 15503 }); 15504 } 15505 15506 this.list.appendChild(node); 15507 15508 // Resolve availability async and stamp sold-out state once known. 15509 // Guard on isConnected so a re-render mid-fetch doesn't touch a
15510 // detached node. 15511 this.isSavedItemAvailable(item).then((available) => { 15512 if (!available && node.isConnected) this.markUnavailable(node); 15513 }); 15514 }); 15515 } 15516 } 15517 customElements.define('bp-saved-drawer', BpSavedDrawer); 15518 } 15519</script>
15519 15520 15521 15522 15523 15524 <div 15525 class="account-button header-actions__action color-scheme-1" 15526 > 15527 <shopify-store store-domain="https://www.bostonproper.com" country="US" language="en"></shopify-store> 15528<shopify-account 15529 menu="customer-account-main-menu" 15530 15531 > 15532 15533 <span 15534 slot="signed-out-avatar" 15535 class="account-button__text header-actions__text-style hidden" 15536 > 15537 Account 15538 </span> 15539 15540 <span 15541 slot="signed-out-avatar" 15542 class="" 15543 aria-hidden="true" 15544 > 15545 15546 <span 15547 slot="signed-out-avatar" 15548 class="account-button__icon" 15549 aria-hidden="true" 15550 ><svg width="44" height="44" viewBox="0 0 44 44" fill="none" xmlns="http://www.w3.org/2000/svg"> 15551<path d="M16.2227 15.2379C16.2134 12.9677 17.7898 11.0652 19.936 10.3449C23.4796 9.10175 27.7591 11.331 27.7783 15.2379C27.7587 19.1448 23.4796 21.374 19.936 20.1309C17.7903 19.4105 16.2134 17.5081 16.2227 15.2379ZM18.0005 15.2379C17.992 16.8309 18.828 18.4907 20.3231 19.2146C21.3583 19.7286 22.6431 19.7286 23.6783 19.2146C25.1729 18.4907 26.0094 16.8309 26.0009 15.2379C26.0094 13.6448 25.1734 11.985 23.6783 11.2612C22.6431 10.7472 21.3583 10.7472 20.3231 11.2612C18.8285 11.985 17.992 13.6448 18.0005 15.2379Z" fill="currentColor"/> 15552<path d="M12 34C12.28 29.7933 13.4618 25.0731 17.7702 23.2244C19.1058 22.6506 20.5636 22.4433 22 22.4372C24.3076 22.4241 26.7129 23.0455 28.4364 24.6324C29.9582 26.0034 30.8698 27.9263 31.3693 29.8649C31.6809 31.0482 31.8529 32.2665 31.9596 33.4808C31.972 33.6536 31.9853 33.8268 32 34H29.7778C29.7956 33.733 29.8182 33.3246 29.824 33.051C29.8591 30.5425 29.4138 27.8399 27.6716 25.9069C26.6804 24.8318 25.348 24.1738 23.8938 23.9146C22.0573 23.6027 20.0453 23.6973 18.336 24.4605C17.4333 24.8698 16.6467 25.4863 16.0364 26.2503L15.9004 26.431C15.784 26.5771 15.6858 26.7421 15.5796 26.8965C15.5347 26.9698 15.4516 27.1186 15.408 27.191C15.3351 27.308 15.2636 27.4738 15.1973 27.5951C14.3849 29.2863 14.1489 31.1922 14.176 33.0514C14.1871 33.3176 14.1987 33.7352 14.2222 34.0004H12V34Z" fill="currentColor"/> 15553</svg> 15554</span> 15555 15556 </span> 15557 15558 </shopify-account> 15559 </div> 15560 15561 15562 15563<bp-cart-drawer class="bp-cart-drawer"><button 15564 type="button" 15565 class="bp-cart-drawer__trigger button-unstyled header-actions__action" 15566 data-bp-cart-open 15567 aria-haspopup="dialog" 15568 aria-label="Cart" 15569 ><svg width="44" height="44" viewBox="0 0 44 44" fill="none" xmlns="http://www.w3.org/2000/svg"> 15570<path d="M12.0056 15.7691H31.6937H31.8451L31.9148 15.771C32.278 15.7797 32.6482 15.9823 32.8268 16.3059C32.938 16.4983 32.9759 16.6612 33.0022 16.8634C33.4498 21.2612 33.8731 25.7394 34.5178 30.1086C34.6327 30.9237 34.7435 31.7392 34.8506 32.5552C34.8547 32.5903 34.8598 32.6609 34.8616 32.6964C34.8889 33.1127 34.6941 33.6024 34.2718 33.8294C33.9755 33.9993 33.6815 34.0011 33.3686 33.9997C31.157 33.9997 12.811 33.9997 10.6515 33.9997C10.4346 33.9951 10.1328 34.0168 9.89368 33.9513C9.23232 33.7847 8.91618 33.1713 9.0191 32.515C9.16909 31.3709 9.44831 29.3324 9.60846 28.1675C10.0368 25.012 10.3617 21.7057 10.6824 18.5286C10.7083 18.3412 10.8153 17.1934 10.838 17.0203L10.8537 16.8694L10.8555 16.8505C10.8555 16.8458 10.8569 16.8362 10.8592 16.8237C10.9312 16.2154 11.3729 15.7626 12.0056 15.7691ZM12.0056 17.1537C12.0444 17.1537 12.0993 17.1274 12.1482 17.0914C12.187 17.0618 12.2142 17.0235 12.2253 17.0037C12.2373 16.9838 12.2378 16.9765 12.2364 16.9811C12.2355 16.9871 12.2336 16.9958 12.2322 17.0023C12.2322 17.0023 12.2313 17.0051 12.2308 17.0129L12.229 17.0318L12.2128 17.1823C11.9142 20.0285 11.5529 23.0635 11.299 25.9079C11.1246 28.0553 11.0036 30.5711 10.8541 32.7236L10.8518 32.7495V32.7569C10.8518 32.7569 10.8523 32.7587 10.8527 32.7495C10.8527 32.7426 10.8527 32.7232 10.8481 32.6973C10.8329 32.5889 10.7743 32.4698 10.6833 32.3655C10.5763 32.2524 10.4346 32.1643 10.3054 32.1509C10.298 32.1509 10.322 32.1527 10.3584 32.1532C12.7754 32.1541 30.8602 32.1532 33.3691 32.1532H33.508C33.5246 32.1523 33.5394 32.1518 33.5468 32.1513C33.5551 32.1509 33.5
15570583 32.1504 33.5514 32.1509C33.3538 32.1707 33.1171 32.3789 33.0515 32.5709C33.0068 32.6923 33.0132 32.7292 33.0137 32.7536C33.0137 32.7504 33.0137 32.7518 33.0137 32.7449L33.0114 32.719C32.8416 30.2979 32.7349 27.8656 32.5176 25.4505C32.2619 22.6989 31.9185 19.7359 31.6204 16.9815C31.631 17.0065 31.6647 17.0609 31.7136 17.0951C31.7528 17.1255 31.7967 17.1431 31.8216 17.1491L31.8373 17.1528H31.8437C31.8437 17.1528 31.846 17.1541 31.847 17.1541C31.8493 17.1541 31.852 17.1541 31.8511 17.1541C31.8502 17.1541 31.8544 17.1541 31.8451 17.1541C31.7639 17.1532 31.4796 17.1551 31.391 17.1541H12.0056V17.1537Z" fill="currentColor"/> 15571<path d="M15.0254 20.6119C15.1782 18.5997 15.2225 16.5187 15.8266 14.5651C16.734 11.5684 18.7877 9.98351 21.9326 10.0001C27.3328 10.0107 28.4539 14.3911 28.7234 18.9108C28.7622 19.4858 28.7931 20.06 28.8397 20.6115L26.9936 20.6027C27.0412 20.1015 27.1053 19.4092 27.1339 18.9181C27.3342 15.9118 27.0098 11.9371 23.5031 11.0995C22.4804 10.8627 21.3843 10.8627 20.3615 11.0995C17.4687 11.7885 16.7293 14.6421 16.6901 17.2733C16.6652 18.3611 16.7676 19.5126 16.8715 20.6027L15.0254 20.6115V20.6119Z" fill="currentColor"/> 15572</svg> 15573<span class="bp-cart-drawer__trigger-count" data-bp-cart-trigger-count hidden>0</span> 15574 </button> 15575 15576 <dialog class="bp-cart-drawer__dialog" data-bp-cart-dialog aria-label="Bag"> 15577 <header class="bp-cart-drawer__header"> 15578 <h2 class="bp-cart-drawer__heading"> 15579 <span class="bp-cart-drawer__bubble" data-bp-cart-bubble>0</span> 15580 Bag 15581 </h2> 15582 <button type="button" class="bp-cart-drawer__close" data-bp-cart-close aria-label="Close bag"> 15583 <svg width="24" height="24" viewBox="0 0 24 24" fill="none" aria-hidden="true"> 15584 <path d="M18 6C17.7882 6.18821 17.5764 6.37643 17.3646 6.56464C13.5525 9.95251 9.74038 13.3404 5.92825 16.7282C5.71646 16.9165 5.50468 17.1047 5.29289 17.2929C5.0433 17.5147 4.79371 17.7365 4.54412 17.9583C5.0433 18.4575 5.54248 18.9567 6.04167 19.4559C6.26348 19.2063 6.48529 18.9567 6.70711 18.7071C6.89532 18.4953 7.08354 18.2835 7.27175 18.0718C10.6596 14.2596 14.0475 10.4475 17.4354 6.63536C17.6236 6.42357 17.8118 6.21179 18 6Z" fill="currentColor"/> 15585 <path d="M18.7071 6.70711C18.9567 6.48529 19.2063 6.26348 19.4559 6.04167C18.9567 5.54248 18.4575 5.0433 17.9583 4.54412C17.7365 4.79371 17.5147 5.0433 17.2929 5.29289C17.1047 5.50468 16.9165 5.71646 16.7282 5.92825C13.3404 9.74038 9.95251 13.5525 6.56464 17.3646C6.37643 17.5764 6.18821 17.7882 6 18C6.21178 17.8118 6.42357 17.6236 6.63536 17.4354C10.4475 14.0475 14.2596 10.6596 18.0718 7.27175C18.2835 7.08354 18.4953 6.89532 18.7071 6.70711Z" fill="currentColor"/> 15586 <path d="M6 6C6.18821 6.21179 6.37643 6.42357 6.56464 6.63536C9.95251 10.4475 13.3404 14.2596 16.7282 18.0718C16.9165 18.2835 17.1047 18.4953 17.2929 18.7071C17.5147 18.9567 17.7365 19.2063 17.9583 19.4559C18.4575 18.9567 18.9567 18.4575 19.4559 17.9583C19.2063 17.7365 18.9567 17.5147 18.7071 17.2929C18.4953 17.1047 18.2835 16.9165 18.0718 16.7282C14.2596 13.3404 10.4475 9.95251 6.63536 6.56464C6.42357 6.37643 6.21179 6.18821 6 6Z" fill="currentColor"/> 15587 <path d="M6.70711 5.29289C6.48529 5.0433 6.26348 4.79371 6.04167 4.54412C5.54248 5.0433 5.0433 5.54248 4.54412 6.04167C4.79371 6.26348 5.0433 6.48529 5.29289 6.70711C5.50468 6.89532 5.71646 7.08354 5.92825 7.27175C9.74038 10.6596 13.5525 14.0475 17.3646 17.4354C17.5764 17.6236 17.7882 17.8118 18 18C17.8118 17.7882 17.6236 17.5764 17.4354 17.3646C14.0475 13.5525 10.6596 9.74038 7.27175 5.92825C7.08354 5.71646 6.89532 5.50468 6.70711 5.29289Z" fill="currentColor"/> 15588 </svg> 15589 </button> 15590 </header> 15591 15592 <div class="bp-cart-drawer__body" data-bp-cart-body><div class="bp-cart-drawer__empty" data-bp-cart-empty > 15593 <p>Your bag is empty.</p> 15594 <a href="/collections/all" class="bp-cart-drawer__empty-cta">Continue shopping</a> 15595 <button type="button" class="bp-cart-drawer__empty-cta bp-cart-drawer__empty-cta--btn" data-bp-saved-open>View saved items</button><div class="bp-cart-drawer__recs" data-bp-cart-recs><header class="bp-cart-drawer__recs-head"> 15596 <h3 class="bp-cart-drawer__recs-title">Recommended for you</h3> 15597 <div class="bp-cart-drawer__recs-arrows"> 15598 <button type="button" class="bp-cart-drawer__recs-arrow" data-bp-recs-
15598prev aria-label="Previous"><svg width="44" height="44" viewBox="0 0 44 44" fill="none" aria-hidden="true"> 15599 <path d="M18.1326 13.8674C18.0884 13.8244 18.0442 13.7814 18 13.7385C17.9128 13.8256 17.8256 13.9128 17.7385 14C17.7814 14.0442 17.8244 14.0884 17.8674 14.1326C18.1304 14.4029 18.3934 14.6733 18.6564 14.9436C21.0232 17.3768 23.3901 19.8099 25.7569 22.2431V21.7569C23.3238 24.1238 20.8906 26.4906 18.4575 28.8575C18.1871 29.1205 17.9168 29.3835 17.6464 29.6464C17.527 29.7627 17.4075 29.8789 17.288 29.9951C17.527 30.2341 17.7659 30.473 18.0049 30.712C18.1211 30.5925 18.2373 30.473 18.3536 30.3536C18.6165 30.0832 18.8795 29.8129 19.1425 29.5425C21.5094 27.1094 23.8762 24.6762 26.2431 22.2431L26.486 21.9933L26.2431 21.7569C23.8099 19.3901 21.3768 17.0232 18.9436 14.6564C18.6733 14.3934 18.4029 14.1304 18.1326 13.8674Z" fill="currentColor"/> 15600 <path d="M18.3536 13.6464C18.2373 13.527 18.1211 13.4075 18.0049 13.288C17.7659 13.527 17.527 13.7659 17.288 14.0049C17.4075 14.1211 17.527 14.2373 17.6464 14.3536C17.9168 14.6165 18.1871 14.8795 18.4575 15.1425C20.8906 17.5094 23.3238 19.8762 25.7569 22.2431V21.7569C23.3901 24.1901 21.0232 26.6232 18.6564 29.0564C18.3934 29.3267 18.1304 29.5971 17.8674 29.8674C17.8244 29.9116 17.7814 29.9558 17.7385 30C17.8256 30.0872 17.9128 30.1744 18 30.2615C18.0442 30.2186 18.0884 30.1756 18.1326 30.1326C18.4029 29.8696 18.6733 29.6066 18.9436 29.3436C21.3768 26.9768 23.8099 24.6099 26.2431 22.2431L26.486 21.9933L26.2431 21.7569C23.8762 19.3238 21.5094 16.8906 19.1425 14.4575C18.8795 14.1871 18.6165 13.9168 18.3536 13.6464Z" fill="currentColor"/> 15601 </svg> 15602 </button> 15603 <button type="button" class="bp-cart-drawer__recs-arrow" data-bp-recs-next aria-label="Next"> 15604 <svg width="44" height="44" viewBox="0 0 44 44" fill="none" aria-hidden="true"> 15605 <path d="M18.1326 13.8674C18.0884 13.8244 18.0442 13.7814 18 13.7385C17.9128 13.8256 17.8256 13.9128 17.7385 14C17.7814 14.0442 17.8244 14.0884 17.8674 14.1326C18.1304 14.4029 18.3934 14.6733 18.6564 14.9436C21.0232 17.3768 23.3901 19.8099 25.7569 22.2431V21.7569C23.3238 24.1238 20.8906 26.4906 18.4575 28.8575C18.1871 29.1205 17.9168 29.3835 17.6464 29.6464C17.527 29.7627 17.4075 29.8789 17.288 29.9951C17.527 30.2341 17.7659 30.473 18.0049 30.712C18.1211 30.5925 18.2373 30.473 18.3536 30.3536C18.6165 30.0832 18.8795 29.8129 19.1425 29.5425C21.5094 27.1094 23.8762 24.6762 26.2431 22.2431L26.486 21.9933L26.2431 21.7569C23.8099 19.3901 21.3768 17.0232 18.9436 14.6564C18.6733 14.3934 18.4029 14.1304 18.1326 13.8674Z" fill="currentColor"/> 15606 <path d="M18.3536 13.6464C18.2373 13.527 18.1211 13.4075 18.0049 13.288C17.7659 13.527 17.527 13.7659 17.288 14.0049C17.4075 14.1211 17.527 14.2373 17.6464 14.3536C17.9168 14.6165 18.1871 14.8795 18.4575 15.1425C20.8906 17.5094 23.3238 19.8762 25.7569 22.2431V21.7569C23.3901 24.1901 21.0232 26.6232 18.6564 29.0564C18.3934 29.3267 18.1304 29.5971 17.8674 29.8674C17.8244 29.9116 17.7814 29.9558 17.7385 30C17.8256 30.0872 17.9128 30.1744 18 30.2615C18.0442 30.2186 18.0884 30.1756 18.1326 30.1326C18.4029 29.8696 18.6733 29.6066 18.9436 29.3436C21.3768 26.9768 23.8099 24.6099 26.2431 22.2431L26.486 21.9933L26.2431 21.7569C23.8762 19.3238 21.5094 16.8906 19.1425 14.4575C18.8795 14.1871 18.6165 13.9168 18.3536 13.6464Z" fill="currentColor"/> 15607 </svg> 15608 </button> 15609 </div> 15610 </header> 15611 <ul class="bp-cart-drawer__recs-track" role="list"><li class="bp-cart-drawer__recs-card"><product-card class="bp-card-host"> 15612 <article 15613 class="bp-card" 15614 data-product-id="15399681392806" 15615 data-product-url="/products/leopard-print-skirt-brown-multi" 15616 data-bp-card-secondary-hover="true" 15617 data-bp-card-qv-dest="drawer" 15618 data-bp-card-qv-vis-d="hover" 15619 data-bp-card-qv-vis-m="shown" 15620 data-bp-card-qb-style="disabled" 15621 data-bp-track-product='{ "id": 15399681392806, "title": "Leopard Print Skirt Brown Multi", "vendor": "Bagatelle", "handle": "leopard-print-skirt-brown-multi", "url": "\/products\/leopard-print-skirt-brown-multi", "image": "\/\/www.bostonproper.com\/cdn\/shop\/files\/2DO3390_SET.jpg?v=1791009176\u0026width=720", "category": "Skirts", "product_type":null, "selected_variant": { "id": 56779671077030, "sku": "2DO3390800", "price": 110.0, "compare_at_price": 110.0, "option1": "Brown Multi", "option2": "L", "product_id": 15399681392806, "available": true, "inventory_quantity": 51 } }' 15622 > 15623 <div class="bp-card__media"> 15624 <a class="bp-card__media-link" href="/products/leopard-print-skirt-brown-multi" aria-label="Leopard Print Skirt Brown Multi" ref="productCardLink"><img 15625 class="bp-card__image bp-card__image--primary" 15626 src="//www.bostonproper.com/cdn/shop/files/2DO3390_SET.jpg?v=1791009176&
15626width=720" 15627 srcset="//www.bostonproper.com/cdn/shop/files/2DO3390_SET.jpg?v=1791009176&width=360 360w, //www.bostonproper.com/cdn/shop/files/2DO3390_SET.jpg?v=1791009176&width=540 540w, //www.bostonproper.com/cdn/shop/files/2DO3390_SET.jpg?v=1791009176&width=720 720w, //www.bostonproper.com/cdn/shop/files/2DO3390_SET.jpg?v=1791009176&width=1080 1080w" 15628 sizes="(min-width: 1024px) 25vw, 50vw" 15629 width="1639" 15630 height="2048" 15631 alt="Leopard Print Skirt Brown Multi" 15632 loading="lazy" 15633 data-bp-card-image-primary 15634 ><img 15635 class="bp-card__image bp-card__image--secondary" 15636 src="//www.bostonproper.com/cdn/shop/files/2_c2593330-685e-4f0d-affc-d3a27bbd70d9.jpg?v=1791009175&width=720" 15637 srcset="//www.bostonproper.com/cdn/shop/files/2_c2593330-685e-4f0d-affc-d3a27bbd70d9.jpg?v=1791009175&width=360 360w, //www.bostonproper.com/cdn/shop/files/2_c2593330-685e-4f0d-affc-d3a27bbd70d9.jpg?v=1791009175&width=540 540w, //www.bostonproper.com/cdn/shop/files/2_c2593330-685e-4f0d-affc-d3a27bbd70d9.jpg?v=1791009175&width=720 720w" 15638 sizes="(min-width: 1024px) 25vw, 50vw" 15639 width="1639" 15640 height="2048" 15641 alt="" 15642 loading="lazy" 15643 aria-hidden="true" 15644 data-bp-card-image-secondary 15645 ></a><div class="bp-card__badges" data-bp-card-badges></div></div> 15646 15647 <div class="bp-card__body"><h3 class="bp-card__title"> 15648 <a href="/products/leopard-print-skirt-brown-multi">Leopard Print Skirt</a> 15649 </h3> 15650 15651 <div class="bp-card__price-row"><span class="bp-card__price">$110</span> 15652 <s class="bp-card__compare" hidden></s></div></div> 15653 </article> 15654</product-card> 15655</li><li class="bp-cart-drawer__recs-card"><product-card class="bp-card-host"> 15656 <article 15657 class="bp-card" 15658 data-product-id="15399681360038" 15659 data-product-url="/products/metallic-zebra-printed-dress-tan-multi" 15660 data-bp-card-secondary-hover="true" 15661 data-bp-card-qv-dest="drawer" 15662 data-bp-card-qv-vis-d="hover" 15663 data-bp-card-qv-vis-m="shown" 15664 data-bp-card-qb-style="disabled" 15665 data-bp-track-product='{ "id": 15399681360038, "title": "Metallic Zebra Printed Dress Tan Multi", "vendor": "Bagatelle", "handle": "metallic-zebra-printed-dress-tan-multi", "url": "\/products\/metallic-zebra-printed-dress-tan-multi", "image": "\/\/www.bostonproper.com\/cdn\/shop\/files\/2DO0590_SET.jpg?v=1791009160\u0026width=720", "category": "Dresses", "product_type":null, "selected_variant": { "id": 56779670782118, "sku": "2DO0590006", "price": 180.0, "compare_at_price": 180.0, "option1": "Tan Multi", "option2": "6", "product_id": 15399681360038, "available": true, "inventory_quantity": 50 } }' 15666 > 15667 <div class="bp-card__media"> 15668 <a class="bp-card__media-link" href="/products/metallic-zebra-printed-dress-tan-multi" aria-label="Metallic Zebra Printed Dress Tan Multi" ref="productCardLink"><img 15669 class="bp-card__image bp-card__image--primary" 15670 src="//www.bostonproper.com/cdn/shop/files/2DO0590_SET.jpg?v=1791009160&width=720" 15671 srcset="//www.bostonproper.com/cdn/shop/files/2DO0590_SET.jpg?v=1791009160&width=360 360w, //www.bostonproper.com/cdn/shop/files/2DO0590_SET.jpg?v=1791009160&width=540 540w, //www.bostonproper.com/cdn/shop/files/2DO0590_SET.jpg?v=1791009160&width=720 720w, //www.bostonproper.com/cdn/shop/files/2DO0590_SET.jpg?v=1791009160&width=1080 1080w" 15672 sizes="(min-width: 1024px) 25vw, 50vw" 15673 width="1639" 15674 height="2048" 15675 alt="Metallic Zebra Printed Dress Tan Multi" 15676 loading="lazy" 15677 data-bp-card-image-primary 15678 ><img 15679 class="bp-card__image bp-card__image--secondary" 15680 src="//www.bostonproper.com/cdn/shop/files/2_0161cf9a-a917-4e26-98d6-d48db12f058a.jpg?v=1791009160&width=720" 15681 srcset="//www.bostonproper.com/cdn/shop/files/2_0161cf9a-a917-4e26-98d6-d48db12f058a.jpg?v=1791009160&width=360 360w, //www.bostonproper.com/cdn/shop/files/2_0161cf9a-a917-4e26-98d6-d48db12f058a.jpg?v=1791009160&width=540 540w, //www.bostonproper.com/cdn/shop/files/2_0161cf9a-a917-4e26-98d6-d48db12f058a.jpg?v=1791009160&width=720 720w" 15682 sizes="(min-width: 1024px) 25vw, 50vw" 15683 width="1639" 15684 height="2048" 15685 alt="" 15686 loading="lazy" 15687 aria-hidden="true" 15688 data-bp-card-image-secondary 15689 ></a><div class="bp-card__badges" data-bp-card-badges></div></div> 15690 15691 <div class="bp-card__body"><h3 class="bp-card__title"> 15692 <a href="/products/metallic-zebra-printed-dress-tan-multi">Metallic Zebra Printed Dress</a> 15693 </h3> 15694 15695 <div class="bp-card__price-row"><span class="bp-card__price">$180</span> 15696 <s class="bp-card__compare" hidden></s></div></div> 15697 </article> 15698</product-card> 15699</li><li class="bp-cart-drawer__recs-card"><product-card class="bp-card-host"> 15700 <article 15701 class="bp-card" 15702 data-product-id="15399680114854" 15703 data-product-url="/products/sam-edelman-fur-lace-up-bootie-tan" 15704 data-bp-card-secondary-hover="true" 15705 data-bp-card-qv-dest="drawer" 15706 data-bp-card-qv-vis-d="hover" 15707 data-bp-card-qv-vis-m="shown" 15708 data-bp-card-qb-style="disabled" 15709 data-bp-track-product='{ "id": 15399680114854, "title": "Sam Edelman Fur Lace Up Bootie Tan", "vendor": "Sam Edelman", "handle": "sam-edelman-fur-lace-up-bootie-tan", "url": "\/products\/sam-edelman-fur-lace-up-bootie-tan", "image": "\/\/www.bostonproper.com\/cdn\/shop\/files\/2DO5680_SET.jpg?v=1791009185\u0026width=720", "category": "Shoes", "product_type":null, "selected_variant": { "id": 56779666161830, "sku": "2DO5680060", "price": 180.0, "compare_at_price": 180.0, "option1": "Tan", "option2": "6.0", "product_id": 15399680114854, "available": true, "inventory_quantity": 13 } }' 15710 > 15711 <div class="bp-card__media"> 15712 <a class="bp-card__media-link" href="/products/sam-edelman-fur-lace-up-bootie-tan" aria-label="Sam Edelman Fur Lace Up Bootie Tan" ref="productCardLink"><img 15713 class="bp-card__image bp-card__image--primary" 15714 src="//www.bostonproper.com/cdn/shop/files/2DO5680_SET.jpg?v=1791009185&width=720" 15715 srcset="//www.bostonproper.com/cdn/shop/files/2DO5680_SET.jpg?v=1791009185&width=360 360w, //www.bostonproper.com/cdn/shop/files/2DO5680_SET.jpg?v=1791009185&width=540 540w, //www.bostonproper.com/cdn/shop/files/2DO5680_SET.jpg?v=1791009185&width=720 720w, //www.bostonproper.com/cdn/shop/files/2DO5680_SET.jpg?v=1791009185&width=1080 1080w" 15716 sizes="(min-width: 1024px) 25vw, 50vw" 15717 width="1639" 15718 height="2048" 15719 alt="Sam Edelman Fur Lace Up Bootie Tan" 15720 loading="lazy" 15721 data-bp-card-image-primary 15722 ><img 15723 class="bp-card__image bp-card__image--secondary" 15724 src="//www.bostonproper.com/cdn/shop/files/2_811cc34e-278e-4bea-a759-24085c37ff4f.jpg?v=1791009185&width=720" 15725 srcset="//www.bostonproper.com/cdn/shop/files/2_811cc34e-278e-4bea-a759-24085c37ff4f.jpg?v=1791009185&width=360 360w, //www.bostonproper.com/cdn/shop/files/2_811cc34e-278e-4bea-a759-24085c37ff4f.jpg?v=1791009185&width=540 540w, //www.bostonproper.com/cdn/shop/files/2_811cc34e-278e-4bea-a759-24085c37ff4f.jpg?v=1791009185&width=720 720w" 15726 sizes="(min-width: 1024px) 25vw, 50vw" 15727 width="1639" 15728 height="2048" 15729 alt="" 15730 loading="lazy" 15731 aria-hidden="true" 15732 data-bp-card-image-secondary 15733 ></a><div class="bp-card__badges" data-bp-card-badges></div></div> 15734 15735 <div class="bp-card__body"><h3 class="bp-card__title"> 15736 <a href="/products/sam-edelman-fur-lace-up-bootie-tan">Sam Edelman Fur Lace Up Bootie</a> 15737 </h3> 15738 15739 <div class="bp-card__price-row"><span class="bp-card__price">$180</span> 15740 <s class="bp-card__compare" hidden></s></div></div> 15741 </article> 15742</product-card> 15743</li><li class="bp-cart-drawer__recs-card"><product-card class="bp-card-host"> 15744 <article 15745 class="bp-card" 15746 data-product-id="15399680082086" 15747 data-product-url="/products/ultimate-velvet-double-breasted-shaped-blazer-black" 15748 data-bp-card-secondary-hover="true" 15749 data-bp-card-qv-dest="drawer" 15750 data-bp-card-qv-vis-d="hover" 15751 data-bp-card-qv-vis-m="shown" 15752 data-bp-card-qb-style="disabled" 15753 data-bp-track-product='{ "id": 15399680082086, "title": "Ultimate Velvet Double Breasted Shaped Blazer Black", "vendor": "Boston Proper", "handle": "ultimate-velvet-double-breasted-shaped-blazer-black", "url": "\/products\/ultimate-velvet-double-breasted-shaped-blazer-black", "image": "\/\/www.bostonproper.com\/cdn\/shop\/files\/2DP1920_SET.jpg?v=1791009211\u0026width=720", "category": "Jackets", "product_type":null, "selected_variant": { "id": 56779665801382, "sku": "2DP1920002", "price": 150.0, "compare_at_price": 150.0, "option1": "Black", "option2": "2", "product_id": 15399680082086, "available": true, "inventory_quantity": 21 } }' 15754 > 15755 <div class="bp-card__media"> 15756 <a class="bp-card__media-link" href="/products/ultimate-velvet-double-breasted-shaped-blazer-black" aria-label="Ultimate Velvet Double Breasted Shaped Blazer Black" ref="productCardLink"><img 15757 class="bp-card__image bp-card__image--primary" 15758 src="//www.bostonproper.com/cdn/shop/files/2DP1920_SET.jpg?v=1791009211&width=720" 15759 srcset="//www.bostonproper.com/cdn/shop/files/2DP1920_SET.jpg?v=1791009211&width=360 360w, //www.bostonproper.com/cdn/shop/files/2DP1920_SET.jpg?v=1791009211&width=540 540w, //www.bostonproper.com/cdn/shop/files/2DP1920_SET.jpg?v=1791009211&width=720 720w, //www.bostonproper.com/cdn/shop/files/2DP1920_SET.jpg?v=1791009211&width=1080 1080w" 15760 sizes="(min-width: 1024px) 25vw, 50vw" 15761 width="1639" 15762 height="2048" 15763 alt="Ultimate Velvet Double Breasted Shaped Blazer Black" 15764 loading="lazy" 15765 data-bp-card-image-primary 15766 ><img 15767 class="bp-card__image bp-card__image--secondary" 15768 src="//www.bostonproper.com/cdn/shop/files/2_1560b9bf-904f-46e5-b2c8-2e27b8946935.jpg?v=1791009211&width=720" 15769 srcset="//www.bostonproper.com/cdn/shop/files/2_1560b9bf-904f-46e5-b2c8-2e27b8946935.jpg?v=1791009211&width=360 360w, //www.bostonproper.com/cdn/shop/files/2_1560b9bf-904f-46e5-b2c8-2e27b8946935.jpg?v=1791009211&width=540 540w, //www.bostonproper.com/cdn/shop/files/2_1560b9bf-904f-46e5-b2c8-2e27b8946935.jpg?v=1791009211&width=720 720w" 15770 sizes="(min-width: 1024px) 25vw, 50vw" 15771 width="1639" 15772 height="2048" 15773 alt="" 15774 loading="lazy" 15775 aria-hidden="true" 15776 data-bp-card-image-secondary 15777 ></a><div class="bp-card__badges" data-bp-card-badges></div></div> 15778 15779 <div class="bp-card__body"><h3 class="bp-card__title"> 15780 <a href="/products/ultimate-velvet-double-breasted-shaped-blazer-black">Ultimate Velvet Double Breasted Shaped Blazer</a> 15781 </h3> 15782 15783 <div class="bp-card__price-row"><span class="bp-card__price">$150</span> 15784 <s class="bp-card__compare" hidden></s></div></div> 15785 </article> 15786</product-card> 15787</li><li class="bp-cart-drawer__recs-card"><product-card class="bp-card-host"> 15788 <article 15789 class="bp-card" 15790 data-product-id="15371225596070" 15791 data-product-url="/products/breast-cancer-awareness-graphic-sweatshirt-azalea" 15792 data-bp-card-secondary-hover="true" 15793 data-bp-card-qv-dest="drawer" 15794 data-bp-card-qv-vis-d="hover" 15795 data-bp-card-qv-vis-m="shown" 15796 data-bp-card-qb-style="disabled" 15797 data-bp-track-product='{ "id": 15371225596070, "title": "Breast Cancer Awareness Graphic Sweatshirt Azalea", "vendor": "Boston Proper", "handle": "breast-cancer-awareness-graphic-sweatshirt-azalea", "url": "\/products\/breast-cancer-awareness-graphic-sweatshirt-azalea", "image": "\/\/www.bostonproper.com\/cdn\/shop\/files\/2DR2070_SET.jpg?v=1791009256\u0026width=720", "category": "Athleisure", "product_type":null, "selected_variant": { "id": 56671921471654, "sku": "2DR2070700", "price": 60.0, "compare_at_price": 60.0, "option1": "Azalea", "option2": "M", "product_id": 15371225596070, "available": true, "inventory_quantity": 121 } }' 15798 > 15799 <div class="bp-card__media"> 15800 <a class="bp-card__media-link" href="/products/breast-cancer-awareness-graphic-sweatshirt-azalea" aria-label="Breast Cancer Awareness Graphic Sweatshirt Azalea" ref="productCardLink"><img 15801 class="bp-card__image bp-card__image--primary" 15802 src="//www.bostonproper.com/cdn/shop/files/2DR2070_SET.jpg?v=1791009256&width=720" 15803 srcset="//www.bostonproper.com/cdn/shop/files/2DR2070_SET.jpg?v=1791009256&width=360 360w, //www.bostonproper.com/cdn/shop/files/2DR2070_SET.jpg?v=1791009256&width=540 540w, //www.bostonproper.com/cdn/shop/files/2DR2070_SET.jpg?v=1791009256&width=720 720w, //www.bostonproper.com/cdn/shop/files/2DR2070_SET.jpg?v=1791009256&width=1080 1080w" 15804 sizes="(min-width: 1024px) 25vw, 50vw" 15805 width="1639" 15806 height="2048" 15807 alt="Breast Cancer Awareness Graphic Sweatshirt Azalea" 15808 loading="lazy" 15809 data-bp-card-image-primary 15810 ><img 15811 class="bp-card__image bp-card__image--secondary" 15812 src="//www.bostonproper.com/cdn/shop/files/2_c24d6d54-749e-49e8-a1fc-99f0a0a3a351.jpg?v=1791009256&width=720" 15813 srcset="//www.bostonproper.com/cdn/shop/files/2_c24d6d54-749e-49e8-a1fc-99f0a0a3a351.jpg?v=1791009256&width=360 360w, //www.bostonproper.com/cdn/shop/files/2_c24d6d54-749e-49e8-a1fc-99f0a0a3a351.jpg?v=1791009256&width=540 540w, //www.bostonproper.com/cdn/shop/files/2_c24d6d54-749e-49e8-a1fc-99f0a0a3a351.jpg?v=1791009256&width=720 720w" 15814 sizes="(min-width: 1024px) 25vw, 50vw" 15815 width="1639" 15816 height="2048" 15817 alt="" 15818 loading="lazy" 15819 aria-hidden="true" 15820 data-bp-card-image-secondary 15821 ></a><div class="bp-card__badges" data-bp-card-badges></div></div> 15822 15823 <div class="bp-card__body"><h3 class="bp-card__title"> 15824 <a href="/products/breast-cancer-awareness-graphic-sweatshirt-azalea">Breast Cancer Awareness Graphic Sweatshirt</a> 15825 </h3> 15826 15827 <div class="bp-card__price-row"><span class="bp-card__price">$60</span> 15828 <s class="bp-card__compare" hidden></s></div></div> 15829 </article> 15830</product-card> 15831</li><li class="bp-cart-drawer__recs-card"><product-card class="bp-card-host"> 15832 <article 15833 class="bp-card" 15834 data-product-id="15366923452582" 15835 data-product-url="/products/mock-neck-intarsia-flare-sleeve-tunic-west-side-paisley" 15836 data-bp-card-secondary-hover="true" 15837 data-bp-card-qv-dest="drawer" 15838 data-bp-card-qv-vis-d="hover" 15839 data-bp-card-qv-vis-m="shown" 15840 data-bp-card-qb-style="disabled" 15841 data-bp-track-product='{ "id": 15366923452582, "title": "Mock Neck Intarsia Flare Sleeve Tunic West Side Paisley", "vendor": "Boston Proper", "handle": "mock-neck-intarsia-flare-sleeve-tunic-west-side-paisley", "url": "\/products\/mock-neck-intarsia-flare-sleeve-tunic-west-side-paisley", "image": "\/\/www.bostonproper.com\/cdn\/shop\/files\/2DM9540_SET.jpg?v=1791009180\u0026width=720", "category": "Sweater Tops", "product_type":null, "selected_variant": { "id": 56658234933414, "sku": "2DM9540400", "price": 100.0, "compare_at_price": 100.0, "option1": "West Side Paisley",
15841 "option2": "XXS", "product_id": 15366923452582, "available": true, "inventory_quantity": 4 } }' 15842 > 15843 <div class="bp-card__media"> 15844 <a class="bp-card__media-link" href="/products/mock-neck-intarsia-flare-sleeve-tunic-west-side-paisley" aria-label="Mock Neck Intarsia Flare Sleeve Tunic West Side Paisley" ref="productCardLink"><img 15845 class="bp-card__image bp-card__image--primary" 15846 src="//www.bostonproper.com/cdn/shop/files/2DM9540_SET.jpg?v=1791009180&width=720" 15847 srcset="//www.bostonproper.com/cdn/shop/files/2DM9540_SET.jpg?v=1791009180&width=360 360w, //www.bostonproper.com/cdn/shop/files/2DM9540_SET.jpg?v=1791009180&width=540 540w, //www.bostonproper.com/cdn/shop/files/2DM9540_SET.jpg?v=1791009180&width=720 720w, //www.bostonproper.com/cdn/shop/files/2DM9540_SET.jpg?v=1791009180&width=1080 1080w" 15848 sizes="(min-width: 1024px) 25vw, 50vw" 15849 width="1639" 15850 height="2048" 15851 alt="Mock Neck Intarsia Flare Sleeve Tunic West Side Paisley" 15852 loading="lazy" 15853 data-bp-card-image-primary 15854 ><img 15855 class="bp-card__image bp-card__image--secondary" 15856 src="//www.bostonproper.com/cdn/shop/files/2_ba1b3491-d2cb-4ccb-8e77-772ed7e57457.jpg?v=1791009179&width=720" 15857 srcset="//www.bostonproper.com/cdn/shop/files/2_ba1b3491-d2cb-4ccb-8e77-772ed7e57457.jpg?v=1791009179&width=360 360w, //www.bostonproper.com/cdn/shop/files/2_ba1b3491-d2cb-4ccb-8e77-772ed7e57457.jpg?v=1791009179&width=540 540w, //www.bostonproper.com/cdn/shop/files/2_ba1b3491-d2cb-4ccb-8e77-772ed7e57457.jpg?v=1791009179&width=720 720w" 15858 sizes="(min-width: 1024px) 25vw, 50vw" 15859 width="1639" 15860 height="2048" 15861 alt="" 15862 loading="lazy" 15863 aria-hidden="true" 15864 data-bp-card-image-secondary 15865 ></a><div class="bp-card__badges" data-bp-card-badges></div></div> 15866 15867 <div class="bp-card__body"><h3 class="bp-card__title"> 15868 <a href="/products/mock-neck-intarsia-flare-sleeve-tunic-west-side-paisley">Mock Neck Intarsia Flare Sleeve Tunic</a> 15869 </h3> 15870 15871 <div class="bp-card__price-row"><span class="bp-card__price">$100</span> 15872 <s class="bp-card__compare" hidden></s></div></div> 15873 </article> 15874</product-card> 15875</li><li class="bp-cart-drawer__recs-card"><product-card class="bp-card-host"> 15876 <article 15877 class="bp-card" 15878 data-product-id="15364508745894" 15879 data-product-url="/products/bcbgeneration-hardware-strap-to-the-knee-boot-cognac" 15880 data-bp-card-secondary-hover="true" 15881 data-bp-card-qv-dest="drawer" 15882 data-bp-card-qv-vis-d="hover" 15883 data-bp-card-qv-vis-m="shown" 15884 data-bp-card-qb-style="disabled" 15885 data-bp-track-product='{ "id": 15364508745894, "title": "BCBGeneration Hardware Strap To The Knee Boot Cognac", "vendor": "BCBG", "handle": "bcbgeneration-hardware-strap-to-the-knee-boot-cognac", "url": "\/products\/bcbgeneration-hardware-strap-to-the-knee-boot-cognac", "image": "\/\/www.bostonproper.com\/cdn\/shop\/files\/2DO6680_SET_538a0547-6fc7-4be8-8b60-4ea8b9720f2e.jpg?v=1791009184\u0026width=720", "category": "Shoes", "product_type":null, "selected_variant": { "id": 56648379236518, "sku": "2DO6680060", "price": 89.0, "compare_at_price": 89.0, "option1": "Cognac", "option2": "6.0", "product_id": 15364508745894, "available": true, "inventory_quantity": 54 } }' 15886 > 15887 <div class="bp-card__media"> 15888 <a class="bp-card__media-link" href="/products/bcbgeneration-hardware-strap-to-the-knee-boot-cognac" aria-label="BCBGeneration Hardware Strap To The Knee Boot Cognac" ref="productCardLink"><img 15889 class="bp-card__image bp-card__image--primary" 15890 src="//www.bostonproper.com/cdn/shop/files/2DO6680_SET_538a0547-6fc7-4be8-8b60-4ea8b9720f2e.jpg?v=1791009184&width=720" 15891 srcset="//www.bostonproper.com/cdn/shop/files/2DO6680_SET_538a0547-6fc7-4be8-8b60-4ea8b9720f2e.jpg?v=1791009184&width=360 360w, //www.bostonproper.com/cdn/shop/files/2DO6680_SET_538a0547-6fc7-4be8-8b60-4ea8b9720f2e.jpg?v=1791009184&width=540 540w, //www.bostonproper.com/cdn/shop/files/2DO6680_SET_538a0547-6fc7-4be8-8b60-4ea8b9720f2e.jpg?v=1791009184&width=720 720w, //www.bostonproper.com/cdn/shop/files/2DO6680_SET_538a0547-6fc7-4be8-8b60-4ea8b9720f2e.jpg?v=1791009184&width=1080 1080w" 15892 sizes="(min-width: 1024px) 25vw, 50vw" 15893 width="1639" 15894 height="2048" 15895 alt="BCBGeneration Hardware Strap To The Knee Boot Cognac" 15896 loading="lazy" 15897 data-bp-card-image-primary 15898 ><img 15899 class="bp-card__image bp-card__image--secondary" 15900 src="//www.bostonproper.com/cdn/shop/files/2_a1b81ef7-627c-4e8f-ac6d-4c188935d7ce.jpg?v=1791009184&width=720" 15901 srcset="//www.bostonproper.com/cdn/shop/files/2_a1b81ef7-627c-4e8f-ac6d-4c188935d7ce.jpg?v=1791009184&width=360 360w, //www.bostonproper.com/cdn/shop/files/2_a1b81ef7-627c-4e8f-ac6d-4c188935d7ce.jpg?v=1791009184&width=540 540w, //www.bostonproper.com/cdn/shop/files/2_a1b81ef7-627c-4e8f-ac6d-4c188935d7ce.jpg?v=1791009184&width=720 720w" 15902 sizes="(min-width: 1024px) 25vw, 50vw" 15903 width="1639" 15904 height="2048" 15905 alt="" 15906 loading="lazy" 15907 aria-hidden="true" 15908 data-bp-card-image-secondary 15909 ></a><div class="bp-card__badges" data-bp-card-badges></div></div> 15910 15911 <div class="bp-card__body"><h3 class="bp-card__title"> 15912 <a href="/products/bcbgeneration-hardware-strap-to-the-knee-boot-cognac">BCBGeneration Hardware Strap To The Knee Boot</a> 15913 </h3> 15914 15915 <div class="bp-card__price-row"><span class="bp-card__price">$89</span> 15916 <s class="bp-card__compare" hidden></s></div></div> 15917 </article> 15918</product-card> 15919</li><li class="bp-cart-drawer__recs-card"><product-card class="bp-card-host"> 15920 <article 15921 class="bp-card" 15922 data-product-id="15363223355558" 15923 data-product-url="/products/high-waisted-sport-legging-cherry-lacquer" 15924 data-bp-card-secondary-hover="true" 15925 data-bp-card-qv-dest="drawer" 15926 data-bp-card-qv-vis-d="hover" 15927 data-bp-card-qv-vis-m="shown" 15928 data-bp-card-qb-style="disabled" 15929 data-bp-track-product='{ "id": 15363223355558, "title": "High Waisted Sport Legging Cherry Lacquer", "vendor": "Boston Proper", "handle": "high-waisted-sport-legging-cherry-lacquer", "url": "\/products\/high-waisted-sport-legging-cherry-lacquer", "image": "\/\/www.bostonproper.com\/cdn\/shop\/files\/2DB3271_SET.jpg?v=1791009254\u0026width=720", "category": "Athleisure", "product_type":null, "selected_variant": { "id": 56643511681190, "sku": "2DB3271500", "price": 50.0, "compare_at_price": 50.0, "option1": "Cherry Lacquer", "option2": "XS", "product_id": 15363223355558, "available": true, "inventory_quantity": 32 } }' 15930 > 15931 <div class="bp-card__media"> 15932 <a class="bp-card__media-link" href="/products/high-waisted-sport-legging-cherry-lacquer" aria-label="High Waisted Sport Legging Cherry Lacquer" ref="productCardLink"><img 15933 class="bp-card__image bp-card__image--primary" 15934 src="//www.bostonproper.com/cdn/shop/files/2DB3271_SET.jpg?v=1791009254&width=720" 15935 srcset="//www.bostonproper.com/cdn/shop/files/2DB3271_SET.jpg?v=1791009254&width=360 360w, //www.bostonproper.com/cdn/shop/files/2DB3271_SET.jpg?v=1791009254&width=540 540w, //www.bostonproper.com/cdn/shop/files/2DB3271_SET.jpg?v=1791009254&width=720 720w, //www.bostonproper.com/cdn/shop/files/2DB3271_SET.jpg?v=1791009254&width=1080 1080w" 15936 sizes="(min-width: 1024px) 25vw, 50vw" 15937 width="1639" 15938 height="2048" 15939 alt="High Waisted Sport Legging Cherry Lacquer" 15940 loading="lazy" 15941 data-bp-card-image-primary 15942 ><img 15943 class="bp-card__image bp-card__image--secondary" 15944 src="//www.bostonproper.com/cdn/shop/files/2_c0eb9c53-309b-4e5a-bdeb-b4f3a1da61e1.jpg?v=1791009254&width=720" 15945 srcset="//www.bostonproper.com/cdn/shop/files/2_c0eb9c53-309b-4e5a-bdeb-b4f3a1da61e1.jpg?v=1791009254&width=360 360w, //www.bostonproper.com/cdn/shop/files/2_c0eb9c53-309b-4e5a-bdeb-b4f3a1da61e1.jpg?v=1791009254&width=540 540w, //www.bostonproper.com/cdn/shop/files/2_c0eb9c53-309b-4e5a-bdeb-b4f3a1da61e1.jpg?v=1791009254&width=720 720w" 15946 sizes="(min-width: 1024px) 25vw, 50vw" 15947 width="1639" 15948 height="2048" 15949 alt="" 15950 loading="lazy" 15951 aria-hidden="true" 15952 data-bp-card-image-secondary 15953 ></a><div class="bp-card__badges" data-bp-card-badges></div></div> 15954 15955 <div class="bp-card__body"><h3 class="bp-card__title"> 15956 <a href="/products/high-waisted-sport-legging-cherry-lacquer">High Waisted Sport Legging</a> 15957 </h3> 15958 15959 <div class="bp-card__price-row"><span class="bp-card__price">$50</span> 15960 <s class="bp-card__compare" hidden></s></div></div> 15961 </article> 15962</product-card> 15963</li><li class="bp-cart-drawer__recs-card"><product-card class="bp-card-host"> 15964 <article 15965 class="bp-card" 15966 data-product-id="15342913847462" 15967 data-product-url="/products/herringbone-double-breasted-blazer-with-rhinestone-embellishment-gold-gray-multi" 15968 data-bp-card-secondary-hover="true" 15969 data-bp-card-qv-dest="drawer" 15970 data-bp-card-qv-vis-d="hover" 15971 data-bp-card-qv-vis-m="shown" 15972 data-bp-card-qb-style="disabled" 15973 data-bp-track-product='{ "id": 15342913847462, "title": "Herringbone Double Breasted Blazer With Rhinestone Embellishment Gold\/Gray Multi", "vendor": "Boston Proper", "handle": "herringbone-double-breasted-blazer-with-rhinestone-embellishment-gold-gray-multi", "url": "\/products\/herringbone-double-breasted-blazer-with-rhinestone-embellishment-gold-gray-multi", "image": "\/\/www.bostonproper.com\/cdn\/shop\/files\/2DP1690_SET.jpg?v=1791009251\u0026width=720", "category": "Jackets", "product_type":null, "selected_variant": { "id": 56564923891878, "sku": "2DP1690006", "price": 250.0, "compare_at_price": 250.0, "option1": "Gold\/Gray Multi", "option2": "6", "product_id": 15342913847462, "available": true, "inventory_quantity": 71 } }' 15974 > 15975 <div class="bp-card__media"> 15976 <a class="bp-card__media-link" href="/products/herringbone-double-breasted-blazer-with-rhinestone-embellishment-gold-gray-multi" aria-label="Herringbone Double Breasted Blazer With Rhinestone Embellishment Gold/Gray Multi" ref="productCardLink"><img 15977 class="bp-card__image bp-card__image--primary" 15978 src="//www.bostonproper.com/cdn/shop/files/2DP1690_SET.jpg?v=1791009251&width=720" 15979 srcset="//www.bostonproper.com/cdn/shop/files/2DP1690_SET.jpg?v=1791009251&width=360 360w, //www.bostonproper.com/cdn/shop/files/2DP1690_SET.jpg?v=1791009251&width=540 540w, //www.bostonproper.com/cdn/shop/files/2DP1690_SET.jpg?v=1791009251&width=720 720w, //www.bostonproper.com/cdn/shop/files/2DP1690_SET.jpg?v=1791009251&width=1080 1080w" 15980 sizes="(min-width: 1024px) 25vw, 50vw" 15981 width="1639" 15982 height="2048" 15983 alt="Herringbone Double Breasted Blazer With Rhinestone Embellishment Gold/Gray Multi" 15984 loading="lazy" 15985 data-bp-card-image-primary 15986 ><img 15987 class="bp-card__image bp-card__image--secondary" 15988 src="//www.bostonproper.com/cdn/shop/files/2_3c4a7bff-e851-481b-a
15988a8c-37759ba4fe6e.jpg?v=1791009251&width=720" 15989 srcset="//www.bostonproper.com/cdn/shop/files/2_3c4a7bff-e851-481b-aa8c-37759ba4fe6e.jpg?v=1791009251&width=360 360w, //www.bostonproper.com/cdn/shop/files/2_3c4a7bff-e851-481b-aa8c-37759ba4fe6e.jpg?v=1791009251&width=540 540w, //www.bostonproper.com/cdn/shop/files/2_3c4a7bff-e851-481b-aa8c-37759ba4fe6e.jpg?v=1791009251&width=720 720w" 15990 sizes="(min-width: 1024px) 25vw, 50vw" 15991 width="1639" 15992 height="2048" 15993 alt="" 15994 loading="lazy" 15995 aria-hidden="true" 15996 data-bp-card-image-secondary 15997 ></a><div class="bp-card__badges" data-bp-card-badges></div></div> 15998 15999 <div class="bp-card__body"><h3 class="bp-card__title"> 16000 <a href="/products/herringbone-double-breasted-blazer-with-rhinestone-embellishment-gold-gray-multi">Herringbone Double Breasted Blazer With Rhinestone Embellishment</a> 16001 </h3> 16002 16003 <div class="bp-card__price-row"><span class="bp-card__price">$250</span> 16004 <s class="bp-card__compare" hidden></s></div></div> 16005 </article> 16006</product-card> 16007</li><li class="bp-cart-drawer__recs-card"><product-card class="bp-card-host"> 16008 <article 16009 class="bp-card" 16010 data-product-id="15336227864742" 16011 data-product-url="/products/knit-suede-drape-front-jacket-stone" 16012 data-bp-card-secondary-hover="true" 16013 data-bp-card-qv-dest="drawer" 16014 data-bp-card-qv-vis-d="hover" 16015 data-bp-card-qv-vis-m="shown" 16016 data-bp-card-qb-style="disabled" 16017 data-bp-track-product='{ "id": 15336227864742, "title": "Knit Suede Drape Front Jacket Stone", "vendor": "Boston Proper", "handle": "knit-suede-drape-front-jacket-stone", "url": "\/products\/knit-suede-drape-front-jacket-stone", "image": "\/\/www.bostonproper.com\/cdn\/shop\/files\/2DR1780_SET.jpg?v=1791009250\u0026width=720", "category": "Jackets", "product_type":null, "selected_variant": { "id": 56545549123750, "sku": "2DR1780400", "price": 125.0, "compare_at_price": 125.0, "option1": "Stone", "option2": "XXS", "product_id": 15336227864742, "available": true, "inventory_quantity": 6 } }' 16018 > 16019 <div class="bp-card__media"> 16020 <a class="bp-card__media-link" href="/products/knit-suede-drape-front-jacket-stone" aria-label="Knit Suede Drape Front Jacket Stone" ref="productCardLink"><img 16021 class="bp-card__image bp-card__image--primary" 16022 src="//www.bostonproper.com/cdn/shop/files/2DR1780_SET.jpg?v=1791009250&width=720" 16023 srcset="//www.bostonproper.com/cdn/shop/files/2DR1780_SET.jpg?v=1791009250&width=360 360w, //www.bostonproper.com/cdn/shop/files/2DR1780_SET.jpg?v=1791009250&width=540 540w, //www.bostonproper.com/cdn/shop/files/2DR1780_SET.jpg?v=1791009250&width=720 720w, //www.bostonproper.com/cdn/shop/files/2DR1780_SET.jpg?v=1791009250&width=1080 1080w" 16024 sizes="(min-width: 1024px) 25vw, 50vw" 16025 width="1639" 16026 height="2048" 16027 alt="Knit Suede Drape Front Jacket Stone" 16028 loading="lazy" 16029 data-bp-card-image-primary 16030 ><img 16031 class="bp-card__image bp-card__image--secondary" 16032 src="//www.bostonproper.com/cdn/shop/files/2_658da758-2997-4038-9a66-4589dff50ec8.jpg?v=1791009249&width=720" 16033 srcset="//www.bostonproper.com/cdn/shop/files/2_658da758-2997-4038-9a66-4589dff50ec8.jpg?v=1791009249&width=360 360w, //www.bostonproper.com/cdn/shop/files/2_658da758-2997-4038-9a66-4589dff50ec8.jpg?v=1791009249&width=540 540w, //www.bostonproper.com/cdn/shop/files/2_658da758-2997-4038-9a66-4589dff50ec8.jpg?v=1791009249&width=720 720w" 16034 sizes="(min-width: 1024px) 25vw, 50vw" 16035 width="1639" 16036 height="2048" 16037 alt="" 16038 loading="lazy" 16039 aria-hidden="true" 16040 data-bp-card-image-secondary 16041 ></a><div class="bp-card__badges" data-bp-card-badges></div></div> 16042 16043 <div class="bp-card__body"><h3 class="bp-card__title"> 16044 <a href="/products/knit-suede-drape-front-jacket-stone">Knit Suede Drape Front Jacket</a> 16045 </h3> 16046 16047 <div class="bp-card__price-row"><span class="bp-card__price">$125</span> 16048 <s class="bp-card__compare" hidden></s></div></div> 16049 </article> 16050</product-card> 16051</li><li class="bp-cart-drawer__recs-card"><product-card class="bp-card-host"> 16052 <article 16053 class="bp-card" 16054 data-product-id="15336227831974" 16055 data-product-url="/products/knit-suede-drape-front-jacket-blue" 16056 data-bp-card-secondary-hover="true" 16057 data-bp-card-qv-dest="drawer" 16058 data-bp-card-qv-vis-d="hover" 16059 data-bp-card-qv-vis-m="shown" 16060 data-bp-card-qb-style="disabled" 16061 data-bp-track-product='{ "id": 15336227831974, "title": "Knit Suede Drape Front Jacket Blue", "vendor": "Boston Proper", "handle": "knit-suede-drape-front-jacket-blue", "url": "\/products\/knit-suede-drape-front-jacket-blue", "image": "\/\/www.bostonproper.com\/cdn\/shop\/files\/2DR1730_SET_9fea2518-0e42-496d-aca4-19f54a5af591.jpg?v=1791009249\u0026width=720", "category": "Jackets", "product_type":null, "selected_variant": { "id": 56545548927142, "sku": "2DR1730400", "price": 125.0, "compare_at_price": 125.0, "option1": "Blue", "option2": "XXS", "product_id": 15336227831974, "available": true, "inventory_quantity": 12 } }' 16062 > 16063 <div class="bp-card__media"> 16064 <a class="bp-card__media-link" href="/products/knit-suede-drape-front-jacket-blue" aria-label="Knit Suede Drape Front Jacket Blue" ref="productCardLink"><img 16065 class="bp-card__image bp-card__image--primary" 16066 src="//www.bostonproper.com/cdn/shop/files/2DR1730_SET_9fea2518-0e42-496d-aca4-19f54a5af591.jpg?v=1791009249&width=720" 16067 srcset="//www.bostonproper.com/cdn/shop/files/2DR1730_SET_9fea2518-0e42-496d-aca4-19f54a5af591.jpg?v=1791009249&width=360 360w, //www.bostonproper.com/cdn/shop/files/2DR1730_SET_9fea2518-0e42-496d-aca4-19f54a5af591.jpg?v=1791009249&width=540 540w, //www.bostonproper.com/cdn/shop/files/2DR1730_SET_9fea2518-0e42-496d-aca4-19f54a5af591.jpg?v=1791009249&width=720 720w, //www.bostonproper.com/cdn/shop/files/2DR1730_SET_9fea2518-0e42-496d-aca4-19f54a5af591.jpg?v=1791009249&width=1080 1080w" 16068 sizes="(min-width: 1024px) 25vw, 50vw" 16069 width="1639" 16070 height="2048" 16071 alt="Knit Suede Drape Front Jacket Blue" 16072 loading="lazy" 16073 data-bp-card-image-primary 16074 ><img 16075 class="bp-card__image bp-card__image--secondary" 16076 src="//www.bostonproper.com/cdn/shop/files/2_289296ee-9816-41a8-9944-3c93127b0c63.jpg?v=1791009248&width=720" 16077 srcset="//www.bostonproper.com/cdn/shop/files/2_289296ee-9816-41a8-9944-3c93127b0c63.jpg?v=1791009248&width=360 360w, //www.bostonproper.com/cdn/shop/files/2_289296ee-9816-41a8-9944-3c93127b0c63.jpg?v=1791009248&width=540 540w, //www.bostonproper.com/cdn/shop/files/2_289296ee-9816-41a8-9944-3c93127b0c63.jpg?v=1791009248&width=720 720w" 16078 sizes="(min-width: 1024px) 25vw, 50vw" 16079 width="1639" 16080 height="2048" 16081 alt="" 16082 loading="lazy" 16083 aria-hidden="true" 16084 data-bp-card-image-secondary 16085 ></a><div class="bp-card__badges" data-bp-card-badges></div></div> 16086 16087 <div class="bp-card__body"><h3 class="bp-card__title"> 16088 <a href="/products/knit-suede-drape-front-jacket-blue">Knit Suede Drape Front Jacket</a> 16089 </h3> 16090 16091 <div class="bp-card__price-row"><span class="bp-card__price">$125</span> 16092 <s class="bp-card__compare" hidden></s></div></div> 16093 </article> 16094</product-card> 16095</li><li class="bp-cart-drawer__recs-card"><product-card class="bp-card-host"> 16096 <article 16097 class="bp-card" 16098 data-product-id="15334258868390" 16099 data-product-url="/products/half-zip-sparkle-snowflake-sweater-french-kiss" 16100 data-bp-card-secondary-hover="true" 16101 data-bp-card-qv-dest="drawer" 16102 data-bp-card-qv-vis-d="hover" 16103 data-bp-card-qv-vis-m="shown" 16104 data-bp-card-qb-style="disabled" 16105 data-bp-track-product='{ "id": 15334258868390, "title": "Half Zip Sparkle Snowflake Sweater French Kiss", "vendor": "Boston Proper", "handle": "half-zip-sparkle-snowflake-sweater-french-kiss", "url": "\/products\/half-zip-sparkle-snowflake-sweater-french-kiss", "image": "\/\/www.bostonproper.com\/cdn\/shop\/files\/2DQ0611_SET.jpg?v=1791009258\u0026width=720", "category": "Sweater Tops", "product_type":null, "selected_variant": { "id": 56539690696870, "sku": "2DQ0611500", "price": 100.0, "compare_at_price": 100.0, "option1": "French Kiss", "option2": "XS", "product_id": 15334258868390, "available": true, "inventory_quantity": 90 } }' 16106 > 16107 <div class="bp-card__media"> 16108 <a class="bp-card__media-link" href="/products/half-zip-sparkle-snowflake-sweater-french-kiss" aria-label="Half Zip Sparkle Snowflake Sweater French Kiss" ref="productCardLink"><img 16109 class="bp-card__image bp-card__image--primary" 16110 src="//www.bostonproper.com/cdn/shop/files/2DQ0611_SET.jpg?v=1791009258&width=720" 16111 srcset="//www.bostonproper.com/cdn/shop/files/2DQ0611_SET.jpg?v=1791009258&width=360 360w, //www.bostonproper.com/cdn/shop/files/2DQ0611_SET.jpg?v=1791009258&width=540 540w, //www.bostonproper.com/cdn/shop/files/2DQ0611_SET.jpg?v=1791009258&width=720 720w, //www.bostonproper.com/cdn/shop/files/2DQ0611_SET.jpg?v=1791009258&width=1080 1080w" 16112 sizes="(min-width: 1024px) 25vw, 50vw" 16113 width="1639" 16114 height="2048" 16115 alt="Half Zip Sparkle Snowflake Sweater French Kiss" 16116 loading="lazy" 16117 data-bp-card-image-primary 16118 ><img 16119 class="bp-card__image bp-card__image--secondary" 16120 src="//www.bostonproper.com/cdn/shop/files/2_6a50f250-465a-41b2-9e01-3af49d502b58.jpg?v=1791009258&width=720" 16121 srcset="//www.bostonproper.com/cdn/shop/files/2_6a50f250-465a-41b2-9e01-3af49d502b58.jpg?v=1791009258&width=360 360w, //www.bostonproper.com/cdn/shop/files/2_6a50f250-465a-41b2-9e01-3af49d502b58.jpg?v=1791009258&width=540 540w, //www.bostonproper.com/cdn/shop/files/2_6a50f250-465a-41b2-9e01-3af49d502b58.jpg?v=1791009258&width=720 720w" 16122 sizes="(min-width: 1024px) 25vw, 50vw" 16123 width="1639" 16124 height="2048" 16125 alt="" 16126 loading="lazy" 16127 aria-hidden="true" 16128 data-bp-card-image-secondary 16129 ></a><div class="bp-card__badges" data-bp-card-badges></div></div> 16130 16131 <div class="bp-card__body"><h3 class="bp-card__title"> 16132 <a href="/products/half-zip-sparkle-snowflake-sweater-french-kiss">Half Zip Sparkle Snowflake Sweater</a> 16133 </h3> 16134 16135 <div class="bp-card__price-row"><span class="bp-card__price">$100</span> 16136 <s class="bp-card__compare" hidden></s></div></div> 16137 </article> 16138</product-card> 16139</li></ul> 16140 </div></div> 16141 <ul class="bp-cart-drawer__items" data-bp-cart-items hidden></ul><section class="bp-cart-drawer__ctl" data-bp-ctl hidden> 16142 <header class="bp-cart-drawer__ctl-head"> 16143 <h3 class="bp-cart-drawer__ctl-title">Complete the Look</h3> 16144 <div class="bp-cart-drawer__ctl-arrows"> 16145 <button type="button" class="bp-cart-drawer__ctl-arrow" data-bp-ctl-prev aria-label="Previous"><svg width="44" height="44" viewBox="0 0 44 44" fill="none" aria-hidden="true">
16146 <path d="M18.1326 13.8674C18.0884 13.8244 18.0442 13.7814 18 13.7385C17.9128 13.8256 17.8256 13.9128 17.7385 14C17.7814 14.0442 17.8244 14.0884 17.8674 14.1326C18.1304 14.4029 18.3934 14.6733 18.6564 14.9436C21.0232 17.3768 23.3901 19.8099 25.7569 22.2431V21.7569C23.3238 24.1238 20.8906 26.4906 18.4575 28.8575C18.1871 29.1205 17.9168 29.3835 17.6464 29.6464C17.527 29.7627 17.4075 29.8789 17.288 29.9951C17.527 30.2341 17.7659 30.473 18.0049 30.712C18.1211 30.5925 18.2373 30.473 18.3536 30.3536C18.6165 30.0832 18.8795 29.8129 19.1425 29.5425C21.5094 27.1094 23.8762 24.6762 26.2431 22.2431L26.486 21.9933L26.2431 21.7569C23.8099 19.3901 21.3768 17.0232 18.9436 14.6564C18.6733 14.3934 18.4029 14.1304 18.1326 13.8674Z" fill="currentColor"/> 16147 <path d="M18.3536 13.6464C18.2373 13.527 18.1211 13.4075 18.0049 13.288C17.7659 13.527 17.527 13.7659 17.288 14.0049C17.4075 14.1211 17.527 14.2373 17.6464 14.3536C17.9168 14.6165 18.1871 14.8795 18.4575 15.1425C20.8906 17.5094 23.3238 19.8762 25.7569 22.2431V21.7569C23.3901 24.1901 21.0232 26.6232 18.6564 29.0564C18.3934 29.3267 18.1304 29.5971 17.8674 29.8674C17.8244 29.9116 17.7814 29.9558 17.7385 30C17.8256 30.0872 17.9128 30.1744 18 30.2615C18.0442 30.2186 18.0884 30.1756 18.1326 30.1326C18.4029 29.8696 18.6733 29.6066 18.9436 29.3436C21.3768 26.9768 23.8099 24.6099 26.2431 22.2431L26.486 21.9933L26.2431 21.7569C23.8762 19.3238 21.5094 16.8906 19.1425 14.4575C18.8795 14.1871 18.6165 13.9168 18.3536 13.6464Z" fill="currentColor"/> 16148 </svg> 16149 </button> 16150 <button type="button" class="bp-cart-drawer__ctl-arrow" data-bp-ctl-next aria-label="Next"> 16151 <svg width="44" height="44" viewBox="0 0 44 44" fill="none" aria-hidden="true"> 16152 <path d="M18.1326 13.8674C18.0884 13.8244 18.0442 13.7814 18 13.7385C17.9128 13.8256 17.8256 13.9128 17.7385 14C17.7814 14.0442 17.8244 14.0884 17.8674 14.1326C18.1304 14.4029 18.3934 14.6733 18.6564 14.9436C21.0232 17.3768 23.3901 19.8099 25.7569 22.2431V21.7569C23.3238 24.1238 20.8906 26.4906 18.4575 28.8575C18.1871 29.1205 17.9168 29.3835 17.6464 29.6464C17.527 29.7627 17.4075 29.8789 17.288 29.9951C17.527 30.2341 17.7659 30.473 18.0049 30.712C18.1211 30.5925 18.2373 30.473 18.3536 30.3536C18.6165 30.0832 18.8795 29.8129 19.1425 29.5425C21.5094 27.1094 23.8762 24.6762 26.2431 22.2431L26.486 21.9933L26.2431 21.7569C23.8099 19.3901 21.3768 17.0232 18.9436 14.6564C18.6733 14.3934 18.4029 14.1304 18.1326 13.8674Z" fill="currentColor"/> 16153 <path d="M18.3536 13.6464C18.2373 13.527 18.1211 13.4075 18.0049 13.288C17.7659 13.527 17.527 13.7659 17.288 14.0049C17.4075 14.1211 17.527 14.2373 17.6464 14.3536C17.9168 14.6165 18.1871 14.8795 18.4575 15.1425C20.8906 17.5094 23.3238 19.8762 25.7569 22.2431V21.7569C23.3901 24.1901 21.0232 26.6232 18.6564 29.0564C18.3934 29.3267 18.1304 29.5971 17.8674 29.8674C17.8244 29.9116 17.7814 29.9558 17.7385 30C17.8256 30.0872 17.9128 30.1744 18 30.2615C18.0442 30.2186 18.0884 30.1756 18.1326 30.1326C18.4029 29.8696 18.6733 29.6066 18.9436 29.3436C21.3768 26.9768 23.8099 24.6099 26.2431 22.2431L26.486 21.9933L26.2431 21.7569C23.8762 19.3238 21.5094 16.8906 19.1425 14.4575C18.8795 14.1871 18.6165 13.9168 18.3536 13.6464Z" fill="currentColor"/> 16154 </svg> 16155 </button> 16156 </div> 16157 </header><div class="bp-cart-drawer__ctl-track" data-bp-ctl-track></div><div class="bp-cart-drawer__ctl-indicator" data-bp-ctl-indicator aria-hidden="true"></div> 16158 </section> 16159 </div><footer class="bp-cart-drawer__foot" hidden><section 16160 class="bp-cart-discount bp-cart-discount--drawer" 16161 data-bp-cart-discount 16162 data-bp-existing-codes="" 16163><div class="bp-cart-discount__collapse" data-bp-promo-collapse> 16164 <button 16165 type="button" 16166 class="bp-cart-discount__summary" 16167 data-bp-promo-toggle 16168 aria-expanded="false" 16169 aria-controls="bp-promo-body-drawer" 16170 > 16171 <span class="bp-cart-discount__summary-text">Promo code</span> 16172 <svg class="bp-cart-discount__summary-chev" width="12" height="8" viewBox="0 0 12 8" fill="none" aria-hidden="true"> 16173 <path d="M1 1.5L6 6.5L11 1.5" stroke="currentColor" stroke-width="1.4" stroke-linecap="round" stroke-linejoin="round"/> 16174 </svg> 16175 </button> 16176 <div class="bp-cart-discount__collapse-body" id="bp-promo-body-drawer" data-bp-promo-body> 16177 <div class="bp-cart-discount__collapse-inner"><form class="bp-cart-discount__form" data-bp-cart-discount-form novalidate> 16178 <div class="bp-cart-discount__row"> 16179 <input 16180 type="text" 16181 id="bp-cart-discount-input-drawer" 16182 name="discount" 16183 class="bp-cart-discount__input" 16184 placeholder="Enter promo code" 16185 autocomplete="off" 16186 autocapitalize="characters" 16187 spellcheck="false" 16188 data-bp-cart-discount-input 16189 aria-label="Promo code" 16190 aria-describedby="bp-cart-discount-error-drawer" 16191 > 16192 <button type="submit" class="bp-cart-discount__submit" data-bp-cart-discount-submit>Apply</button> 16193 </div> 16194 <p class="bp-cart-discount__error" id="bp-cart-discount-error-drawer" data-bp-cart-discount-error role="alert" hidden>This code isn't valid. Please try another.</p> 16195 </form></div> 16196 </div> 16197 </div></section> 16198 16199<style data-shopify> 16200 .bp-cart-discount { 16201 width: 100%; 16202 margin: 12px 0 0; 16203 font-family: var(--bp-font-body); 16204 } 16205 /* Drawer footer (snippets/bp-cart-drawer.liquid) is a flex column with 16206 its own `gap` between rows â this block's own top margin stacked on 16207 top of that gap, doubling the space above it. Page surface keeps the 16208 margin (not laid out in a gapped flex stack). */ 16209 .bp-cart-discount--drawer { margin-top: 0; } 16210 .bp-cart-discount__form { margin: 0; padding: 0; } 16211 16212 /* Collapsible promo bar (drawer). Collapsed = just the summary row, 16213 so the sticky footer reclaims the input + button height. The body 16214 grows/shrinks with a grid-template-rows 0fr<->1fr transition. */ 16215 .bp-cart-discount__collapse { width: 100%; } 16216 .bp-cart-discount__summary { 16217 display: flex; 16218 width: 100%; 16219 align-items: center; 16220 justify-content: space-between; 16221 gap: 8px; 16222 margin: 0; 16223 padding: 2px 0; 16224 background: transparent; 16225 border: 0; 16226 text-align: left; 16227 cursor: pointer; 16228 font-family: var(--bp-font-body); 16229 font-size: 12px; 16230 font-weight: 700; 16231 letter-spacing: 0.5px; 16232 text-transform: uppercase; 16233 color: var(--bp-color-text); 16234 } 16235 .bp-cart-discount__summary:focus-visible { 16236 outline: 2px solid var(--bp-color-text); 16237 outline-offset: 2px; 16238 } 16239 .bp-cart-discount__summary-chev { 16240 flex: 0 0 auto; 16241 transition: transform 0.28s ease; 16242 } 16243 .bp-cart-discount__collapse.is-open .bp-cart-discount__summary-chev { transform: rotate(180deg); } 16244 16245 .bp-cart-discount__collapse-body { 16246 display: grid; 16247 grid-template-rows: 0fr; 16248 transition: grid-template-rows 0.28s ease; 16249 } 16250 .bp-cart-discount__collapse.is-open .bp-cart-discount__collapse-body { grid-template-rows: 1fr; } 16251 /* The inner wrapper is the animated row â must clip + allow 0 height. */ 16252 .bp-cart-discount__collapse-inner { 16253 overflow: hidden; 16254 min-height: 0; 16255 padding-top: 10px; 16256 } 16257 @media (prefers-reduced-motion: reduce) { 16258 .bp-cart-discount__collapse-body, 16259 .bp-cart-discount__summary-chev { transition: none; } 16260 } 16261 16262 .bp-cart-discount__label { 16263 display: block; 16264 margin: 0 0 6px; 16265 font-size: 12px; 16266 font-weight: 700; 16267 letter-spacing: 0.5px; 16268 text-transform: uppercase; 16269 color: var(--bp-color-text); 16270 } 16271 .bp-cart-discount__row { 16272 display: flex; 16273 gap: 8px; 16274 align-items: stretch; 16275 } 16276 .bp-cart-discount__input { 16277 flex: 1 1 auto; 16278 min-width: 0; 16279 height: 44px; 16280 padding: 0 12px; 16281 background: transparent; 16282 border: 1px solid rgba(23, 22, 9, 0.32); 16283 color: var(--bp-color-text); 16284 font-family: var(--bp-font-body); 16285 font-size: 14px; 16286 line-height: 1; 16287 letter-spacing: 0.04em; 16288 text-transform: uppercase; 16289 box-sizing: border-box; 16290 } 16291 .bp-cart-discount__input::placeholder { color: var(--bp-color-body-copy, #74736B); text-transform: none; letter-spacing: -0.14px; } 16292 .bp-cart-discount__input:focus { 16293 outline: none; 16294 border-color: var(--bp-color-text); 16295 } 16296 .bp-cart-discount__input:focus-visible { 16297 outline: 2px solid var(--bp-color-text); 16298 outline-offset: 2px; 16299 } 16300 .bp-cart-discount__submit { 16301 flex: 0 0 auto; 16302 height: 44px; 16303 padding: 0 20px; 16304 background: var(--bp-color-text); 16305 color: var(--bp-color-cta-text); 16306 border: 0; 16307 font-family: var(--bp-font-body); 16308 font-size: 14px; 16309 font-weight: 500; 16310 letter-spacing: -0.14px; 16311 cursor: pointer; 16312 transition: opacity 0.2s ease; 16313 } 16314 .bp-cart-discount__submit:hover { opacity: 0.85; } 16315 .bp-cart-discount__submit:disabled { opacity: 0.5;
16315 cursor: not-allowed; } 16316 .bp-cart-discount__error { 16317 margin: 8px 0 0; 16318 color: var(--bp-color-promo, #B5341B); 16319 font-size: 12px; 16320 line-height: 145%; 16321 letter-spacing: -0.12px; 16322 } 16323 16324 /* Applied-code pills */ 16325 .bp-cart-discount__pills { 16326 list-style: none; 16327 margin: 10px 0 0; 16328 padding: 0; 16329 display: flex; 16330 flex-wrap: wrap; 16331 gap: 6px; 16332 } 16333 .bp-cart-discount__pill { 16334 display: inline-flex; 16335 align-items: center; 16336 gap: 6px; 16337 padding: 4px 4px 4px 10px; 16338 background: var(--bp-color-bg-cream, #F7F6EA); 16339 color: var(--bp-color-text); 16340 font-size: 12px; 16341 font-weight: 500; 16342 letter-spacing: 0.04em; 16343 text-transform: uppercase; 16344 border-radius: 0; 16345 } 16346 .bp-cart-discount__pill-code { line-height: 1; } 16347 .bp-cart-discount__pill-remove { 16348 display: inline-flex; 16349 align-items: center; 16350 justify-content: center; 16351 width: 20px; 16352 height: 20px; 16353 padding: 0; 16354 background: transparent; 16355 border: 0; 16356 color: var(--bp-color-text); 16357 cursor: pointer; 16358 border-radius: 50%; 16359 transition: background 0.15s ease; 16360 } 16361 .bp-cart-discount__pill-remove:hover { background: rgba(23, 22, 9, 0.08); } 16362</style> 16363
16364<script> 16365 (function () { 16366 if (window.__bpCartDiscountWired) return; 16367 window.__bpCartDiscountWired = true; 16368 16369 const ROOT = (window.Shopify && window.Shopify.routes && window.Shopify.routes.root) || '/'; 16370 16371 function getCodesFromHost(host) { 16372 const raw = host.getAttribute('data-bp-existing-codes') || ''; 16373 return raw ? raw.split(',').filter(Boolean) : []; 16374 } 16375 16376 async function postDiscount(codes) { 16377 const res = await fetch(ROOT + 'cart/update.js', { 16378 method: 'POST', 16379 headers: { 'Content-Type': 'application/json', 'Accept': 'application/json' }, 16380 body: JSON.stringify({ discount: codes.join(',') }), 16381 credentials: 'same-origin', 16382 cache: 'no-store', 16383 }); 16384 if (!res.ok) throw new Error('cart/update.js HTTP ' + res.status); 16385 return res.json(); 16386 } 16387 16388 function setError(host, show, msg) { 16389 const el = host.querySelector('[data-bp-cart-discount-error]'); 16390 const input = host.querySelector('[data-bp-cart-discount-input]'); 16391 if (input) input.setAttribute('aria-invalid', show ? 'true' : 'false'); 16392 if (!el) return; 16393 if (show) { 16394 if (msg) el.textContent = msg; 16395 el.hidden = false; 16396 } else { 16397 el.hidden = true; 16398 } 16399 } 16400 16401 function isApplicableInCart(cart, code) { 16402 return (cart.discount_codes || []).some((d) => d.code === code && d.applicable === true); 16403 } 16404 16405 function esc(s) { 16406 return String(s == null ? '' : s) 16407 .replace(/&/g, '&').replace(/</g, '<').replace(/>/g, '>') 16408 .replace(/"/g, '"'); 16409 } 16410 16411 // Rebuild the applied-code pills in place so they behave like checkout: 16412 // a chip with a remove appears the moment a code applies and clears the 16413 // moment it is removed, with no page reload. Mirrors the server-rendered 16414 // pill markup; the delegated remove handler below covers injected buttons. 16415 function renderPills(host, applied) { 16416 const form = host.querySelector('[data-bp-cart-discount-form]'); 16417 if (!form) return; 16418 let ul = host.querySelector('[data-bp-cart-discount-pills]'); 16419 if (!applied.length) { if (ul) ul.remove(); return; } 16420 if (!ul) { 16421 ul = document.createElement('ul'); 16422 ul.className = 'bp-cart-discount__pills'; 16423 ul.setAttribute('data-bp-cart-discount-pills', ''); 16424 ul.setAttribute('role', 'list'); 16425 form.parentNode.insertBefore(ul, form.nextSibling); 16426 } 16427 ul.innerHTML = applied.map((code) => { 16428 const c = esc(code); 16429 return '<li class="bp-cart-discount__pill" data-bp-cart-discount-pill data-discount-code="' + c + '">' + 16430 '<span class="bp-cart-discount__pill-code">' + c + '</span>' + 16431 '<button type="button" class="bp-cart-discount__pill-remove" aria-label="Remove ' + c + '" data-bp-cart-discount-remove="' + c + '">' + 16432 '<svg width="12" height="12" viewBox="0 0 12 12" fill="none" aria-hidden="true"><path d="M3 3l6 6M9 3l-6 6" stroke="currentColor" stroke-width="1.2" stroke-linecap="round"/></svg>' + 16433 '</button></li>'; 16434 }).join(''); 16435 } 16436 16437 function broadcastCartUpdate(cart) { 16438 document.dispatchEvent(new CustomEvent('cart:updated', { detail: { cart } })); 16439 // Update every BP-discount host's data-bp-existing-codes (so the next 16440 // submit carries the new state) AND re-render its applied-code pills in 16441 // place, so the removable chip shows/clears live like the checkout one. 16442 const applied = (cart.discount_codes || []) 16443 .filter((d) => d.applicable) 16444 .map((d) => d.code); 16445 document.querySelectorAll('[data-bp-cart-discount]').forEach((host) => { 16446 host.setAttribute('data-bp-existing-codes', applied.join(',')); 16447 renderPills(host, applied); 16448 }); 16449 } 16450 16451 // Collapsible promo bar (drawer) â toggle the accordion open/closed. 16452 document.addEventListener('click', (e) => { 16453 const toggle = e.target && e.target.closest && e.target.closest('[data-bp-promo-toggle]'); 16454 if (!toggle) return; 16455 const collapse = toggle.closest('[data-bp-promo-collapse]'); 16456 if (!collapse) return; 16457 const open = collapse.classList.toggle('is-open'); 16458 toggle.setAttribute('aria-expanded', open ? 'true' : 'false'); 16459 }); 16460 16461 document.addEventListener('submit', async (e) => { 16462 const form = e.target && e.target.closest && e.target.closest('[data-bp-cart-discount-form]'); 16463 if (!form) return; 16464 e.preventDefault(); 16465 const host = form.closest('[data-bp-cart-discount]'); 16466 const input = form.querySelector('[data-bp-cart-discount-input]'); 16467 const submit = form.querySelector('[data-bp-cart-discount-submit]'); 16468 if (!host || !input) return; 16469 const code = (input.value || '').trim(); 16470 if (!code) return; 16471 16472 const existing = getCodesFromHost(host); 16473 if (existing.includes(code)) { 16474 input.value = ''; 16475 return; 16476 } 16477 setError(host, false); 16478 if (submit) submit.disabled = true;
16479 try { 16480 const cart = await postDiscount(existing.concat([code])); 16481 if (!isApplicableInCart(cart, code)) { 16482 setError(host, true); 16483 input.value = ''; 16484 return; 16485 } 16486 broadcastCartUpdate(cart); 16487 input.value = ''; 16488 // Success confirmation. The subtotal change alone is easy to miss and 16489 // the applied-code pills only re-render on reload, so the shopper had 16490 // no signal the code applied (QA 7/22, Dragana). Surface a toast now. 16491 if (window.bpToast) window.bpToast('Promo code "' + code + '" applied.'); 16492 } catch (err) { 16493 console.warn('[bp-cart-discount] apply failed', err); 16494 setError(host, true, 'Something went wrong. Please try again.'); 16495 } finally { 16496 if (submit) submit.disabled = false; 16497 } 16498 }); 16499 16500 document.addEventListener('click', async (e) => { 16501 const btn = e.target && e.target.closest && e.target.closest('[data-bp-cart-discount-remove]'); 16502 if (!btn) return; 16503 e.preventDefault(); 16504 const host = btn.closest('[data-bp-cart-discount]'); 16505 if (!host) return; 16506 const codeToRemove = btn.getAttribute('data-bp-cart-discount-remove'); 16507 const existing = getCodesFromHost(host).filter((c) => c !== codeToRemove);
16508 try { 16509 const cart = await postDiscount(existing); 16510 broadcastCartUpdate(cart); 16511 } catch (err) { 16512 console.warn('[bp-cart-discount] remove failed', err); 16513 } 16514 }); 16515 16516 // Hydrate the applied-code pills from the live cart on load. A code applied 16517 // in a PRIOR session (persisted on the cart) reliably shows up in /cart.js 16518 // discount_codes, but the server-side Liquid cart object can render empty 16519 // for it, so the server-rendered pill was missing and an already-applied 16520 // code showed no chip until re-applied. Reading the live cart here makes 16521 // the removable chip appear on load regardless. 16522 (function hydrateAppliedCodes() { 16523 const run = () => { 16524 if (!document.querySelector('[data-bp-cart-discount]')) return; 16525 fetch(ROOT + 'cart.js', { credentials: 'same-origin', cache: 'no-store' }) 16526 .then((r) => (r.ok ? r.json() : null)) 16527 .then((cart) => { 16528 if (!cart) return; 16529 const applied = (cart.discount_codes || []) 16530 .filter((d) => d.applicable) 16531 .map((d) => d.code); 16532 document.querySelectorAll('[data-bp-cart-discount]').forEach((host) => { 16533 host.setAttribute('data-bp-existing-codes', applied.join(',')); 16534 renderPills(host, applied); 16535 }); 16536 }) 16537 .catch(() => {}); 16538 }; 16539 if (document.readyState === 'loading') { 16540 document.addEventListener('DOMContentLoaded', run); 16541 } else { 16542 run(); 16543 } 16544 })(); 16545 })(); 16546</script>
16546 16547 16548<div class="bp-cart-drawer__subtotal"> 16549 <span class="bp-cart-drawer__subtotal-label">Subtotal:</span> 16550 <span class="bp-cart-drawer__subtotal-value" data-bp-cart-subtotal>$0</span><span class="totals__subtotal-value" aria-hidden="true" hidden></span> 16551 </div> 16552<div class="bp-cart-drawer__sp-skeleton" data-bp-sp-skeleton aria-hidden="true"> 16553 <div class="bp-cart-drawer__sp-skeleton-head"> 16554 <span class="bp-sp-skeleton-bar bp-cart-drawer__sp-skeleton-title"></span> 16555 <span class="bp-sp-skeleton-bar bp-cart-drawer__sp-skeleton-amt"></span> 16556 </div> 16557 <span class="bp-sp-skeleton-bar bp-cart-drawer__sp-skeleton-desc"></span> 16558 </div> 16559 <div 16560 id="narvar-cart-drawer-dp-widget" 16561 class="bp-cart-drawer__narvar-widget" 16562 data-skip-node-update 16563 data-skip-subtree-update 16564 ></div> 16565 16566 <p class="bp-cart-drawer__taxes">Taxes, Shipping, and Discounts calculated at checkout.</p><form id="CartDrawer-Form" action="/cart" method="post" novalidate class="bp-cart-drawer__form"><button 16567 type="submit" 16568 name="checkout" 16569 class="bp-cart-drawer__btn bp-cart-drawer__btn--primary" 16570 data-narvar-feature="CartCheckoutButton" 16571 data-skip-node-update 16572 data-skip-subtree-update 16573 > 16574 Checkout 16575 </button> 16576 <div 16577 id="narvar-cart-drawer-dp-buttons" 16578 class="bp-cart-drawer__narvar-buttons" 16579 data-skip-node-update 16580 data-skip-subtree-update 16581 ></div> 16582 </form> 16583 <a href="/cart" class="bp-cart-drawer__view-bag">View Bag</a> 16584 </footer> 16585 </dialog> 16586</bp-cart-drawer> 16587 16588<style data-shopify> 16589 /* ===== BP Cart Drawer ===== */ 16590 bp-cart-drawer { display: contents; } 16591 16592 /* Trigger (icon button in header) â inherits header sizing. */ 16593 .bp-cart-drawer__trigger { 16594 position: relative; 16595 background: transparent; 16596 border: 0; 16597 padding: 0; 16598 cursor: pointer; 16599 color: var(--color-foreground); 16600 display: inline-flex; 16601 align-items: center; 16602 justify-content: center; 16603 width: 44px; 16604 height: 44px; 16605 } 16606 .bp-cart-drawer__trigger svg { width: 24px; height: 24px; display: block; } 16607 .bp-cart-drawer__trigger-count { 16608 position: absolute; 16609 top: 4px; 16610 right: 4px; 16611 min-width: 18px; 16612 height: 18px; 16613 padding: 0 5px; 16614 /* 18Ã18 circular notification badge â kept as a circle (50%) 16615 rather than squared off per the "no rounded corners" sweep 16616 since notification bubbles are conventionally circular and 16617 9px on an 18Ã18 is geometrically a circle anyway. */ 16618 border-radius: 50%; 16619 background: #EBAE55; 16620 /* Dark ink on the gold badge â white (#fff) on #EBAE55 is only 1.96:1, 16621 failing WCAG AA (QA). #171609 on the gold clears AA comfortably. The 16622 larger .bp-cart-drawer__bubble already uses this. */ 16623 color: var(--bp-color-text); 16624 font-family: var(--bp-font-body); 16625 font-size: 11px; 16626 font-weight: 500; 16627 display: inline-flex; 16628 align-items: center; 16629 justify-content: center; 16630 } 16631 .bp-cart-drawer__trigger-count[hidden] { display: none; } 16632 16633 /* Dialog. */ 16634 .bp-cart-drawer__dialog { 16635 position: fixed; 16636 inset: 0 0 0 auto; 16637 margin: 0; 16638 padding: 0; 16639 border: 0; 16640 width: calc(100vw / 3); 16641 min-width: 480px; 16642 max-width: 100vw; 16643 height: 100dvh; 16644 max-height: 100dvh; 16645 background: var(--bp-color-bg-page, #FEFFFA); 16646 color: var(--bp-color-text); 16647 box-shadow: -8px 0 30px rgb(0 0 0 / 0.12); 16648 overflow: hidden; 16649 font-family: var(--bp-font-body); 16650 z-index: 9999; 16651 } 16652 .bp-cart-drawer__dialog[open] { 16653 display: flex; 16654 flex-direction: column; 16655 animation: bpDrawerSlideIn 0.25s cubic-bezier(0.4, 0, 0.2, 1) forwards; 16656 } 16657 .bp-cart-drawer__dialog.is-closing { 16658 animation: bpDrawerSlideOut 0.2s cubic-bezier(0.4, 0, 0.2, 1) forwards; 16659 } 16660 .bp-cart-drawer__dialog::backdrop { background: rgb(0 0 0 / 0.3); } 16661 @media (max-width: 749px) { 16662 .bp-cart-drawer__dialog { width: 100vw; min-width: 0; } 16663 /* Tighter footer on mobile (QA 7/23, Brooke â Figma parity). */ 16664 .bp-cart-drawer__foot { padding: 16px !important; gap: 4px !important; } 16665 } 16666 16667 /* Header. */ 16668 .bp-cart-drawer__header { 16669 flex: 0 0 auto; 16670 display: flex; 16671 align-items: center; 16672 justify-content: space-between; 16673 padding: 20px 24px; 16674 border-bottom: 1px solid rgba(23, 22, 9, 0.08); 16675 background: var(--bp-color-bg-page, #FEFFFA); 16676 } 16677 .bp-cart-drawer__heading { 16678 margin: 0; 16679 display: inline-flex; 16680 align-items: center; 16681 gap: 12px; 16682 color: var(--bp-color-text); 16683 font-family: var(--bp-font-display); 16684 font-size: 28px; 16685 font-weight: 300; 16686 line-height: 120%; 16687 letter-spacing: -0.56px; 16688 } 16689 .bp-cart-drawer__bubble { 16690 /* Sunset pill (QA 7/23, Brooke â was 32px circle, white text) */ 16691 display: flex; 16692 width: 40px; 16693 padding: 10px 0; 16694 justify-content: center;
16695 align-items: center; 16696 border-radius: 100px; 16697 background: #EBAE55; 16698 color: var(--bp-color-text); 16699 text-align: center; 16700 /* Body / Semi / Small */ 16701 font-family: var(--bp-font-body); 16702 font-size: 14px; 16703 font-weight: 660; 16704 line-height: 145%; 16705 letter-spacing: -0.14px; 16706 } 16707 .bp-cart-drawer__close { 16708 background: transparent; 16709 border: 0; 16710 padding: 0; 16711 width: 32px; 16712 height: 32px; 16713 cursor: pointer; 16714 color: var(--bp-color-text); 16715 display: inline-flex; 16716 align-items: center; 16717 justify-content: center; 16718 } 16719 .bp-cart-drawer__close:hover { opacity: 0.6; } 16720 16721 .bp-cart-drawer__body { 16722 flex: 1 1 auto; 16723 overflow-y: auto; 16724 padding: 0 24px; 16725 } 16726 16727 /* Items list. */ 16728 .bp-cart-drawer__items { 16729 margin: 0; 16730 padding: 0; 16731 list-style: none; 16732 } 16733 16734 .bp-cart-drawer__empty { 16735 padding: 48px 0; 16736 text-align: center; 16737 color: var(--bp-color-body-copy); 16738 font-family: var(--bp-font-body); 16739 } 16740 .bp-cart-drawer__empty-cta { 16741 /* CTA / CTA Regular */ 16742 display: block; 16743 margin: 12px auto 0; 16744 background: transparent; 16745 border: 0; 16746 padding: 0; 16747 cursor: pointer; 16748 font-family: var(--bp-font-body); 16749 font-size: 17px; 16750 font-style: normal; 16751 font-weight: 500; 16752 line-height: normal; 16753 letter-spacing: -0.17px; 16754 leading-trim: both; 16755 text-edge: cap; 16756 color: var(--bp-color-text); 16757 text-decoration: underline; 16758 text-underline-offset: 3px; 16759 } 16760 16761 /* "Recommended for you" rail in the empty bag (QA 7/16). */ 16762 .bp-cart-drawer__recs { 16763 margin-top: 40px; 16764 text-align: left; 16765 } 16766 .bp-cart-drawer__recs-head { 16767 display: flex; 16768 align-items: center; 16769 justify-content: space-between; 16770 gap: 12px; 16771 margin-bottom: 16px; 16772 } 16773 .bp-cart-drawer__recs-title { 16774 font-family: var(--bp-font-display); 16775 font-size: 18px; 16776 font-weight: 300; 16777 letter-spacing: -0.36px; 16778 color: var(--bp-color-text); 16779 margin: 0; 16780 } 16781 .bp-cart-drawer__recs-arrows { display: inline-flex; gap: 12px; flex-shrink: 0; } 16782 .bp-cart-drawer__recs-arrow { 16783 /* 44px hit area â same chevron frame as Complete the Look / YMAL. */ 16784 width: 44px; 16785 height: 44px; 16786 background: transparent; 16787 border: 0; 16788 padding: 0; 16789 cursor: pointer; 16790 color: var(--bp-color-text); 16791 display: inline-flex; 16792 align-items: center; 16793 justify-content: center; 16794 transition: opacity 0.15s ease; 16795 } 16796 .bp-cart-drawer__recs-arrow:hover { opacity: 0.6; } 16797 /* Both buttons carry the right-pointing glyph; mirror the prev one. */ 16798 .bp-cart-drawer__recs-arrow[data-bp-recs-prev] svg { transform: scaleX(-1); } 16799 .bp-cart-drawer__recs-track { 16800 display: flex; 16801 gap: 12px; 16802 margin: 0; 16803 padding: 0; 16804 list-style: none; 16805 overflow-x: auto; 16806 scroll-snap-type: x mandatory; 16807 scrollbar-width: none; 16808 -ms-overflow-style: none; 16809 } 16810 .bp-cart-drawer__recs-track::-webkit-scrollbar { display: none; } 16811 /* Each rail item holds a full bp-product-card (collection eyebrow, swatches, 16812 price). Fixed width so the cards peek in the horizontal rail. */ 16813 .bp-cart-drawer__recs-card { 16814 /* % of the rail so ~2.3 cards show (two full + a peek of the third) 16815 regardless of drawer width, rather than three cramped ones (QA 7/27). */ 16816 flex: 0 0 42%; 16817 scroll-snap-align: start; 16818 } 16819 16820 /* Complete the Look. */ 16821 .bp-cart-drawer__ctl { padding: 24px 0 8px; } 16822 .bp-cart-drawer__ctl-head { 16823 display: flex; 16824 align-items: center; 16825 justify-content: space-between; 16826 margin-bottom: 12px; 16827 } 16828 .bp-cart-drawer__ctl-title { 16829 margin: 0; 16830 font-family: var(--bp-font-display); 16831 font-size: 20px; 16832 font-weight: 300; 16833 letter-spacing: -0.4px; 16834 color: var(--bp-color-text); 16835 } 16836 .bp-cart-drawer__ctl-arrows { display: inline-flex; gap: 12px; } 16837 .bp-cart-drawer__ctl-arrow { 16838 /* 44px hit area matches the Figma chevron frame (QA 7/23). */ 16839 width: 44px; 16840 height: 44px; 16841 background: transparent; 16842 border: 0; 16843 padding: 0; 16844 cursor: pointer; 16845 color: var(--bp-color-text); 16846 display: inline-flex; 16847 align-items: center; 16848 justify-content: center; 16849 transition: opacity 0.15s ease; 16850 } 16851 .bp-cart-drawer__ctl-arrow:hover { opacity: 0.6; } 16852 /* Both buttons carry the right-pointing glyph; mirror the prev one. */ 16853 .bp-cart-drawer__ctl-arrow[data-bp-ctl-prev] svg { transform: scaleX(-1); } 16854 16855 .bp-cart-drawer__ctl-track { 16856 display: flex; 16857 gap: 12px; 16858 overflow-x: auto; 16859 scroll-behavior: smooth; 16860 scroll-snap-type: x mandatory; 16861 -webkit-overflow-scrolling: touch; 16862 scrollbar-width: none; 16863 -ms-overflow-style: none; 16864 } 16865 .bp-cart-drawer__ctl-track::-webkit-scrollbar { display: none; }
16866 /* Scroll-progress indicator â ported from bp-product-carousel's 16867 .bp-pc__indicator (same thin bar + moving thumb). */ 16868 .bp-cart-drawer__ctl-indicator { 16869 margin-top: 12px; 16870 height: 3px; 16871 background: rgba(23, 22, 9, 0.12); 16872 position: relative; 16873 overflow: hidden; 16874 } 16875 .bp-cart-drawer__ctl-indicator::after { 16876 content: ''; 16877 position: absolute; 16878 top: 0; 16879 bottom: 0; 16880 left: calc(var(--bp-ctl-thumb-x, 0) * (100% - var(--bp-ctl-thumb-w, 100%))); 16881 width: var(--bp-ctl-thumb-w, 100%); 16882 background: var(--bp-color-text); 16883 transition: left 0.1s linear, width 0.1s linear; 16884 } 16885 .bp-cart-drawer__ctl-card { 16886 /* Fixed card width so cards don't stretch with the drawer/screen 16887 width (QA 7/23, Brooke): on wide screens a flex-fill basis made 16888 each card huge and the square cover image looked zoomed in. 16889 A fixed 175px basis keeps the card size constant and lets more 16890 cards fit per view as the drawer widens (2 on mobile â 3+ on 16891 desktop) instead of two ever-expanding cards. */ 16892 flex: 0 0 175px; 16893 max-width: 175px; 16894 min-width: 0; 16895 scroll-snap-align: start; 16896 position: relative; 16897 } 16898 .bp-cart-drawer__ctl-badge { 16899 position: absolute; 16900 top: 8px; 16901 left: 8px; 16902 z-index: 1; 16903 padding: 4px 8px; 16904 background: var(--bp-color-bg-cream); 16905 color: var(--bp-color-text); 16906 font-family: var(--bp-font-body); 16907 font-size: 11px; 16908 font-weight: 500; 16909 letter-spacing: 0.04em; 16910 } 16911 .bp-cart-drawer__ctl-image { 16912 aspect-ratio: 3 / 4; 16913 background: var(--bp-color-bg-cream); 16914 overflow: hidden; 16915 margin-bottom: 8px; 16916 } 16917 .bp-cart-drawer__ctl-image svg, 16918 .bp-cart-drawer__ctl-image img { 16919 width: 100%; 16920 height: 100%; 16921 object-fit: contain; 16922 display: block; 16923 } 16924 .bp-cart-drawer__ctl-name { 16925 margin: 0; 16926 font-family: var(--bp-font-body); 16927 font-size: 13px; 16928 color: var(--bp-color-text); 16929 } 16930 .bp-cart-drawer__ctl-price { 16931 margin: 4px 0 0; 16932 font-family: var(--bp-font-body); 16933 font-size: 13px; 16934 color: var(--bp-color-text); 16935 } 16936 .bp-cart-drawer__ctl-sale { color: var(--bp-color-promo); margin-right: 6px; } 16937 .bp-cart-drawer__ctl-compare { color: var(--bp-color-body-copy); text-decoration: line-through; } 16938 16939 /* Promo code input â hidden in the cart flyout per request (still 16940 available on the /cart page). */ 16941 .bp-cart-discount--drawer { display: none; } 16942 16943 /* Footer (sticky). */ 16944 .bp-cart-drawer__foot { 16945 flex: 0 0 auto; 16946 background: var(--bp-color-bg-page); 16947 /* Horizontal padding must match .bp-cart-drawer__body/__header (24px) so 16948 the subtotal + checkout buttons line up with the product images/titles 16949 above (QA 7/27 â footer was 16px, 8px left of the body edge, Figma 16950 has them flush). */ 16951 padding: 12px 24px 16px; 16952 border-top: 1px solid rgba(23, 22, 9, 0.08); 16953 display: flex; 16954 flex-direction: column; 16955 gap: 6px; 16956 } 16957 .bp-cart-drawer__subtotal { 16958 display: flex; 16959 justify-content: space-between; 16960 align-items: baseline; 16961 } 16962 .bp-cart-drawer__subtotal-label { 16963 /* Heading / H5 (was H4/24px) */ 16964 font-family: var(--bp-font-display); 16965 font-size: 20px; 16966 font-style: normal; 16967 font-weight: 300; 16968 line-height: 120%; 16969 letter-spacing: -0.48px; 16970 color: var(--bp-color-text); 16971 } 16972 .bp-cart-drawer__subtotal-value { 16973 /* Body / Regular / Medium */ 16974 font-family: var(--bp-font-body); 16975 font-size: 17px; 16976 font-style: normal; 16977 font-weight: 500; 16978 line-height: 145%; 16979 letter-spacing: -0.17px; 16980 color: var(--bp-color-text); 16981 text-align: right; 16982 } 16983 16984 /* Narvar mount slots. The footer's own flex `gap` already reserves 16985 even spacing around the widget whether it's empty or populated, so 16986 no margin here â an earlier `:not(:empty)` margin stacked on top of 16987 that gap once Narvar injected, doubling the space AND causing the 16988 surrounding rows to visibly jump apart as it populated (the opposite 16989 of the intended effect). The injected widget owns its own internal 16990 styling. */ 16991 .bp-cart-drawer__narvar-widget, 16992 .bp-cart-drawer__narvar-buttons { width: 100%; } 16993 .bp-cart-drawer__narvar-buttons:not(:empty) { margin-top: 12px; } 16994 .bp-cart-drawer__protection { 16995 background: var(--bp-color-bg-cream); 16996 padding: 14px 16px; 16997 cursor: pointer; 16998 display: block; 16999 } 17000 .bp-cart-drawer__protection-toggle { 17001 position: absolute; 17002 opacity: 0; 17003 pointer-events: none; 17004 } 17005 .bp-cart-drawer__protection-row { 17006 display: flex; 17007 justify-content: space-between; 17008 align-items: baseline; 17009 gap: 8px; 17010 } 17011 .bp-cart-drawer__protection-label { 17012 /* Heading / H6 */ 17013 font-family: var(--bp-font-display); 17014 font-size: 14px; 17015 font-weight: 300; 17016 line-height: 120%; 17017 letter-spacing: -0.28px; 17018 color: var(--bp-color-text); 17019 display: inline-flex; 17020 align-items: center; 17021 gap: 6px; 17022 } 17023 .bp-cart-drawer__protection-info { 17024 display: inline-flex; 17025 align-items: center; 17026 color: var(--bp-color-text); 17027 transform: translateY(1px); 17028 }
17029 .bp-cart-drawer__protection-price { 17030 /* Body / Book / Small */ 17031 font-family: var(--bp-font-body); 17032 font-size: 14px; 17033 font-weight: 400; 17034 line-height: 145%; 17035 letter-spacing: -0.14px; 17036 color: var(--bp-color-text); 17037 text-align: right; 17038 flex: 0 0 auto; 17039 } 17040 .bp-cart-drawer__protection-help { 17041 /* Body / Book / Tiny */ 17042 display: block; 17043 margin-top: 4px; 17044 font-family: var(--bp-font-body); 17045 font-size: 10px; 17046 font-weight: 400; 17047 line-height: 145%; 17048 letter-spacing: -0.1px; 17049 color: var(--bp-color-text); 17050 } 17051 17052 .bp-cart-drawer__taxes { 17053 /* Body / XSmall / Book */ 17054 margin: 0; 17055 font-family: var(--bp-font-body); 17056 font-size: 12px; 17057 font-weight: 400; 17058 line-height: 145%; 17059 letter-spacing: -0.12px; 17060 color: var(--bp-color-text); 17061 } 17062 17063 .bp-cart-drawer__form { margin: 0; } 17064 .bp-cart-drawer__btn { 17065 /* CTA / CTA Regular */ 17066 width: 100%; 17067 height: 52px; 17068 padding: 0 24px; 17069 border: 1px solid var(--bp-color-text); 17070 cursor: pointer; 17071 font-family: var(--bp-font-body); 17072 font-size: 17px; 17073 font-style: normal; 17074 font-weight: 500; 17075 line-height: normal; 17076 letter-spacing: -0.17px; 17077 leading-trim: both; 17078 text-edge: cap; 17079 transition: opacity 0.2s ease; 17080 } 17081 .bp-cart-drawer__btn--primary { 17082 background: var(--bp-color-text); 17083 color: var(--bp-color-cta-text); 17084 } 17085 .bp-cart-drawer__btn--primary:hover { opacity: 0.85; } 17086 /* Disabled checkout (empty / $0 subtotal): dimmed + non-interactive. */ 17087 .bp-cart-drawer__btn--disabled, 17088 .bp-cart-drawer__btn--disabled:hover { 17089 opacity: 0.45; 17090 pointer-events: none; 17091 cursor: not-allowed; 17092 } 17093 .bp-cart-drawer__btn--secondary { 17094 background: var(--bp-color-bg-page); 17095 color: var(--bp-color-text); 17096 } 17097 .bp-cart-drawer__btn--secondary:hover { background: var(--bp-color-bg-cream); } 17098 .bp-cart-drawer__view-bag { 17099 /* CTA / CTA Regular */ 17100 align-self: center; 17101 color: var(--bp-color-text); 17102 font-family: var(--bp-font-body); 17103 font-size: 14px; 17104 font-style: normal; 17105 font-weight: 500; 17106 line-height: normal; 17107 letter-spacing: -0.17px; 17108 leading-trim: both; 17109 text-edge: cap; 17110 text-decoration: underline; 17111 text-underline-offset: 3px; 17112 background: transparent; 17113 border: 0; 17114 padding: 0; 17115 cursor: pointer; 17116 } 17117 17118 @keyframes bpDrawerSlideIn { 17119 from { transform: translateX(100%); } 17120 to { transform: translateX(0); } 17121 } 17122 @keyframes bpDrawerSlideOut { 17123 from { transform: translateX(0); } 17124 to { transform: translateX(100%); } 17125 } 17126</style> 17127
17128<script type="module"> 17129 // --------------------------------------------------------------------------- 17130 // Narvar shipping-protection config (QA 7/22). Narvar's order-sync app embed 17131 // (shopify-app-block, block id 16157100011046449464) emits window.NarvarDPData 17132 // globally, resolving the delivery-protection product via all_products[handle]. 17133 // That hits Shopify's 20-UNIQUE-HANDLE all_products cap on product-heavy pages 17134 // (home/PLP render 20+ cards before the end-of-body embed runs), so its 17135 // variant_id/metafield come out BLANK there â `variant_id: ,` SyntaxError â 17136 // window.NarvarDPData never assigned â widget never renders. PDP/cart resolve 17137 // fewer handles, so Narvar's lookup succeeds there. (A Liquid source here fails 17138 // the SAME cap â confirmed â so these are hardcoded store constants instead.) 17139 // 17140 // ensureNarvarData() patches these into Narvar's OWN broken script on drawer 17141 // open and re-executes it, reusing Narvar's real cart_attributes + per-theme 17142 // settings verbatim. Narvar's own values win when present (the repair no-ops 17143 // when window.NarvarDPData is already set, e.g. PDP/cart). 17144 // 17145 // TODO (long-term): Narvar should source its DP variant/metafield from a shop 17146 // metafield / fixed variant ref (cap-proof), not all_products â then this can 17147 // be removed. If the DP product is ever recreated, update BP_NARVAR_DP_VARIANT 17148 // (or swap to a client-side fetch('/products/narvardeliveryprotection.js')). 17149 const BP_NARVAR_DP_VARIANT = 46514716934310; // narvardeliveryprotection variant 17150 const BP_NARVAR_DP_META_RAW = {"isRetailerActive":true,"US":{"version":9,"protectionType":"insurance","currency":"USD","minimumPremiumValue":3,"taxAdjustment":15,"l10ns":{"en":{"title":"Shipping Protection","description":"Protect your purchase from loss, damage, and theft with InsureShield.","tnc":"","dpLogoUrl":"https://assets.narvar.com/delivery-protection/ups-insureshield.png","privacyPolicyUrl":"","tosUrl":"","footerText":"By selecting coverage, you acknowledge you have read and agree to the","learnMoreText":"Program Terms and Conditions.","learnMoreUrl":"https://www.insureshield.com/content/terms-and-conditions/Narvar_TermsAndConditions.pdf","zeroPremiumText":"Your purchase includes complimentary coverage paid by Boston Proper for lost, damaged, and stolen packages with InsureShield.","merchantName":"Boston Proper"}},"eligibilityRule":{"shippingCountries":["US"],"maxOrderValue":2000},"premiumRule":{"condition":{"excludedCategories":[],"excludedShippingCountries":[]},"premium":{"type":"percentage","value":3}},"zeroPremiumRule":{"condition":{"customerTags":[""]}},"serviceFee":0,"insuranceType":"upsc"}}; 17151 // Narvar's own code does `JSON.parse(metafield)`, so it must be the JSON STRING. 17152 const BP_NARVAR_DP_METAFIELD = JSON.stringify(BP_NARVAR_DP_META_RAW); 17153 17154 // Patch Narvar's blank config on home/PLP (see the full rationale on 17155 // BpCartDrawer.ensureNarvarData). Match Narvar's ASSIGNMENT script (not our 17156 // own references to the global â ours renders earlier and would self-match),
17157 // fill the blank variant_id/metafield, and re-execute so window.NarvarDPData 17158 // is set. Returns true if the global is now defined. No-op on PDP/cart, where 17159 // Narvar populates it itself. 17160 function bpEnsureNarvarData() { 17161 if (window.NarvarDPData) return true; 17162 const scripts = document.querySelectorAll('script:not([src])'); 17163 for (let i = 0; i < scripts.length; i++) { 17164 const txt = scripts[i].textContent || ''; 17165 if (!/window\.NarvarDPData\s*=\s*\{/.test(txt)) continue; // Narvar's assignment only 17166 if (!/variant_id:\s*,/.test(txt)) break; // already populated â nothing to fix 17167 try { 17168 const fixed = txt 17169 .replace(/variant_id:\s*,/, 'variant_id: ' + String(BP_NARVAR_DP_VARIANT) + ',') 17170 // metafield must be the JSON STRING Narvar's `JSON.parse` expects. 17171 .replace(/metafield:\s*JSON\.stringify\(\s*\)/, 'metafield: ' + JSON.stringify(BP_NARVAR_DP_METAFIELD)); 17172 // eslint-disable-next-line no-new-func 17173 new Function(fixed)(); // runs in global scope; assigns window.NarvarDPData 17174 } catch (e) { /* leave undefined */ } 17175 break; 17176 } 17177 return !!window.NarvarDPData; 17178 } 17179 17180 // Run at MODULE LOAD. This module script (in the header) executes 17181 // after DOM parse but BEFORE Narvar's deferred widget bundle at end-of-body 17182 // (deferred scripts run in document order). So setting a valid
17183 // window.NarvarDPData here means Narvar's own bundle reads it and renders the 17184 // widget natively â no drawer-open trigger or narvarCartWidgetInit needed. 17185 // Narvar's inline config script (non-module, ran during parse) has already 17186 // executed by now, so its blank/broken output is present to patch. 17187 try { bpEnsureNarvarData(); } catch (e) { /* non-fatal */ } 17188 17189 // ----- Narvar checkout-CTA casing + whole-dollar price zeros ----- 17190 // Narvar's app injects its checkout buttons ("Checkout", "Checkout Without 17191 // Shipping Protection") with `text-transform: uppercase` (its own 17192 // custom_css). The BP stylesheet override (bp-fonts) couldn't reliably 17193 // out-rank it â load order / re-injection let Narvar's uppercase win, so 17194 // the CTAs read ALL CAPS instead of the "first letter cap" the design 17195 // calls for. Fix it inline with !important, which beats any stylesheet 17196 // rule, the moment each button appears. `capitalize` == "first letter cap" 17197 // and is safe for a title- or lower-cased source label. One observer here 17198 // covers BOTH surfaces: the drawer buttons (CartDrawer-*) and the cart 17199 // page buttons (CartPage-*), since this drawer renders on every page. 17200 // 17201 // Same observer also strips ".00" off whole-dollar prices Narvar renders 17202 // itself (site-wide convention â see bp-htwi's stripPriceZeros): the 17203 // checkout button label ("Checkout - $42.00" once Narvar swaps in the 17204 // protection total) and the Shipping Protection widget price. The 17205 // subtotal is handled differently â see the NARVAR_WIDGET_SELECTORS 17206 // comment below for why. 17207 (function () { 17208 if (window.__bpNarvarCtaCasingWired) return; 17209 window.__bpNarvarCtaCasingWired = true; 17210 var IDS = [ 17211 'CartDrawer-Checkout-DP-With', 'CartDrawer-Checkout-DP-Without', 17212 'CartPage-Checkout-DP-With', 'CartPage-Checkout-DP-Without' 17213 ]; 17214 // The Narvar Shipping Protection mount is a live third-party React app 17215 // that can re-render its internal DOM continuously (toggle state, 17216 // animations). A persistent subtree+characterData observer on it would 17217 // re-run a full text-node walk on every one of those internal 17218 // mutations too, which spirals into a tight synchronous loop and hangs 17219 // the tab â keep this one to a one-shot strip per page-wide childList 17220 // mutation instead (QA 7/27, first crash). 17221 // 17222 // The subtotal element (`.totals__subtotal-value`, Narvar's configured 17223 // `subtotal_selector`) is NOT stripped by this script at all anymore â 17224 // Narvar appears to actively watch + re-assert that exact selector, so 17225 // a characterData observer on it fought Narvar's own logic in an 17226 // infinite write/react loop and froze the tab too (QA 7/27, second 17227 // crash). Narvar now gets its OWN hidden decoy node to own; the visible 17228 // subtotal shoppers see is a separate span we fully control (see 17229 // bp-cart-drawer.liquid's subtotal markup / bp-cart-page.liquid's twin). 17230 var NARVAR_WIDGET_SELECTORS = ['#narvar-cart-drawer-dp-widget', '#narvar-cart-page-dp-widget']; 17231 // Text-node-only walk (mirrors bp-htwi's stripPriceZeros) so element 17232 // structure (icons, nested spans) is left untouched. Idempotent â a 17233 // stripped "$39" has no ".00" left to match. 17234 function stripDotZero(root) { 17235 if (!root) return; 17236 var walker = document.createTreeWalker(root, NodeFilter.SHOW_TEXT); 17237 var node; 17238 while ((node = walker.nextNode())) { 17239 if (node.nodeValue && node.nodeValue.indexOf('.00') !== -1) { 17240 node.nodeValue = node.nodeValue.replace(/(\d)\.00\b/g, '$1'); 17241 } 17242 } 17243 } 17244 // The page-wide observer below only watches childList/attributes, so it 17245 // catches an element FIRST APPEARING but not a later direct text-node 17246 // write to it. Give small static price elements (checkout buttons, 17247 // subtotal) their own characterData-watching observer so those later 17248 // writes get caught too. 17249 var watched = new WeakSet(); 17250 function watchPriceText(el) { 17251 if (!el) return; 17252 stripDotZero(el); 17253 if (watched.has(el)) return; 17254 watched.add(el);
17255 try { 17256 new MutationObserver(function () { stripDotZero(el); }) 17257 .observe(el, { childList: true, subtree: true, characterData: true }); 17258 } catch (e) { /* observer unsupported */ } 17259 } 17260 var scheduled = false; 17261 function fixCasing() { 17262 scheduled = false; 17263 for (var i = 0; i < IDS.length; i++) { 17264 var btn = document.getElementById(IDS[i]); 17265 if (!btn) continue; 17266 if (btn.style.textTransform !== 'capitalize') { 17267 btn.style.setProperty('text-transform', 'capitalize', 'important'); 17268 } 17269 watchPriceText(btn); 17270 } 17271 for (var k = 0; k < NARVAR_WIDGET_SELECTORS.length; k++) { 17272 stripDotZero(document.querySelector(NARVAR_WIDGET_SELECTORS[k])); 17273 } 17274 // Site convention: no ".00" on whole-dollar amounts (matches the drawer 17275 // subtotal's formatMoney + the Liquid `money | replace: '.00', ''`). 17276 // Narvar's #checkout-price span (subtotal + protection, shown on the 17277 // DP-With button now that #307 no longer hides it) renders "$40.00" â 17278 // strip the trailing .00 so it reads "$40" like the rest of the cart (QA 17279 // 7/27, Dragana). Guarded on a .00 match so our own edit doesn't loop the 17280 // observer. querySelectorAll (not getElementById) in case Narvar reuses 17281 // the id across the flyout + cart-page buttons. 17282 var priceEls = document.querySelectorAll('[id="checkout-price"]'); 17283 for (var k = 0; k < priceEls.length; k++) { 17284 var txt = priceEls[k].textContent; 17285 if (txt && /\.00(\D|$)/.test(txt)) { 17286 priceEls[k].textContent = txt.replace(/\.00(\D|$)/g, '$1'); 17287 } 17288 } 17289 } 17290 function schedule() { 17291 if (scheduled) return; 17292 scheduled = true; 17293 requestAnimationFrame(fixCasing); 17294 } 17295 // Narvar injects/mutates these buttons asynchronously (on load for the 17296 // cart page, on drawer open for the drawer), so watch for it. `style` 17297 // is intentionally NOT in attributeFilter â our own inline set would 17298 // re-trigger this page-wide pass (the per-element observers above cover 17299 // the text-content case that needed characterData). 17300 try { 17301 new MutationObserver(schedule).observe(document.documentElement, { 17302 childList: true, subtree: true, attributes: true, attributeFilter: ['id', 'class'] 17303 }); 17304 } catch (e) { /* observer unsupported â the initial pass below still runs */ } 17305 fixCasing(); 17306 })(); 17307 17308 // ----- Shipping Protection loading skeleton (QA 7/27, Dragana) ----- 17309 // The SP widget is injected by Narvar's async bundle a few seconds after the 17310 // flyout opens / the cart page loads. A shimmer skeleton holds the box shape 17311 // until then (CSS `:has(.narvar-cart-widget-card)` hides it the instant the 17312 // real box renders). This JS is the belt-and-braces layer: it hides each 17313 // skeleton the moment its sibling mount gets a child (covers browsers without 17314 // `:has()`), and after a grace period hides any skeleton whose mount is still
17315 // empty â Narvar renders NO widget for ineligible carts (non-US / over the 17316 // max order value), and a lingering loading box would be misleading. Covers 17317 // both surfaces (this drawer renders on every page, incl. /cart). Supersedes 17318 // the earlier reserve-space approach (#307). 17319 (function () { 17320 if (window.__bpSpSkeletonWired) return; 17321 window.__bpSpSkeletonWired = true; 17322 function hide(skel) { if (skel) skel.classList.add('bp-sp-skeleton--hidden'); } 17323 function mountFor(skel) { 17324 // The Narvar mount is the skeleton's next element sibling. 17325 var el = skel.nextElementSibling; 17326 return (el && /narvar-cart-(drawer|page)-dp-widget/.test(el.id || '')) ? el : null; 17327 } 17328 var skels = document.querySelectorAll('[data-bp-sp-skeleton]'); 17329 for (var i = 0; i < skels.length; i++) { 17330 (function (skel) { 17331 var mount = mountFor(skel); 17332 if (!mount) { hide(skel); return; } 17333 if (mount.firstChild) { hide(skel); return; } // already populated 17334 try { 17335 var mo = new MutationObserver(function () { 17336 if (mount.firstChild) { hide(skel); mo.disconnect(); } 17337 }); 17338 mo.observe(mount, { childList: true }); 17339 } catch (e) { /* no observer â the timeout below still resolves it */ } 17340 })(skels[i]); 17341 } 17342 // Grace period: drop any skeleton Narvar never filled (ineligible carts). 17343 window.setTimeout(function () { 17344 var s = document.querySelectorAll('[data-bp-sp-skeleton]'); 17345 for (var j = 0; j < s.length; j++) { 17346 var m = s[j].nextElementSibling; 17347 if (!m || !m.firstChild) hide(s[j]); 17348 } 17349 }, 6000); 17350 })(); 17351 17352 // ----- Line item delegation + cart mutation helper ----- 17353 // Registered once per page from the drawer (which always renders), 17354 // not from the line item snippet (which doesn't render when the cart 17355 // is empty, leaving the empty-cart-then-add path with no listeners). 17356 if (!window.__bpCartLineWired) { 17357 window.__bpCartLineWired = true; 17358 17359 document.addEventListener('click', async (e) => { 17360 const line = e.target.closest('[data-bp-cart-line]'); 17361 if (!line) return; 17362 const key = line.dataset.lineKey; 17363 17364 const stepBtn = e.target.closest('[data-bp-qty-step]'); 17365 const removeBtn = e.target.closest('[data-bp-remove]'); 17366 const saveBtn = e.target.closest('[data-bp-save]'); 17367 17368 if (stepBtn) { 17369 const input = line.querySelector('[data-bp-qty-input]'); 17370 const next = Math.max(1, (parseInt(input.value, 10) || 1) + parseInt(stepBtn.dataset.bpQtyStep, 10)); 17371 await window.bpCartChange(key, next); 17372 return; 17373 } 17374 if (removeBtn) { 17375 await window.bpCartChange(key, 0); 17376 return; 17377 } 17378 if (saveBtn) { 17379 // Atlas parity â route the save through window.handleSaveForLater 17380 // (bp-swym-helpers). That fn calls Swym's AddToSmartWishlist, 17381 // removes the line from cart, updates the customer-scoped 17382 // local mirror (`save-for-later-items`), and dispatches the 17383 // wishlist + cart events the drawer + saved-drawer listen to. 17384 try { 17385 const cart = await fetch('/cart.js', { cache: 'no-store', credentials: 'same-origin' }).then((r) => r.json()); 17386 const item = cart.items.find((i) => i.key === key); 17387 if (item) { 17388 const product = { 17389 product_id: item.product_id, 17390 id: item.product_id, 17391 handle: item.handle, 17392 variant_id: item.variant_id, 17393 variant_title: item.variant_title, 17394 variant_price: item.final_price, 17395 variant_compare_at_price: item.original_price, 17396 title: item.product_title, 17397 image: item.image, 17398 quantity: item.quantity, 17399 properties: item.properties, 17400 sku: item.sku, 17401 variant_sku: item.sku, 17402 line_key: item.key, 17403 tags: item.tags 17404 }; 17405 if (typeof window.handleSaveForLater === 'function') { 17406 await window.handleSaveForLater({ preventDefault: () => {} }, product); 17407 } else { 17408 // Fallback if bp-swym-helpers hasn't loaded yet â drop the 17409 // line and stash a minimal snapshot under the legacy key 17410 // so the next page load can migrate it. 17411 const saved = JSON.parse(localStorage.getItem('bp.saved_for_later') || '[]'); 17412 saved.unshift({ 17413 variantId: item.variant_id, productId: item.product_id, key: item.key, 17414 title: item.product_title, variantTitle: item.variant_title, 17415 image: item.image, price: item.final_price, url: item.url, 17416 quantity: item.quantity, properties: item.properties, savedAt: Date.now() 17417 }); 17418 localStorage.setItem('bp.saved_for_later', JSON.stringify(saved.slice(0, 50))); 17419 document.dispatchEvent(new CustomEvent('bp:saved:updated')); 17420 await window.bpCartChange(key, 0); 17421 } 17422 saveBtn.classList.add('is-saved'); 17423 } 17424 } catch (err) { console.error('[bp-cart] save-for-later failed', err); } 17425 return; 17426 } 17427 }); 17428 17429 document.addEventListener('change', (e) => { 17430 const input = e.target.closest('[data-bp-qty-input]'); 17431 if (!input) return; 17432 const line = input.closest('[data-bp-cart-line]'); 17433 if (!line) return; 17434 const next = Math.max(1, parseInt(input.value, 10) || 1); 17435 window.bpCartChange(line.dataset.lineKey, next); 17436 }); 17437 17438 // Cart mutation helper â POSTs to /cart/change.js, then dis
17438patches 17439 // `cart:updated` with the new cart in event.detail so the drawer 17440 // updates line items in place without a page reload. 17441 window.bpCartChange = async (key, quantity) => { 17442 try { 17443 const res = await fetch('/cart/change.js', { 17444 method: 'POST', 17445 headers: { 'Content-Type': 'application/json', Accept: 'application/json' }, 17446 body: JSON.stringify({ id: key, quantity }), 17447 cache: 'no-store', 17448 credentials: 'same-origin' 17449 }); 17450 if (!res.ok) throw new Error('HTTP ' + res.status); 17451 const cart = await res.json(); 17452 document.dispatchEvent(new CustomEvent('cart:updated', { detail: { cart } })); 17453 return cart; 17454 } catch (e) { console.error('[bp-cart] change failed', e); } 17455 }; 17456 } 17457 17458 if (!customElements.get('bp-cart-drawer')) { 17459 class BpCartDrawer extends HTMLElement { 17460 connectedCallback() { 17461 this.dialog = this.querySelector('[data-bp-cart-dialog]'); 17462 this.opener = this.querySelector('[data-bp-cart-open]'); 17463 if (!this.dialog || !this.opener) return; 17464 17465 this.opener.addEventListener('click', (e) => { 17466 e.preventDefault(); 17467 this.open(); 17468 }); 17469 17470 this.dialog.addEventListener('click', (e) => { 17471 if (e.target.closest('[data-bp-cart-close]')) { 17472 e.preventDefault(); 17473 this.close(); 17474 return; 17475 } 17476 // Narvar's Shipping Protection info icon opens an info surface that 17477 // would be clipped by the drawer â close the drawer on click so it 17478 // shows (Narvar's own handler still runs; we don't preventDefault). 17479 if (e.target.closest('.narvar-cart-widget-info-icon')) { 17480 this.close(); 17481 return; 17482 } 17483 if (e.target === this.dialog) this.close(); 17484 }); 17485 17486 this.dialog.addEventListener('cancel', (e) => { 17487 e.preventDefault(); 17488 this.close(); 17489 }); 17490 17491 // Complete-the-Look arrow nav â same pattern as bp-cart-page.liquid. 17492 // Track + buttons are server-rendered empty/present up front (see 17493 // markup above), so wiring at connect time is safe even though the 17494 // track itself is populated later by bp-cart-recommendations.liquid. 17495 const ctlTrack = this.querySelector('[data-bp-ctl-track]'); 17496 const ctlPrev = this.querySelector('[data-bp-ctl-prev]'); 17497 const ctlNext = this.querySelector('[data-bp-ctl-next]'); 17498 if (ctlTrack && ctlPrev && ctlNext) { 17499 const ctlStep = () => Math.max(160, ctlTrack.clientWidth / 4); 17500 ctlPrev.addEventListener('click', () => ctlTrack.scrollBy({ left: -ctlStep(), behavior: 'smooth' })); 17501 ctlNext.addEventListener('click', () => ctlTrack.scrollBy({ left: ctlStep(), behavior: 'smooth' })); 17502 } 17503 17504 // "Recommended for you" (empty-bag) arrow nav â same pattern. The rail is 17505 // a horizontal scroll track with no drag affordance on desktop, so the 17506 // arrows are the way to page through it (QA 8/05, Brooke). 17507 // "Recommended for you" (empty-bag) arrow nav â DELEGATED on the drawer 17508 // so it fires regardless of when the rail is rendered/re-rendered, and 17509 // can't be missed by a connect-time query. `scroll-snap-type: mandatory` 17510 // snaps a programmatic SMOOTH scroll straight back to 0, so we scroll 17511 // instantly (which lands + snaps cleanly) and page by ~2 whole cards so 17512 // the target sits on a snap point (QA 8/05, Brooke). 17513 this.addEventListener('click', (e) => { 17514 const t = e.target; 17515 const dir = (t.closest && t.closest('[data-bp-recs-next]')) ? 1 17516 : (t.closest && t.closest('[data-bp-recs-prev]')) ? -1 : 0; 17517 if (!dir) return; 17518 e.preventDefault(); 17519 const track = this.querySelector('.bp-cart-drawer__recs-track'); 17520 if (!track) return; 17521 const card = track.querySelector('.bp-cart-drawer__recs-card'); 17522 const pitch = card ? card.getBoundingClientRect().width + 12 : track.clientWidth * 0.42; 17523 track.scrollBy({ left: dir * Math.round(pitch) * 2 }); 17524 }); 17525 17526 // Listen for cart updates from anywhere (PDP add, quick-add, etc.). 17527 // BP-internal: `cart:updated` carries the cart in event.detail.cart.
17528 document.addEventListener('cart:updated', (e) => { 17529 if (e.detail && e.detail.cart) this.applyCart(e.detail.cart); 17530 else this.refresh(); 17531 }); 17532 document.addEventListener('product:added', (e) => { 17533 this.open(); 17534 if (e.detail && e.detail.cart) this.applyCart(e.detail.cart); 17535 else this.refresh(); 17536 }); 17537 // Horizon-native: `cart:update` is dispatched by product-form on PDP 17538 // adds, by quick-add, by upsells, etc. Cart object lives in 17539 // event.detail.resource. Open the drawer + sync state. 17540 document.addEventListener('cart:update', (e) => { 17541 const detail = e.detail || {}; 17542 if (detail.data && detail.data.didError) return; 17543 // Source may be 'product-form-component' (PDP), quick-add, or 17544 // cart-line edits. Auto-open only on add-to-cart-style sources. 17545 const source = detail.data && detail.data.source; 17546 const isAdd = source === 'product-form-component' || 17547 source === 'quick-add-component' || 17548 source === 'product-card-component'; 17549 if (isAdd) this.open(); 17550 if (detail.resource) this.applyCart(detail.resource); 17551 else this.refresh(); 17552 }); 17553 // Discount Kit (Helium) dispatches this when it adds/removes a 17554 // GWP line item in the cart. Mirror Atlas's CartFlyout behavior: 17555 // open the drawer so the customer sees the change, then refresh. 17556 document.addEventListener('discount_kit:cart_changed', () => { 17557 this.open(); 17558 this.refresh(); 17559 }); 17560 17561 // Bulletproofing (QA 7/16, Critical): never allow checkout when there 17562 // are no REAL products â only the Narvar shipping-protection add-on can 17563 // linger ($3 orphan). Capture phase so it beats Narvar's own submit 17564 // handler on the injected DP buttons (they share this form). Falls 17565 // through to a full refresh, which clears any orphaned DP line. 17566 const checkoutForm = this.querySelector('#CartDrawer-Form'); 17567 if (checkoutForm) { 17568 checkoutForm.addEventListener('submit', (e) => { 17569 if (this._realCount === 0) { 17570 e.preventDefault(); 17571 e.stopImmediatePropagation(); 17572 this.#showEmptyUI(); 17573 this.refresh(); 17574 } 17575 }, { capture: true }); 17576 } 17577 17578 // Bundle upsell ("Complete the look") is rendered by the shared global 17579 // helper (snippets/bp-cart-bundle-upsell.liquid â window.bpBundleUpsell), 17580 // which self-wires off bp:cart-drawer:opened / bp:cart-lines:rendered / 17581 // cart:updated â no per-surface code here. 17582 // 17583 // Narvar config repair is intentionally NOT run at page load. Doing so 17584 // (and proactively calling narvarCartWidgetInit) double-fired Narvar's 17585 // own auto-init on the PDP and broke its render there. The repair runs 17586 // on drawer open instead (remountNarvar â ensureNarvarData), which is 17587 // the only place the drawer widget is actually needed and leaves 17588 // Narvar's page-load path untouched. 17589 } 17590 17591 open() { 17592 if (this.dialog.open) return; 17593 // Scroll suppression â freeze the page so the only scrollable area is 17594 // the cart body. `<html>` is not the scroller here and showModal() 17595 // alone doesn't stop the page behind the backdrop from scrolling, so 17596 // toggling html overflow did nothing. Pin the body in place at its 17597 // current offset (position:fixed) â the exact technique Horizon's 17598 // dialog.js uses. Restored + scroll position re-applied on close. 17599 const existingTop = document.body.style.top; 17600 const existingPosition = document.body.style.position; 17601 if (existingPosition === 'fixed' && existingTop) { 17602 this._lockScrollY = Math.abs(parseInt(existingTop, 10)) || 0; 17603 } else { 17604 this._lockScrollY = window.scrollY; 17605 } 17606 17607 document.body.style.width = '100%'; 17608 document.body.style.position = 'fixed'; 17609 document.body.style.top = `-${this._lockScrollY}px`;
17610 17611 if (typeof this.dialog.showModal === 'function') this.dialog.showModal(); 17612 else this.dialog.setAttribute('open', ''); 17613 // Notify the cart Complete-the-Look snippet (and anyone else 17614 // watching) that the drawer is now visible. The CTL snippet 17615 // re-syncs on this so cards appear the first time the drawer 17616 // opens, even when the cart was already populated server-side 17617 // and no cart mutation event has fired since. 17618 document.dispatchEvent(new CustomEvent('bp:cart-drawer:opened')); 17619 17620 // Narvar shipping-protection widget. Narvar's cart-widget script is 17621 // loaded on every page, but it only AUTO-renders on pages it detects as 17622 // a cart context (looks for `<cart-drawer>` + a `/cart` URL). Lana's 17623 // drawer is `<bp-cart-drawer>`, so on home / PLP the widget never 17624 // appeared. remountNarvar() calls Narvar's exposed init on open so it 17625 // renders into the drawer mount anywhere (QA 7/21). 17626 this.remountNarvar(); 17627 } 17628 17629 ensureNarvarData() { 17630 // ROOT CAUSE (QA 7/21): Narvar's app-embed injects an end-of-body 17631 // script that sets window.NarvarDPData with variant_id and metafield 17632 // printed from the Liquid `product` object plus a static settings block. 17633 // On non-product pages (home, PLP) `product` is nil, so it renders 17634 // `variant_id: ,` (a bare comma â SyntaxError that aborts the whole 17635 // script before the assignment) AND `metafield: JSON.stringify()` 17636 // (evaluates to undefined). window.NarvarDPData is left unset, so the 17637 // widget has no config and never renders. On PDP/cart both are populated 17638 // â valid JS â it works. That is the "PDP+cart yes, home+PLP no" split. 17639 // 17640 // Both blank values are STORE CONSTANTS â the narvardeliveryprotection 17641 // product's variant + its narvar.dp_config metafield (the InsureShield 17642 // config the widget needs for its title/description/premium). We source 17643 // them in Liquid (BP_NARVAR_DP_VARIANT / BP_NARVAR_DP_METAFIELD) and 17644 // patch them into Narvar's OWN broken script, then re-execute it so the 17645 // rest â Narvar's real cart_attributes + per-theme settings â is reused 17646 // verbatim (we can't recompute the cart_attributes quote/aid blob). 17647 // 17648 // Narvar's own values take precedence: this no-ops when NarvarDPData is 17649 // already set (PDP/cart), and the `variant_id: ,` guard skips any valid 17650 // (populated) script. Runs on drawer OPEN only â a load-time re-run that 17651 // also called narvarCartWidgetInit double-fired Narvar's auto-init on the 17652 // PDP and broke its render, so we leave Narvar's page-load path alone. 17653 // Delegates to the module-level bpEnsureNarvarData (already run once at 17654 // module load). Re-invoked on drawer open as a belt-and-suspenders in 17655 // case Narvar's config script landed after our load-time pass. 17656 return bpEnsureNarvarData(); 17657 } 17658 17659 remountNarvar() { 17660 // Narvar's cart-widget (React) observes the document and renders its 17661 // shipping-protection widget into #narvar-cart-drawer-dp-widget when it 17662 // detects the mount + a valid config. It auto-renders on the PDP + /cart 17663 // but not home/PLP â because window.NarvarDPData is unset there (see 17664 // ensureNarvarData). Repair the config first, then poll its exposed init 17665 // (window.narvarCartWidgetInit) so it renders into the (existing) drawer 17666 // mount on open, beating the async-load race ("open immediately = empty, 17667 // wait ~2s = loads"). 17668 // 17669 // NON-DESTRUCTIVE: do NOT remove/replace the mount. Removing it yanked 17670 // the node out from under Narvar mid-render and broke the PDP/cart cases 17671 // that already worked â leave the mount in place and only invoke init. 17672 this.ensureNarvarData(); 17673 const filled = () => { 17674 const w = this.querySelector('#narvar-cart-drawer-dp-widget'); 17675 return !w || w.children.length > 0; 17676 }; 17677 if (filled()) return; 17678 17679 let tries = 0; 17680 const MAX_TRIES = 24; // 24 Ã 250ms â 6s 17681 const tick = () => { 17682 if (filled() || !this.dialog.open) return; // done, or drawer closed 17683 if (typeof window.narvarCartWidgetInit === 'function') { 17684 try { window.narvarCartWidgetInit(); } catch (e) { /* keep retrying */ } 17685 } 17686 if (++tries < MAX_TRIES) setTimeout(tick, 250); 17687 }; 17688 requestAnimationFrame(tick); // start polling next frame 17689 } 17690 17691 close() { 17692 if (this._closing || !this.dialog.open) return; 17693 this._closing = true; 17694 this.dialog.classList.add('is-closing'); 17695 const onEnd = () => { 17696 this.dialog.classList.remove('is-closing'); 17697 this._closing = false;
17698 try { this.dialog.close(); } catch (_) { this.dialog.removeAttribute('open'); } 17699 // Release the body scroll lock and restore the pre-open scroll 17700 // position (position:fixed had reset it to 0 visually via top offset). 17701 document.body.style.width = ''; 17702 document.body.style.position = ''; 17703 document.body.style.top = ''; 17704 if (this._lockScrollY != null) { 17705 window.scrollTo({ top: this._lockScrollY, behavior: 'instant' }); 17706 this._lockScrollY = null; 17707 } 17708 }; 17709 this.dialog.addEventListener('animationend', onEnd, { once: true }); 17710 setTimeout(() => { if (this._closing) onEnd(); }, 350); 17711 } 17712 17713 async refresh() { 17714 try { 17715 const res = await fetch('/cart.js', { 17716 headers: { Accept: 'application/json' }, 17717 cache: 'no-store', 17718 credentials: 'same-origin' 17719 }); 17720 const cart = await res.json(); 17721 this.applyCart(cart); 17722 } catch (e) { console.error('[bp-cart-drawer] refresh failed', e); } 17723 } 17724 17725 applyCart(cart) { 17726 // The Narvar Shipping Protection line (SKU narvardeliveryprotection) 17727 // is an add-on, not a product the shopper chose â it must never count 17728 // toward the bag, and it must not keep the cart from reading as empty. 17729 const INSURANCE_SKU = 'narvardeliveryprotection'; 17730 const items = Array.isArray(cart.items) ? cart.items : []; 17731 const insuranceLine = items.find((i) => (i.sku || '').toLowerCase() === INSURANCE_SKU); 17732 const realCount = items.reduce( 17733 (n, i) => ((i.sku || '').toLowerCase() === INSURANCE_SKU ? n : n + i.quantity), 17734 0 17735 ); 17736 17737 // When every real product has been removed but the cart still carries 17738 // lines â the Narvar Shipping Protection add-on, which Narvar re-adds 17739 // while its toggle is on, so removing just that ONE line races the 17740 // re-add and it lingers with a $3 total you can still check out (QA 17741 // 7/16, Critical) â clear the WHOLE cart. /cart/clear drops every line 17742 // AND the cart attributes Narvar keys its toggle off, so the insurance 17743 // can't come back. Guarded so the resulting empty cart:updated doesn't 17744 // loop; return early so we never paint the transient "$3, no items" 17745 // state (which was also letting the checkout button show a price over 17746 // an empty bag). 17747 this._realCount = realCount; 17748 if (realCount === 0 && items.length > 0 && !this._clearingCart) { 17749 this._clearingCart = true; 17750 // Optimistically paint the empty state NOW. Without this the drawer 17751 // kept showing the stale items + footer + Narvar button/protection 17752 // during the async clear round-trip, so an "empty bag" could still 17753 // show "Checkout - $68" and a $3 protection line (QA 7/16, Critical). 17754 this.#showEmptyUI(); 17755 fetch('/cart/clear.js', { 17756 method: 'POST', 17757 headers: { 'Content-Type': 'application/json', Accept: 'application/json' }, 17758 cache: 'no-store', 17759 credentials: 'same-origin' 17760 }) 17761 .then((r) => r.json()) 17762 .then((c) => document.dispatchEvent(new CustomEvent('cart:updated', { detail: { cart: c } }))) 17763 .catch((e) => console.error('[bp-cart-drawer] clear failed', e)) 17764 .finally(() => { this._clearingCart = false; }); 17765 return; 17766 } 17767 17768 // Count bubble + trigger count â reflect real products only. 17769 const bubble = this.querySelector('[data-bp-cart-bubble]'); 17770 const triggerCount = this.querySelector('[data-bp-cart-trigger-count]'); 17771 if (bubble) bubble.textContent = realCount; 17772 if (triggerCount) { 17773 triggerCount.textContent = realCount; 17774 triggerCount.toggleAttribute('hidden', realCount === 0); 17775 } 17776 // a11y: announce the new cart count via Horizon's existing status 17777 // live region (header-actions.liquid) â it was rendered but never 17778 // written to, so cart changes were silent to screen readers. 17779 try { 17780 const live = document.querySelector('[data-testid="cart-count-live-region"]'); 17781 // Only announce on an actual change â the first applyCart() primes 17782 // _bpLastCount silently so a page-load / drawer-open render with no 17783 // change doesn't speak the count. 17784 if (live && this._bpLastCount !== undefined && realCount !== this._bpLastCount) { 17785 live.textContent = realCount === 1
17786 ? '1 item in your bag' 17787 : realCount + ' items in your bag'; 17788 } 17789 this._bpLastCount = realCount; 17790 } catch (_) { /* non-fatal */ } 17791 // Subtotal = merchandise only. cart.total_price INCLUDES the Narvar 17792 // Shipping Protection line (SKU narvardeliveryprotection); once Narvar 17793 // adds that line (e.g. after the checkout round-trip) total_price jumps 17794 // to products+protection while the rows still show products only, so 17795 // the Subtotal read as a "lie" ($372.42 instead of $360, QA 8/04, 17796 // Dragana). Sum the non-protection lines â same formula as the Liquid 17797 // render + the visible row prices; protection stays in its own box. 17798 const bpMerchSubtotal = cart.items.reduce( 17799 (sum, i) => ((i.sku || '').toLowerCase() === 'narvardeliveryprotection' ? sum : sum + i.final_line_price), 17800 0 17801 ); 17802 const subtotal = this.querySelector('[data-bp-cart-subtotal]'); 17803 if (subtotal) subtotal.textContent = this.formatMoney(bpMerchSubtotal); 17804 17805 // Disable checkout when there's nothing to pay for ($0 subtotal). Covers 17806 // the primary Checkout button plus any Narvar-injected DP checkout 17807 // buttons that share the form, so a $0 bag can never start checkout. 17808 const noSubtotal = !(bpMerchSubtotal > 0); 17809 this.querySelectorAll( 17810 '.bp-cart-drawer__btn--primary, #CartDrawer-Form [name="checkout"], #narvar-cart-drawer-dp-buttons button, #narvar-cart-drawer-dp-buttons a' 17811 ).forEach((btn) => { 17812 if ('disabled' in btn) btn.disabled = noSubtotal; 17813 btn.toggleAttribute('aria-disabled', noSubtotal); 17814 btn.classList.toggle('bp-cart-drawer__btn--disabled', noSubtotal); 17815 }); 17816 17817 // Per-line updates: walk every existing line and either update qty/price 17818 // in place or remove it if its product is genuinely gone. 17819 // 17820 // Match by line key first, then fall back to VARIANT id. The server- 17821 // rendered `data-line-key` () doesn't always byte-match the 17822 // AJAX cart's `item.key`; when it didn't, the line was treated as "gone" 17823 // â it faded out and syncItemsList re-added it, so a simple qty step made 17824 // the row disappear and reappear. Matching by variant keeps the row and 17825 // updates it in place (and re-syncs its key so later updates match 17826 // exactly). existingKeys tracks the RESOLVED cart keys so hasNewItems 17827 // below can't false-positive off the same mismatch and re-render. 17828 const byKey = new Map(cart.items.map((i) => [i.key, i])); 17829 const byVariant = new Map(); 17830 cart.items.forEach((i) => { 17831 const arr = byVariant.get(i.variant_id) || []; 17832 arr.push(i); 17833 byVariant.set(i.variant_id, arr); 17834 }); 17835 const existingKeys = new Set(); 17836 this.querySelectorAll('[data-bp-cart-line]').forEach((line) => { 17837 let item = byKey.get(line.dataset.lineKey); 17838 if (!item) { 17839 const vid = parseInt(line.dataset.variant, 10); 17840 const bucket = byVariant.get(vid); 17841 if (bucket && bucket.length) { 17842 item = bucket.shift(); // consume one line of this variant 17843 line.dataset.lineKey = item.key; // re-sync so future updates key-match 17844 } 17845 } 17846 if (!item) { 17847 line.style.transition = 'opacity 0.2s ease, transform 0.2s ease'; 17848 line.style.opacity = '0'; 17849 line.style.transform = 'translateX(8px)'; 17850 setTimeout(() => line.remove(), 220); 17851 return; 17852 } 17853 existingKeys.add(item.key); 17854 const qty = line.querySelector('[data-bp-qty-input]'); 17855 if (qty && qty.value !== String(item.quantity)) qty.value = item.quantity; 17856 const price = line.querySelector('[data-bp-line-price]'); 17857 if (price) price.textContent = this.formatMoney(item.final_line_price); 17858 // Original price â struck through, shown only while a discount 17859 // (promo code, etc.) actually lowers the line. Mirrors the 17860 // Liquid render's hidden-by-default state (bp-cart-line-item.liquid) 17861 // so applying/removing a code updates it live without a refetch. 17862 const compare = line.querySelector('[data-bp-line-compare]'); 17863 if (compare) { 17864 const hasDiscount = item.original_line_price > item.final_line_price; 17865 compare.hidden = !hasDiscount; 17866 if (hasDiscount) compare.textContent = this.formatMoney(item.original_line_price); 17867 } 17868 }); 17869 17870 // If any item in the cart doesn't have a row in the drawer yet 17871 // (e.g., PDP add introduced a new line), fetch a fresh render of 17872 // the header section and swap in the new items list HTML. 17873 // Exclude the insurance line â it never gets a drawer row, so counting 17874 // it here would trigger a needless re-render fetch on every cart update. 17875 const hasNewItems = items.some( 17876 (i) => (i.sku || '').toLowerCase() !== INSURANCE_SKU && !existingKeys.has(i.key) 17877 ); 17878 if (hasNewItems) this.syncItemsList(); 17879 17880 // Empty-state vs items-list + footer/CTL visibility. 17881 const itemsList = this.querySelector('[data-bp-cart-items]'); 17882 const emptyState = this.querySelector('[data-bp-cart-empty]'); 17883 const foot = this.querySelector('.bp-cart-drawer__foot'); 17884 const ctl = this.querySelector('[data-bp-ctl]'); 17885 const isEmpty = realCount === 0; 17886 if (itemsList) itemsList.toggleAttribute('hidden', isEmpty); 17887 if (emptyState) emptyState.toggleAttribute('hidden', !isEmpty); 17888 if (foot) foot.toggleAttribute('hidden', isEmpty); 17889 if (ctl) ctl.toggleAttribute('hidden', isEmpty); 17890 17891 // Bundle upsell ("Complete the look") is owned by the shared global 17892 // helper window.bpBundleUpsell (snippets/bp-cart-bundle-upsell.liquid), 17893 // which self-renders off cart:updated / bp:cart-lines:rendered â nothing 17894 // needed here. 17895 } 17896 17897 // Force the drawer's empty presentation immediately. Used optimistically 17898 // while /cart/clear round-trips (so no stale items / Narvar checkout price 17899 // / $3 protection line flashes over an empty bag) and as the submit-guard 17900 // fallback. Zeroes counts + subtotal and hides the footer + CTL. 17901 #showEmptyUI() { 17902 this._realCount = 0; 17903 const bubble = this.querySelector('[data-bp-cart-bubble]'); 17904 const triggerCount = this.querySelector('[data-bp-cart-trigger-count]'); 17905 if (bubble) bubble.textContent = '0'; 17906 if (triggerCount) { triggerCount.textContent = '0'; triggerCount.setAttribute('hidden', ''); } 17907 const subtotal = this.querySelector('[data-bp-cart-subtotal]'); 17908 if (subtotal) subtotal.textContent = this.formatMoney(0); 17909 const itemsList = this.querySelector('[data-bp-cart-items]'); 17910 const emptyState = this.querySelector('[data-bp-cart-empty]'); 17911 const foot = this.querySelector('.bp-cart-drawer__foot'); 17912 const ctl = this.querySelector('[data-bp-ctl]'); 17913 if (itemsList) itemsList.setAttribute('hidden', ''); 17914 if (emptyState) emptyState.removeAttribute('hidden'); 17915 if (foot) foot.setAttribute('hidden', ''); 17916 if (ctl) ctl.setAttribute('hidden', ''); 17917 } 17918 17919 async syncItemsList() { 17920 try { 17921 let html = ''; 17922 17923 // 1. Try Shopify Section Rendering API. Faster + lighter when it works. 17924 // Walk every ancestor section wrapper and ask each one in parallel â 17925 // one of them is the section that renders the cart drawer. 17926 const sectionWrappers = []; 17927 let el = this.parentElement; 17928 while (el) { 17929 if (el.id && el.id.startsWith('shopify-section-')) sectionWrappers.push(el.i
17929d.replace(/^shopify-section-/, '')); 17930 el = el.parentElement; 17931 } 17932 17933 for (const sectionId of sectionWrappers) { 17934 try { 17935 const res = await fetch(`${window.location.pathname}?sections=${sectionId}`, { 17936 headers: { Accept: 'application/json' }, 17937 cache: 'no-store', 17938 credentials: 'same-origin' 17939 }); 17940 if (!res.ok) continue; 17941 const json = await res.json(); 17942 const candidate = json[sectionId]; 17943 if (candidate && candidate.includes('data-bp-cart-items')) { 17944 html = candidate; 17945 break; 17946 } 17947 } catch (_) { /* try next */ } 17948 } 17949 17950 // 2. Fallback: fetch the full page HTML and extract the drawer items. 17951 // Bigger payload but guaranteed to include the drawer markup. 17952 if (!html) { 17953 const res = await fetch(window.location.pathname, { 17954 cache: 'no-store', 17955 credentials: 'same-origin' 17956 }); 17957 if (!res.ok) return; 17958 html = await res.text(); 17959 } 17960 17961 const doc = new DOMParser().parseFromString(html, 'text/html'); 17962 const freshList = doc.querySelector('[data-bp-cart-items]'); 17963 const currentList = this.querySelector('[data-bp-cart-items]'); 17964 if (freshList && currentList) { 17965 currentList.innerHTML = freshList.innerHTML; 17966 currentList.hidden = false; 17967 // The cart lines are the only place bp_style exists on the 17968 // client (/cart.js carries no metafields), and `cart:updated` 17969 // fires the moment /cart/change.js returns â well before this 17970 // section fetch lands. Anything that reads [data-bp-style] 17971 // (Complete the Look's in-bag exclusion) would otherwise be 17972 // looking at the PRE-mutation lines and miss the item just 17973 // added. Announce that the lines are actually in the DOM. 17974 document.dispatchEvent(new CustomEvent('bp:cart-lines:rendered')); 17975 } 17976 } catch (e) { console.error('[bp-cart-drawer] syncItemsList failed', e); } 17977 } 17978 17979 formatMoney(cents) { 17980 // Drop the .00 on whole-dollar prices (site-wide convention). Intl 17981 // adds the thousands separator so a JS-updated total matches the 17982 // Liquid `money | replace: '.00', ''` render (QA 7/24). 17983 return new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }) 17984 .format(cents / 100).replace(/\.00$/, ''); 17985 } 17986 } 17987 customElements.define('bp-cart-drawer', BpCartDrawer); 17988 } 17989</script>
17989 17990 17991 17992 17993 <span 17994 ref="liveRegion" 17995 class="visually-hidden" 17996 role="status" 17997 data-testid="cart-count-live-region" 17998 ></span> 17999</header-actions> 18000
18001<script 18002 type="module" 18003 src="//www.bostonproper.com/cdn/shop/t/1521/assets/header-actions.js?v=121308165565653895611790713714" 18004 fetchpriority="low" 18005></script>
18005 18006 18007 18008 18009 18010 18011 18012 </div> 18013 </div> 18014 </div> 18015 18016 18017 18018 18019 18020 18021 18022 18023 18024 18025 18026 <div class="header__row header__navigation-bar-row color-scheme-1"> 18027 18028 18029 18030 18031 18032 18033<style data-shopify> 18034 .header__underlay { 18035 --color-submenu: rgb(254 255 250 / 1.0); 18036 } 18037</style> 18038 18039 18040 18041 18042 </div> 18043 18044</header-component> 18045
18046<script 18047 src="//www.bostonproper.com/cdn/shop/t/1521/assets/header.js?v=36718054510661947891790713714" 18048 type="module" 18049></script>
18049 18050 18051 18052 18053 18054
18054<script type="module"> 18055 import { hydrate } from '@theme/section-hydration'; 18056 const url = new URL(window.location.href); 18057 url.searchParams.delete('page'); 18058 hydrate('sections--29563569635494__header_section', url); 18059 </script>
18059 18060 18061 18062 18063</header> 18064<!-- END sections: header-group --> 18065 </div> 18066 18067
18067<script> 18068 // Inline header height and menu style calculations to prevent layout shift 18069 // Note: Updates in calculateHeaderGroupHeight(), updateTransparentHeaderOffset() and setHeaderMenuStyle() utilities.js should be kept in sync with this function 18070 (function setHeaderHeighCustomProperties() { 18071 /* 18072 * Header calculation functions for maintaining CSS variables 18073 * Mimic calculateHeaderGroupHeight() in utilities.js 18074 */ 18075 const header = document.querySelector('header-component'); 18076 const headerGroup = document.querySelector('#header-group'); 18077 const hasHeaderSection = headerGroup?.querySelector('.header-section'); 18078 18079 if (!header || !headerGroup) return; 18080 18081 const headerTopRow = header.querySelector('.header__row--top'); 18082 const headerHeight = header.offsetHeight; 18083 18084 // Calculate the total height of the header group 18085 let headerGroupHeight = 0; 18086 const children = headerGroup.children; 18087 for (let i = 0; i < children.length; i++) { 18088 const element = children[i]; 18089 if (element === header || !(element instanceof HTMLElement)) continue; 18090 headerGroupHeight += element.offsetHeight; 18091 } 18092 18093 // Check for transparent header special case 18094 if (header.hasAttribute('transparent') && header.parentElement?.nextElementSibling) { 18095 headerGroupHeight += headerHeight; 18096 } 18097 18098 // Set CSS variables 18099 document.body.style.setProperty('--header-height', `${headerHeight}px`); 18100 document.body.style.setProperty('--header-group-height', `${headerGroupHeight}px`); 18101 18102 if (headerTopRow) { 18103 window.requestAnimationFrame(function () { 18104 document.body.style.setProperty('--top-row-height', `${headerTopRow.offsetHeight}px`); 18105 }); 18106 } 18107 18108 /** 18109 * Updates CSS custom properties for transparent header offset calculation 18110 * Mimic updateTransparentHeaderOffset() in utilities.js 18111 */ 18112 18113 if (!hasHeaderSection || !header?.hasAttribute('transparent')) { 18114 document.body.style.setProperty('--transparent-header-offset-boolean', '0'); 18115 return; 18116 } 18117 18118 const hasImmediateSection = hasHeaderSection.nextElementSibling?.classList.contains('shopify-section'); 18119 18120 const shouldApplyOffset = !hasImmediateSection ? '1' : '0'; 18121 document.body.style.setProperty('--transparent-header-offset-boolean', shouldApplyOffset); 18122 })(); 18123 18124 (function setHeaderMenuStyle() { 18125 const headerComponent = document.querySelector('#header-component'); 18126 if (headerComponent) { 18127 const isTouchDevice = 'ontouchstart' in window && navigator.maxTouchPoints > 0; 18128 const overflowList = headerComponent?.querySelector('overflow-list'); 18129 const hasReachedMinimum = overflowList && overflowList.hasAttribute('minimum-reached'); 18130 headerComponent.dataset.menuStyle = isTouchDevice || hasReachedMinimum ? 'drawer' : 'menu'; 18131 } 18132 })(); 18133 18134 /* Boston Proper: force drawer mode below 1024px (mobile + tablet). 18135 Above 1024px we DO NOT force 'menu' â we let Horizon's overflow-list 18136 logic in setHeaderMenuStyle() (assets/utilities.js) decide. That logic 18137 flips to 'drawer' at intermediate desktop widths where menu items can't 18138 fit (e.g. the Shopify theme editor iframe at ~1500px), preventing the 18139 right-side action icons from being pushed off-screen. 18140 18141 The MutationObserver only re-asserts when we're below the breakpoint â 18142 so Horizon's debounced resize handler (header-menu.js:47-49) can switch 18143 the dataset freely on desktop, but can't undo our drawer mode on mobile. */ 18144 (function bpResponsiveMenuStyle() { 18145 const headerComponent = document.querySelector('#header-component'); 18146 if (!headerComponent) return; 18147 const mq = window.matchMedia('(min-width: 1024px)'); 18148 const isTouchDevice = 'ontouchstart' in window && navigator.maxTouchPoints > 0; 18149 function shouldForceDrawer() { 18150 return isTouchDevice || !mq.matches; 18151 } 18152 function enforce() { 18153 if (!shouldForceDrawer()) return; // desktop: let Horizon decide 18154 if (headerComponent.dataset.menuStyle !== 'drawer') { 18155 headerComponent.dataset.menuStyle = 'drawer'; 18156 } 18157 } 18158 function onChange() { 18159 enforce(); 18160 if (!shouldForceDrawer()) { 18161 // Crossing up to desktop â let Horizon recompute now. 18162 window.dispatchEvent(new Event('resize')); 18163 } 18164 } 18165 if (mq.addEventListener) { 18166 mq.addEventListener('change', onChange); 18167 } else if (mq.addListener) { 18168 mq.addListener(onChange); 18169 } 18170 new MutationObserver(enforce).observe(headerComponent, { 18171 attributes: true, 18172 attributeFilter: ['data-menu-style'] 18173 }); 18174 enforce(); 18175 })(); 18176 18177 /* Boston Proper: compact-nav state machine. 18178 18179 Behavior (per design): the resting nav is IN FLOW and scrolls 18180 away with the page. Once it has fully scrolled out of view, a 18181 compact fixed bar takes over. Positioning + variant styles live 18182 in snippets/bp-fonts.liquid, keyed off data-bp-nav. 18183 18184 ââ Why this is not Horizon's data-sticky-state ââââââââââââââ 18185 Horizon's #observeStickyPosition writes data-sticky-state from 18186 an IntersectionObserver on the header itself. The compact 18187 variant changes the header's height AND its position, so keying 18188 it off that flag makes the styles perturb the geometry that 18189 produces the flag: observer writes -> header resizes/unpins -> 18190 observer re-fires -> writes the opposite -> the header strobes 18191 between variants mid-scroll. Measured 2026-07-14 in Chrome: at 18192 threshold: 1, isIntersecting is false at ratio 0.9888 and true 18193 at ratio 1, so Horizon's flag flips at exactly rect.top === 0 â 18194 the one offset where a header pinned at top: -1px hands off 18195 from in-flow to pinned, and precisely where the strobe showed. 18196 18197 This machine reads window.scrollY instead. Because 18198 .header-section reserves the resting height via 18199 --bp-header-rest-h even while the child is fixed, engaging the 18200 compact bar reflows nothing, so scrollY cannot be perturbed by 18201 the state it produces. The loop is broken at the source rather 18202 than damped. The dead band below is belt-and-braces for 18203 sub-pixel scroll noise at the threshold. 18204 18205 Horizon still writes data-sticky-state; it only drives opacity 18206 and view-transition-name now (sections/header.liquid:562-577), 18207 no geometry, so the two writers can't interact. */ 18208 (function bpCompactNav() { 18209 const header = document.querySelector('#header-component[sticky="always"]'); 18210 if (!header) return; 18211 const section = header.closest('.header-section'); 18212 if (!section) return; 18213 18214 const DEAD_BAND = 8; 18215 let restBottom = 0; 18216 let rafId = null; 18217 18218 /* The resting height is only measurable while the header is in 18219 its resting layout â when compact it is fixed and shrunken. 18220 Drop to rest, measure, restore. Runs on load + resize only, 18221 never per scroll frame, so the forced reflow is not on the 18222 hot path. */ 18223 function measure() { 18224 const wasCompact = header.dataset.bpNav === 'compact'; 18225 if (wasCompact) header.dataset.bpNav = 'rest'; 18226 18227 const restHeight = header.offsetHeight; 18228 section.style.setProperty('--bp-header-rest-h', restHeight + 'px'); 18229 /* Document-space bottom edge of the resting header. section is 18230 static and its reserved height never changes, so this is 18231 stable regardless of the current state. */ 18232 restBottom = section.getBoundingClientRect().top + window.scrollY + restHeight; 18233 18234 if (wasCompact) header.dataset.bpNav = 'compact'; 18235 } 18236
18237 function update() { 18238 rafId = null; 18239 const y = window.scrollY; 18240 const isCompact = header.dataset.bpNav === 'compact'; 18241 if (!isCompact && y >= restBottom) { 18242 header.dataset.bpNav = 'compact'; 18243 } else if (isCompact && y < restBottom - DEAD_BAND) { 18244 header.dataset.bpNav = 'rest'; 18245 } 18246 } 18247 18248 function onScroll() { 18249 if (rafId !== null) return; 18250 rafId = window.requestAnimationFrame(update); 18251 } 18252 18253 function onResize() { 18254 measure(); 18255 onScroll(); 18256 } 18257 18258 document.addEventListener('scroll', onScroll, { passive: true }); 18259 window.addEventListener('resize', onResize, { passive: true }); 18260 18261 header.dataset.bpNav = 'rest'; 18262 measure(); 18263 update(); 18264 /* Fonts/images landing after first paint change the resting 18265 height; re-measure once the page has settled. */ 18266 window.addEventListener('load', onResize, { once: true }); 18267 })(); 18268 18269 /* Boston Proper: a11y patch for the third-party promo-bar iframe 18270 (id "PBarNextFrame*" â the free-gift/promo bar app embed, e.g. 18271 Discount Kit). The vendor injects this iframe client-side with 18272 no title/aria-label, which fails the axe "frame-title" rule. 18273 We don't control that markup, so we watch for the iframe and 18274 label it the moment it lands in the DOM (works whether it's 18275 already present, or the app mounts/remounts it later). */ 18276 (function bpLabelPromoBarFrame() { 18277 const SELECTOR = 'iframe[id^="PBarNextFrame"]'; 18278 18279 function label(frame) { 18280 if (!frame || frame.hasAttribute('title')) return; 18281 frame.setAttribute('title', 'Store promotion'); 18282 } 18283 18284 const existing = document.querySelector(SELECTOR); 18285 if (existing) label(existing); 18286 18287 new MutationObserver(function (mutations) { 18288 for (const mutation of mutations) { 18289 for (const node of mutation.addedNodes) { 18290 if (!(node instanceof Element)) continue; 18291 if (node.matches(SELECTOR)) { 18292 label(node); 18293 } else { 18294 const frame = node.querySelector && node.querySelector(SELECTOR); 18295 if (frame) label(frame); 18296 } 18297 } 18298 } 18299 }).observe(document.documentElement, { childList: true, subtree: true }); 18300 })(); 18301 </script>
18301 18302 18303 <main 18304 id="MainContent" 18305 class="content-for-layout" 18306 role="main" 18307 tabindex="-1" 18308 data-page-transition-enabled="false" 18309 data-product-transition="false" 18310 data-template="product" 18311 > 18312 <div id="shopify-section-template--29563577761958__main" class="shopify-section"> 18313
18314<script type="application/ld+json"> 18315 {"@context":"http:\/\/schema.org\/","@id":"\/products\/boston-proper-printed-scarf-with-love-bp#product","@type":"ProductGroup","brand":{"@type":"Brand","name":"Boston Proper"},"category":"Promotions","description":"","hasVariant":[{"@id":"\/products\/boston-proper-printed-scarf-with-love-bp?variant=56389031657638#variant","@type":"Product","image":"https:\/\/www.bostonproper.com\/cdn\/shop\/files\/2CT8071_SET_e6f3e3ce-5a5f-451b-bc3b-ea1f8f71d236.jpg?v=1791009198\u0026width=1920","name":"Boston Proper Printed Scarf With Love BP - With Love BP \/ One Size","offers":{"@id":"\/products\/boston-proper-printed-scarf-with-love-bp?variant=56389031657638#offer","@type":"Offer","availability":"http:\/\/schema.org\/InStock","price":"50.00","priceCurrency":"USD","url":"https:\/\/www.bostonproper.com\/products\/boston-proper-printed-scarf-with-love-bp?variant=56389031657638"},"sku":"2CT8071000"}],"name":"Boston Proper Printed Scarf With Love BP","productGroupID":"15286962618534","url":"https:\/\/www.bostonproper.com\/products\/boston-proper-printed-scarf-with-love-bp"} 18316</script>
18316 18317 18318 18319 18320 18321 18322 18323 18324 18325 18326 18327 18328 18329<div class="section-background color-scheme-1"></div> 18330<div 18331 class="product-information section section--page-width spacing-style color-scheme-1 relative" 18332 style="--padding-block-start: 20px;--padding-block-end: max(20px, calc(var(--spacing-scale) * 40px)); --gap: 20px;" 18333 data-testid="product-information" 18334> 18335 18336 18337 <a 18338 class="skip-to-content-link button-secondary" 18339 href="#ProductInformation-template--29563577761958__main" 18340> 18341 Skip to product information 18342</a> 18343 18344 18345 18346 18347 <div 18348 class="product-information__grid product-information--media-left" 18349 data-product-grid-content 18350 > 18351 18352 18353 18354 18355 18356 18357 <div 18358 class="product-information__media" 18359 data-testid="product-information-media" 18360 > 18361 18362<bp-pdp-gallery 18363 class="product-slider product-slider-main-images relative" 18364 data-section-id="template--29563577761958__main" 18365 data-product-id="15286962618534" 18366 data-bp-style="2CT8071" 18367 data-bp-amplience-base="https://media.bostonproper.com" 18368 data-bp-max-items="10" 18369 data-bp-media-count="2" 18370 data-bp-image-count="2" 18371 data-bp-product-title="Boston Proper Printed Scarf With Love BP" 18372 18373 18374 ><nav class="bp-pdp-gallery__crumbs bp-crumbs" aria-label="Breadcrumb"> 18375 <ol class="bp-crumbs__list"> 18376 <li class="bp-crumbs__item"> 18377 <a class="bp-crumbs__link" href="https://www.bostonproper.com/">Homepage</a> 18378 <span class="bp-crumbs__sep" aria-hidden="true">/</span> 18379 </li><li class="bp-crumbs__item bp-crumbs__item--current"> 18380 <span aria-current="page">Boston Proper Printed Scarf With Love BP</span> 18381 </li> 18382 </ol> 18383 </nav>
18383<script> 18384 (function () { 18385 var el = document.querySelector('.bp-pdp-gallery__crumbs [data-bp-crumb-handle]'); 18386 if (!el) return; 18387 var h = el.getAttribute('data-bp-crumb-handle'); 18388 if (!h) return; 18389 function apply(n) { 18390 n = (n || '').trim(); 18391 if (n && n !== el.textContent.trim()) el.textContent = n; 18392 } 18393 function readMap() { 18394 try { 18395 return JSON.parse(sessionStorage.getItem('bp_coll_names') || '{}'); 18396 } catch (e) { 18397 return {}; 18398 } 18399 } 18400 var map = readMap(); 18401 if (map[h]) { 18402 apply(map[h]); 18403 return; 18404 } 18405 var pendKey = 'bp_coll_name_pending_' + h; 18406 if (sessionStorage.getItem(pendKey)) return; 18407 try { 18408 sessionStorage.setItem(pendKey, '1'); 18409 } catch (e) {} 18410 fetch('/collections/' + encodeURIComponent(h) + '?view=crumb-title', { 18411 credentials: 'same-origin', 18412 headers: { 'X-Requested-With': 'fetch' }, 18413 }) 18414 .then(function (r) { 18415 return r.ok ? r.text() : ''; 18416 }) 18417 .then(function (t) { 18418 var n = (t || '').trim(); 18419 if (!n || n.length > 120 || n.indexOf('<') !== -1) return; 18420 apply(n); 18421 var m = readMap(); 18422 m[h] = n; 18423 try { 18424 sessionStorage.setItem('bp_coll_names', JSON.stringify(m)); 18425 } catch (e) {} 18426 }) 18427 .catch(function () {}) 18428 .finally(function () { 18429 try { 18430 sessionStorage.removeItem(pendKey); 18431 } catch (e) {} 18432 }); 18433 })(); 18434 </script>
18434<div class="bp-pdp-gallery__stage"> 18435 <div class="bp-pdp-gallery__rail" data-bp-pdp-rail><div class="bp-pdp-gallery__slide" data-index="0" data-media-type="image"> 18436 <div class="product-media"> 18437 <img 18438 class="bp-pdp-gallery__img product-media__image" 18439 data-bp-zoomable 18440 data-bp-zoom-index="0" 18441 data-bp-amp-primary 18442 src="https://media.bostonproper.com/s/bostonproper/2CT8071_SET/1?w=1400&fmt=auto" 18443 srcset=" 18444 https://media.bostonproper.com/s/bostonproper/2CT8071_SET/1?w=400&fmt=auto 400w, 18445 https://media.bostonproper.com/s/bostonproper/2CT8071_SET/1?w=800&fmt=auto 800w, 18446 https://media.bostonproper.com/s/bostonproper/2CT8071_SET/1?w=1200&fmt=auto 1200w, 18447 https://media.bostonproper.com/s/bostonproper/2CT8071_SET/1?w=1600&fmt=auto 1600w, 18448 https://media.bostonproper.com/s/bostonproper/2CT8071_SET/1?w=1920&fmt=auto 1920w, 18449 https://media.bostonproper.com/s/bostonproper/2CT8071_SET/1?w=2560&fmt=auto 2560w 18450 " 18451 sizes="(min-width: 1024px) calc(50vw - 60px), 100vw" 18452 alt="Boston Proper Printed Scarf With Love BP" 18453 width="1400" 18454 height="1750" 18455 loading="eager" 18456 fetchpriority="high" 18457 onerror="if(!this.dataset.bpFellBack){this.dataset.bpFellBack='1';this.removeAttribute('srcset');this.src='//www.bostonproper.com/cdn/shop/files/2CT8071_SET_e6f3e3ce-5a5f-451b-bc3b-ea1f8f71d236.jpg?v=1791009198&width=1400';}" 18458 > 18459 </div> 18460 </div><div 18461 class="bp-pdp-gallery__slide" 18462 data-index="1" 18463 data-media-type="image" 18464 data-bp-amp-placeholder 18465 ></div></div><div class="bp-pdp-gallery__first-cell" aria-hidden="true"> 18466 <div 18467 class="bp-pdp-save-later bp-pdp-save-later--overlay" 18468 data-bp-pdp-save-later 18469 data-product-id="15286962618534" 18470 > 18471 <button 18472 type="button" 18473 class="bp-pdp-save-later__btn" 18474 data-bp-pdp-save-later-btn 18475 aria-label="Add to Wishlist" 18476 aria-pressed="false" 18477 > 18478 <svg 18479 class="bp-pdp-save-later__icon" 18480 width="20" 18481 height="20"
18482 viewBox="0 0 24 24" 18483 fill="none" 18484 stroke="currentColor" 18485 stroke-width="1.5" 18486 stroke-linecap="round" 18487 stroke-linejoin="round" 18488 aria-hidden="true" 18489 > 18490 <path d="M20.84 4.61a5.5 5.5 0 0 0-7.78 0L12 5.67l-1.06-1.06a5.5 5.5 0 0 0-7.78 7.78l1.06 1.06L12 21.23l7.78-7.78 1.06-1.06a5.5 5.5 0 0 0 0-7.78z"/> 18491 </svg> 18492 </button> 18493 </div><button 18494 type="button" 18495 class="bp-pdp-gallery__htwi" 18496 data-bp-gallery-htwi 18497 aria-label="Scroll to How to Wear It" 18498 hidden 18499 > 18500 <span class="bp-pdp-gallery__htwi-label">How to Wear It</span></button></div> 18501 </div><template data-bp-shopify-fallback><div class="bp-pdp-gallery__slide" data-index="0" data-media-type="image"> 18502 <div class="product-media"><img 18503 class="bp-pdp-gallery__img product-media__image" 18504 data-bp-zoomable 18505 data-bp-zoom-index="0" 18506 src="//www.bostonproper.com/cdn/shop/files/2CT8071_SET_e6f3e3ce-5a5f-451b-bc3b-ea1f8f71d236.jpg?v=1791009198&width=1400" 18507 srcset=" 18508 //www.bostonproper.com/cdn/shop/files/2CT8071_SET_e6f3e3ce-5a5f-451b-bc3b-ea1f8f71d236.jpg?v=1791009198&width=400 400w, 18509 //www.bostonproper.com/cdn/shop/files/2CT8071_SET_e6f3e3ce-5a5f-451b-bc3b-ea1f8f71d236.jpg?v=1791009198&width=800 800w, 18510 //www.bostonproper.com/cdn/shop/files/2CT8071_SET_e6f3e3ce-5a5f-451b-bc3b-ea1f8f71d236.jpg?v=1791009198&width=1200 1200w, 18511 //www.bostonproper.com/cdn/shop/files/2CT8071_SET_e6f3e3ce-5a5f-451b-bc3b-ea1f8f71d236.jpg?v=1791009198&width=1600 1600w, 18512 //www.bostonproper.com/cdn/shop/files/2CT8071_SET_e6f3e3ce-5a5f-451b-bc3b-ea1f8f71d236.jpg?v=1791009198&width=1920 1920w, 18513 //www.bostonproper.com/cdn/shop/files/2CT8071_SET_e6f3e3ce-5a5f-451b-bc3b-ea1f8f71d236.jpg?v=1791009198&width=2560 2560w 18514 " 18515 sizes="(min-width: 1024px) calc(50vw - 60px), 100vw" 18516 alt="Boston Proper Printed Scarf With Love BP" 18517 width="1639" 18518 height="2048" 18519 loading="lazy" 18520 fetchpriority="auto" 18521 ></div> 18522 </div><div class="bp-pdp-gallery__slide" data-index="1" data-media-type="image"> 18523 <div class="product-media"><img 18524 class="bp-pdp-gallery__img product-media__image" 18525 data-bp-zoomable 18526 data-bp-zoom-index="1" 18527 src="//www.bostonproper.com/cdn/shop/files/2_af52291f-802d-4184-8815-c64ea15071dd.jpg?v=1791009198&width=1400" 18528 srcset=" 18529 //www.bostonproper.com/cdn/shop/files/2_af52291f-802d-4184-8815-c64ea15071dd.jpg?v=1791009198&width=400 400w, 18530 //www.bostonproper.com/cdn/shop/files/2_af52291f-802d-4184-8815-c64ea15071dd.jpg?v=1791009198&width=800 800w, 18531 //www.bostonproper.com/cdn/shop/files/2_af52291f-802d-4184-8815-c64ea15071dd.jpg?v=1791009198&width=1200 1200w, 18532 //www.bostonproper.com/cdn/shop/files/2_af52291f-802d-4184-8815-c64ea15071dd.jpg?v=1791009198&width=1600 1600w, 18533 //www.bostonproper.com/cdn/shop/files/2_af52291f-802d-4184-8815-c64ea15071dd.jpg?v=1791009198&width=1920 1920w, 18534 //www.bostonproper.com/cdn/shop/files/2_af52291f-802d-4184-8815-c64ea15071dd.jpg?v=1791009198&width=2560 2560w 18535 " 18536 sizes="(min-width: 1024px) calc(50vw - 60px), 100vw" 18537 alt="Boston Proper Printed Scarf With Love BP" 18538 width="1639" 18539 height="2048" 18540 loading="lazy" 18541 fetchpriority="auto" 18542 ></div> 18543 </div></template>
18543<script type="application/json" id="bp-pdp-save-data-15286962618534"> 18544 { 18545 "id": "15286962618534", 18546 "handle": "boston-proper-printed-scarf-with-love-bp", 18547 "title": "Boston Proper Printed Scarf With Love BP", 18548 "image": "\/\/www.bostonproper.com\/cdn\/shop\/files\/2CT8071_SET_e6f3e3ce-5a5f-451b-bc3b-ea1f8f71d236.jpg?v=1791009198\u0026width=484", 18549 "variants": {"56389031657638": { 18550 "id": "56389031657638", 18551 "title": "With Love BP \/ One Size", 18552 "price": "5000", 18553 "compare_at_price": "5000", 18554 "sku": "2CT8071000","image": "\/\/www.bostonproper.com\/cdn\/shop\/files\/2CT8071_SET_e6f3e3ce-5a5f-451b-bc3b-ea1f8f71d236.jpg?v=1791009198\u0026width=484" 18555 }} 18556 } 18557 </script>
18557<div 18558 class="bp-pdp-gallery__indicator" 18559 data-bp-pdp-indicator 18560 aria-hidden="true" 18561 ></div></bp-pdp-gallery> 18562 18563 18564 18565 18566 18567 </div> 18568 18569 18570 18571 18572 18573 18574<div 18575 id="ProductInformation-template--29563577761958__main" 18576 class=" 18577 product-details 18578 sticky-content--desktop 18579 " 18580 style="--details-position: flex-start;" 18581 data-testid="product-information-details" 18582 18583> 18584 18585 18586 <div 18587 class=" 18588 group-block 18589 group-block--height-fit 18590 group-block--width-fill 18591 border-style 18592 spacing-style 18593 size-style 18594 18595 18596 " 18597 style=" 18598 --border-width: 1px; --border-style: none; --border-color: 18599rgb(var(--color-border-rgb) / 1.0); --border-radius: 186000px; 18601 18602 --padding-block-start: max(20px, calc(var(--spacing-scale) * 36px));--padding-block-end: max(20px, calc(var(--spacing-scale) * 24px));--padding-inline-start: 0px;--padding-inline-end: 0px; 18603 --size-style-width: 100%;--size-style-height: fit;--size-style-width-mobile: 100%; --size-style-width-mobile-min: 5rem; 18604 18605 " 18606 18607 data-testid="group-block" 18608><div class="group-block__media-wrapper"> 18609 18610 18611 18612 18613 18614 </div> 18615 18616 <div 18617 class=" 18618 group-block-content 18619 18620 layout-panel-flex 18621 layout-panel-flex--column 18622 18623 " 18624 style="--flex-direction: column; --flex-wrap: nowrap; --flex-wrap-mobile: wrap; 18625 18626--gap: 8px; 18627 18628--horizontal-alignment: ; --vertical-alignment: ; 18629--vertical-alignment-mobile: ; 18630" 18631 > 18632 <div class="view-product-title"> 18633 <a 18634 href="/products/boston-proper-printed-scarf-with-love-bp?variant=56389031657638" 18635 class="link" 18636 >Boston Proper Printed Scarf With Love BP 18637 </a> 18638 </div> 18639 18640 18641 18642<div 18643 class=" 18644 group-block 18645 group-block--height-fit 18646 group-block--width-fill 18647 border-style 18648 spacing-style 18649 size-style 18650 18651 18652 " 18653 style=" 18654 --border-width: 1px; --border-style: none; --border-color: 18655rgb(var(--color-border-rgb) / 1.0); --border-radius: 186560px; 18657 18658 --padding-block-start: 0px;--padding-block-end: 0px;--padding-inline-start: 0px;--padding-inline-end: 0px; 18659 --size-style-width: 100%;--size-style-height: fit;--size-style-width-mobile: 100%; --size-style-width-mobile-min: 5rem; 18660 18661 " 18662 18663 data-testid="group-block" 18664><div class="group-block__media-wrapper"> 18665 18666 18667 18668 18669 18670 </div> 18671 18672 <div 18673 class=" 18674 group-block-content 18675 18676 layout-panel-flex 18677 layout-panel-flex--column 18678 mobile-column 18679 " 18680 style="--flex-direction: column; --flex-wrap: nowrap; --flex-wrap-mobile: wrap; 18681 18682--gap: 8px; 18683 18684--horizontal-alignment: flex-start; --vertical-alignment: center; 18685--vertical-alignment-mobile: center; 18686" 18687 > 18688 <a class="bp-collection-eyebrow bp-collection-eyebrow--link" href="/collections/charmeuse" >Charmeuse</a> 18689 18690 18691<div 18692 class="bp-yotpo-stars yotpo-widget-instance" 18693 data-yotpo-instance-id="659034" 18694 data-yotpo-product-id="15286962618534" 18695 data-yotpo-cart-product-id="15286962618534" 18696 data-yotpo-section-id="product-rating" 18697 data-bp-product-url="/products/boston-proper-printed-scarf-with-love-bp" 18698 18699 ></div> 18700 18701 18702 18703 18704 18705 18706 18707 18708 18709 18710 18711 <div class=" spacing-style text-block text-block--AMHlxeTlWeC9RNHpmZ__text_xrnftG h3 text-block--align-left " style=" --padding-block-start: 0px; --padding-block-end:0px; --padding-inline-start:0px; --padding-inline-end:0px; --width: 100%; --max-width: var(--max-width--body-normal); --text-align: left; "> 18712 <h1>Boston Proper Printed Scarf With Love BP</h1> 18713 </div> 18714 18715 18716 18717 18718 18719 18720 18721 18722 18723<product-price 18724 class="text-block text-block--align-left rte text-left paragraph spacing-style" 18725 data-block-id="AU3BiYjdBRFBJMG1WV__price_tVjtKg" 18726 data-product-id="15286962618534" 18727 style=" 18728 18729--color: var(--color-foreground); 18730 --padding-block-start: 4px;--padding-block-end: 0px;--padding-inline-start: 0px;--padding-inline-end: 0px; 18731 --width: 100%; 18732 " 18733 18734> 18735 <div ref="priceContainer"> 18736 18737 18738 <div 18739 class=" 18740 price__regular 18741 18742 " 18743 > 18744 <span class="price">$50</span> 18745 </div> 18746 <div 18747 class=" 18748 price__sale 18749 18750 price__hidden 18751 18752 " 18753 > 18754
18755 <span class="price-item__group"> 18756 <span class="visually-hidden">Sale price </span> 18757 <span class="price-item--sale price">$50</span> 18758 </span> 18759 <span class="price-item__group"> 18760 <span class="visually-hidden">Regular price </span> 18761 <span class="price-item--regular compare-at-price">$50</span> 18762 </span> 18763 18764 </div> 18765 18766</div> 18767 18768 18769</product-price> 18770 18771 18772<span class="bp-promo-line" data-bp-promo-line >Free gift with purchase of $400+</span> 18773 18774 18775 18776 18777<ul class="bp-pills bp-pills--empty" data-bp-pills data-block-id="AWmVZMFVpYWlCSzg4M__bp_tag_pills" ></ul>
18777<script> 18778 (function () { 18779 var list = document.querySelector('[data-bp-pills][data-block-id="AWmVZMFVpYWlCSzg4M__bp_tag_pills"]'); 18780 if (!list) return; 18781 var pid = 15286962618534; 18782 // Defer the badge membership fetch (pathways.dxpapi.com, reached via 18783 // applyToPdpPills â fetchPids) to idle so it doesn't compete with the 18784 // LCP on the PDP. The pill is a minor "Popular / Trending" affordance â 18785 // fine to pop in shortly after paint. Mirrors bp-badges.js's card scan. 18786 var run = function () { 18787 var tries = 30; 18788 (function tick() { 18789 if (window.BpBadges) { window.BpBadges.applyToPdpPills(list, pid); return; } 18790 if (tries-- <= 0) return; 18791 setTimeout(tick, 100); 18792 })(); 18793 }; 18794 if ('requestIdleCallback' in window) { 18795 requestIdleCallback(run, { timeout: 2000 }); 18796 } else { 18797 setTimeout(run, 1200); 18798 } 18799 })(); 18800 </script>
18800 18801 18802 18803 18804</div> 18805</div> 18806 18807 18808 18809 18810 18811 18812 18813<div 18814 class="divider divider-AcUdqV0pEbE5HVHh2N__divider_VJhene spacing-style" 18815 style=" 18816 --divider-justify-content: center; 18817 --padding-block-start: 0px;--padding-block-end: 0px; 18818 " 18819 18820 18821> 18822 <span 18823 class="divider__line" 18824 style=" 18825 --divider-border-thickness: 1px; 18826 18827 --divider-border-rounded: 0; 18828 18829 --divider-flex-basis: 100%; 18830 " 18831 ></span> 18832</div> 18833 18834 18835 18836 18837 18838<div 18839 class="bp-colors" 18840 data-bp-colors 18841 data-bp-inv5-handle="boston-proper-printed-scarf" 18842 data-bp-current-handle="boston-proper-printed-scarf-with-love-bp" 18843 data-bp-group-tag="YGroup_2CT80" 18844 18845 > 18846 <p class="bp-colors__label"> 18847 <span class="bp-colors__label-prefix">Color</span> 18848 <span class="bp-colors__label-name" data-bp-active-color>With Love Bp</span> 18849 </p> 18850 <ul class="bp-colors__swatches" role="list"><li 18851 class="bp-colors__item bp-colors__item--pending" 18852 data-bp-color-name="Americana Bandana" 18853 data-bp-color-handle="boston-proper-printed-scarf-americana-bandana" 18854 data-bp-color-style="2CT8031" 18855 ><a 18856 class="bp-colors__swatch"href="/products/boston-proper-printed-scarf-americana-bandana" 18857 data-bp-swatch 18858 aria-label="Americana Bandana"style="background-image: url('https://media.bostonproper.com/i/bostonproper/2CT8031_2?$poiswatch$&img404=2CT8031_1');"></a><span class="bp-colors__discount" data-bp-color-discount aria-label="60% off">-60%</span></li><li 18859 class="bp-colors__item bp-colors__item--pending" 18860 data-bp-color-name="Florence Fleur" 18861 data-bp-color-handle="boston-proper-printed-scarf-florence-fleur" 18862 data-bp-color-style="2CT8091" 18863 ><a 18864 class="bp-colors__swatch"href="/products/boston-proper-printed-scarf-florence-fleur" 18865 data-bp-swatch 18866 aria-label="Florence Fleur"style="background-image: url('https://media.bostonproper.com/i/bostonproper/2CT8091_2?$poiswatch$&img404=2CT8091_1');"></a></li><li 18867 class="bp-colors__item bp-colors__item--pending" 18868 data-bp-color-name="Flowers Of Hope" 18869 data-bp-color-handle="boston-proper-printed-scarf-flowers-of-hope" 18870 data-bp-color-style="2CT8070" 18871 ><a 18872 class="bp-colors__swatch"href="/products/boston-proper-printed-scarf-flowers-of-hope" 18873 data-bp-swatch 18874 aria-label="Flowers Of Hope"style="background-image: url('https://media.bostonproper.com/i/bostonproper/2CT8070_2?$poiswatch$&img404=2CT8070_1');"></a></li><li 18875 class="bp-colors__item bp-colors__item--pending" 18876 data-bp-color-name="Iris Garden" 18877 data-bp-color-handle="boston-proper-printed-scarf-iris-garden" 18878 data-bp-color-style="2CT8090" 18879 ><a 18880 class="bp-colors__swatch"href="/products/boston-proper-printed-scarf-iris-garden" 18881 data-bp-swatch 18882 aria-label="Iris Garden"style="background-image: url('https://media.bostonproper.com/i/bostonproper/2CT8090_2?$poiswatch$&img404=2CT8090_1');"></a></li><li 18883 class="bp-colors__item bp-colors__item--pending" 18884 data-bp-color-name="Party Polka" 18885 data-bp-color-handle="boston-proper-printed-scarf-party-polka" 18886 data-bp-color-style="2CT8020" 18887 ><a 18888 class="bp-colors__swatch"href="/products/boston-proper-printed-scarf-party-polka" 18889 data-bp-swatch 18890 aria-label="Party Polka"style="background-image: url('https://media.bostonproper.com/i/bostonproper/2CT8020_2?$poiswatch$&img404=2CT8020_1');"></a></li><li 18891 class="bp-colors__item bp-colors__item--pending" 18892 data-bp-color-name="Stunning Sealife" 18893 data-bp-color-handle="boston-proper-printed-scarf-stunning-sealife" 18894 data-bp-color-style="2CT8030"
18895 ><a 18896 class="bp-colors__swatch"href="/products/boston-proper-printed-scarf-stunning-sealife" 18897 data-bp-swatch 18898 aria-label="Stunning Sealife"style="background-image: url('https://media.bostonproper.com/i/bostonproper/2CT8030_2?$poiswatch$&img404=2CT8030_1');"></a></li><li 18899 class="bp-colors__item is-active" 18900 data-bp-color-name="With Love Bp" 18901 data-bp-color-handle="boston-proper-printed-scarf-with-love-bp" 18902 data-bp-color-style="2CT8071" 18903 ><span 18904 class="bp-colors__swatch" 18905 data-bp-swatch 18906 aria-current="true"style="background-image: url('https://media.bostonproper.com/i/bostonproper/2CT8071_2?$poiswatch$&img404=2CT8071_1');"></span> 18907 <span class="bp-colors__sr">With Love Bp (current)</span></li></ul> 18908 </div> 18909 18910
18910<script> 18911 /* Canonical colour-family hydration is shared with the quick-add / quick-view 18912 drawer â see window.bpColorSiblings.hydrateRoot in snippets/bp-shop-globals. 18913 The union-crawl + canonical bp_style ordering used to live inline here; it 18914 now lives once in the shared helper so the PDP and the drawer render the 18915 identical drift-free swatch set + order (the drawer can't run this inline 18916 script tag â Horizon's morph doesn't execute theme-block inline scripts). */ 18917 (function () { 18918 var root = document.querySelector('[data-bp-colors][data-bp-inv5-handle="boston-proper-printed-scarf"]'); 18919 if (!root) return; 18920 var QV = ''; 18921 if (window.bpColorSiblings && window.bpColorSiblings.hydrateRoot) { 18922 window.bpColorSiblings.hydrateRoot(root, { qv: QV }); 18923 } else { 18924 // bpColorSiblings is rendered globally before this block, so this path is 18925 // defensive only: at minimum reveal the active swatch so the row isn't blank. 18926 var active = root.querySelector('.bp-colors__item.is-active'); 18927 if (active) active.classList.remove('bp-colors__item--pending'); 18928 } 18929 })(); 18930 </script>
18930 18931 18932 18933 18934 18935 18936 18937 18938 18939 18940 <variant-picker 18941 class="variant-picker spacing-style variant-picker--left" 18942 style="--color-strikethrough-mix: #000; --padding-block-start: 0px;--padding-block-end: 0px;--padding-inline-start: 0px;--padding-inline-end: 0px;" 18943 data-section-id="template--29563577761958__main" 18944 data-product-id="15286962618534" 18945 data-block-id="ARHVvWmlKWlNHRFpid__variant_picker_R3rGDr" 18946 data-product-url="/products/boston-proper-printed-scarf-with-love-bp" 18947 ref="mainVariantPicker" 18948 18949 data-template-product-match="true" 18950 18951 18952 18953 > 18954 <form class="variant-picker__form"><div 18955 class="variant-option" 18956 data-testid="variant-option-single" 18957 >Color<span 18958 class="variant-option__swatch-value" 18959 data-testid="variant-option-single-value" 18960 >With Love BP</span> 18961 </div><div 18962 class="variant-option" 18963 data-testid="variant-option-single" 18964 >Size<span 18965 class="variant-option__swatch-value" 18966 data-testid="variant-option-single-value" 18967 >One Size</span> 18968 </div>
18968<script type="application/json"> 18969 {"id":56389031657638,"title":"With Love BP \/ One Size","option1":"With Love BP","option2":"One Size","option3":null,"sku":"2CT8071000","requires_shipping":true,"taxable":true,"featured_image":{"id":80304586948774,"product_id":15286962618534,"position":1,"created_at":"2026-10-03T02:33:16-04:00","updated_at":"2026-10-03T02:33:18-04:00","alt":null,"width":1639,"height":2048,"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2CT8071_SET_e6f3e3ce-5a5f-451b-bc3b-ea1f8f71d236.jpg?v=1791009198","variant_ids":[56389031657638]},"available":true,"name":"Boston Proper Printed Scarf With Love BP - With Love BP \/ One Size","public_title":"With Love BP \/ One Size","options":["With Love BP","One Size"],"price":5000,"weight":0,"compare_at_price":5000,"inventory_management":"shopify","barcode":null,"featured_media":{"alt":null,"id":70645919449254,"position":1,"preview_image":{"aspect_ratio":0.8,"height":2048,"width":1639,"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2CT8071_SET_e6f3e3ce-5a5f-451b-bc3b-ea1f8f71d236.jpg?v=1791009198"}},"requires_selling_plan":false,"selling_plan_allocations":[],"quantity_rule":{"min":1,"max":null,"increment":1}} 18970 </script>
18970 18971 </form> 18972 </variant-picker> 18973 18974 18975 18976 18977 18978 18979 18980<div class="bp-size-chart-link" data-bp-size-chart-resolved="true"> 18981 <button 18982 type="button" 18983 class="bp-size-chart-link__btn" 18984 data-bp-size-chart-open 18985 aria-controls="bp-size-chart-drawer" 18986 aria-haspopup="dialog" 18987 ><span>Size Chart</span> 18988 </button></div> 18989 18990 18991<div 18992 data-bp-fma-mount 18993 18994 ><div 18995 class="tfc-fitrec-product" 18996 data-styleid="15286962618534" 18997 data-locale="en_US" 18998 data-userid="" 18999 ></div> 19000 </div> 19001 19002
19002<script> 19003 (function () { 19004 // Re-run TrueFit's calculate when this block paints so the 19005 // tfc-fitrec-product mount picks up the latest variant. Same 19006 // pattern as Atlas's useEffect in ProductMainForm. 19007 const calc = () => { 19008 if (typeof window.tfcapi === 'function') { 19009 try { window.tfcapi('calculate'); } catch (_) {} 19010 } 19011 }; 19012 calc(); 19013 document.addEventListener('variant:update', calc); 19014 document.addEventListener('variant:change', calc); 19015 })(); 19016 </script>
19016 19017 19018 19019 19020<div 19021 class="bp-notify-trigger" 19022 data-bp-notify-trigger 19023 data-section-product="15286962618534" 19024 data-bp-current-variant="56389031657638" 19025 data-bp-current-available="true" 19026 style="display: none;" 19027 19028 > 19029 <button 19030 type="button" 19031 class="bp-notify-trigger__btn" 19032 data-bp-notify-open 19033 data-bp-notify-variant-id="56389031657638" 19034 ><span>Notify Me When Available</span> 19035 </button> 19036 </div>
19036<script type="application/json" id="bp-notify-variants-15286962618534"> 19037 {"56389031657638": { 19038 "available": true, 19039 "fullPrice": true, 19040 "gates": { 19041 "isPreOrder": true, 19042 "dcDate": null, 19043 "currentInv": 2617, 19044 "inboundInv": 0 19045} 19046 19047 } 19048} 19049 </script>
19049 19050 19051
19051<script> 19052 (function () { 19053 const trigger = document.querySelector('[data-bp-notify-trigger][data-section-product="15286962618534"]'); 19054 if (!trigger) return; 19055 const table = JSON.parse(document.getElementById('bp-notify-variants-15286962618534').textContent || '{}'); 19056 // Track which variant is "current" so the trigger opens the drawer 19057 // for the right SKU even if the user hasn't clicked yet. 19058 let currentVariantId = parseInt(trigger.dataset.bpCurrentVariant, 10) || null; 19059 window.bpCurrentVariantId = currentVariantId; 19060 19061 const setVisible = (vid) => { 19062 // Re-query the trigger fresh each time: Horizon re-renders (morphs) the 19063 // buy box on variant:update, which can REPLACE the notify trigger â the 19064 // once-captured reference then points at a detached node, so toggling 19065 // it did nothing. Switching between two sold-out sizes hid the ATC 19066 // (Notify swap) but "showed" a detached trigger â the CTA vanished 19067 // entirely (QA 7/21). A fresh lookup targets the live element. 19068 const trigger = document.querySelector('[data-bp-notify-trigger][data-section-product="15286962618534"]'); 19069 if (!trigger) return; 19070 const entry = table[String(vid)] || {}; 19071 const available = entry.available !== false; 19072 const gates = entry.gates || {}; 19073 const currentInv = Number(gates.currentInv) || 0; 19074 const inboundInv = Number(gates.inboundInv) || 0; 19075 // Restock-eligible: see block doc comment (Atlas parity â 19076 // ProductMainForm.js's showVariantLevelNotifyMe). Only sold-out 19077 // variants that are full price, have no on-hand stock, and have 19078 // no inbound restock already scheduled get the Notify Me swap. 19079 const restockEligible = !available && !!entry.fullPrice && currentInv <= 0 && inboundInv <= 0; 19080 // Toggle Notify trigger 19081 trigger.style.display = restockEligible ? '' : 'none'; 19082 // Hide Horizon's ATC + accelerated checkout ONLY when the Notify Me 19083 // trigger is actually visible in its place. A sold-out-but-ineligible 19084 // variant keeps Horizon's own disabled "Sold out" button 19085 // (blocks/buy-buttons.liquid). And if the trigger can't render for any 19086 // reason (mid-morph / detached / hidden section), `offsetParent` is 19087 // null â keep the disabled out-of-stock button so the buy box is NEVER 19088 // left empty (Ernie 7/21: switching to a crossed-out size dropped the 19089 // CTA; a disabled out-of-stock button should show in that case). 19090 // Reading offsetParent forces the reflow so it reflects the display we 19091 // just set. We scope to the same product-information section so other 19092 // PDPs / quick-view modals aren't affected. 19093 const section = trigger.closest('[data-section-id]') || document; 19094 const notifyShown = restockEligible && trigger.offsetParent !== null; 19095 section.querySelectorAll( 19096 '.buy-buttons,' + 19097 'button[name="add"],' + 19098 'add-to-cart-component,' + 19099 'accelerated-checkout-component,' + 19100 '[data-shop-pay-button]' 19101 ).forEach((el) => { 19102 el.style.display = notifyShown ? 'none' : ''; 19103 }); 19104 // Keep the open trigger's variantId attribute current. 19105 const btn = trigger.querySelector('[data-bp-notify-open]'); 19106 if (btn) btn.setAttribute('data-bp-notify-variant-id', String(vid)); 19107 window.bpCurrentVariantId = vid; 19108 }; 19109 19110 // Initial render â DEFERRED. This block is parsed BEFORE the 19111 // buy-buttons block (its block_order precedes buy_buttons in 19112 // templates/product.json), and this inline script runs synchronously 19113 // during parse. Running setVisible now means the section's 19114 // querySelectorAll for button[name="add"] / add-to-cart-component 19115 // finds NOTHING (not parsed yet), so on an initial out-of-stock load 19116 // the Sold Out button is never hidden and BOTH it and Notify Me show 19117 // (QA 7/22). Wait until the DOM is parsed so the buy box exists. 19118 const initialRender = () => { 19119 setVisible(currentVariantId); 19120 requestAnimationFrame(() => setVisible(currentVariantId)); 19121 }; 19122 if (document.readyState === 'loading') { 19123 document.addEventListener('DOMContentLoaded', initialRender, { once: true }); 19124 } else { 19125 initialRender(); 19126 } 19127 19128 // Horizon dispatches `variant:update` (see assets/events.js 19129 // â ThemeEvents.variantUpdate = 'variant:update'). Also keep the 19130 // older `variant:change` / `variantchange` listeners for 19131 // forward-compat with theme apps that still dispatch those. 19132 const onChange = (e) => { 19133 const vid = 19134 e?.detail?.resource?.id || 19135 e?.detail?.variant?.id ||
19136 e?.detail?.id; 19137 if (!vid) return; 19138 // Run twice: once now, and once after Horizon finishes morphing the 19139 // buy box. `variant:update` can fire BEFORE the add-to-cart / buy- 19140 // buttons re-render, so a synchronous-only pass hid the ATC on the 19141 // pre-morph DOM and the freshly morphed "Sold out" button came back 19142 // visible OR was left in a half-applied state â switching from an 19143 // in-stock size to a crossed-out one dropped the CTA entirely 19144 // (Medium, Ernie 7/21). The rAF pass re-asserts on the FINAL DOM. 19145 setVisible(vid); 19146 requestAnimationFrame(() => setVisible(vid)); 19147 }; 19148 document.addEventListener('variant:update', onChange); 19149 document.addEventListener('variant:change', onChange); 19150 document.addEventListener('variantchange', onChange); 19151 19152 // Fallback observer â two jobs: 19153 // 1. Catch variant changes that don't come through an event. 19154 // 2. Re-assert after Horizon MORPHS the buy box. variant:update 19155 // triggers an ASYNC section re-render; the sync + rAF passes in 19156 // onChange run BEFORE that morph lands, so a freshly re-rendered 19157 // (default-visible) ATC/Sold-out button can override the Notify 19158 // swap, OR â when switching to a crossed-out size â the buy box is 19159 // briefly re-rendered and our stale display state leaves it empty 19160 // (QA 7/22, lemon-drop). Re-running setVisible on the post-morph 19161 // DOM guarantees exactly one of {ATC/Sold-out, Notify} is shown and 19162 // the buy box is never left blank. 19163 const sectionRoot = trigger.closest('[data-section-id]'); 19164 if (sectionRoot) { 19165 let rafPending = false; 19166 const obs = new MutationObserver((mutations) => { 19167 const input = sectionRoot.querySelector('input[name="id"]'); 19168 const vid = input ? parseInt(input.value, 10) : null; 19169 if (vid && vid !== window.bpCurrentVariantId) { setVisible(vid); return; } 19170 // Only react to NODE morphs (childList), not our own display 19171 // (attribute) writes â otherwise setVisible would loop. Coalesce 19172 // via rAF so a burst of morph mutations re-asserts once. 19173 const morphed = mutations.some((m) => 19174 m.type === 'childList' && (m.addedNodes.length || m.removedNodes.length)); 19175 if (!morphed || rafPending) return; 19176 rafPending = true; 19177 requestAnimationFrame(() => { 19178 rafPending = false; 19179 if (window.bpCurrentVariantId) setVisible(window.bpCurrentVariantId); 19180 }); 19181 }); 19182 obs.observe(sectionRoot, { attributes: true, childList: true, subtree: true }); 19183 } 19184 })(); 19185 </script>
19185 19186 19187 19188 19189 19190 19191<span 19192 class="buy-buttons-block buy-buttons-block--AU01KdzgrSDJ3K2Zxa__buy_buttons_eYQEYi" 19193 19194><product-form-component 19195 data-section-id="template--29563577761958__main" 19196 data-product-id="15286962618534" 19197 data-product-url="/products/boston-proper-printed-scarf-with-love-bp" 19198 on:submit="/handleSubmit" 19199 data-quantity-default="1" 19200 data-quantity-error-max="This item has a maximum of {{ maximum }}" 19201 > 19202 <div 19203 class="visually-hidden" 19204 aria-live="assertive" 19205 role="status" 19206 aria-atomic="true" 19207 ref="liveRegion" 19208 ></div><form method="post" action="/cart/add" id="BuyButtons-ProductForm-template--29563577761958__main" accept-charset="UTF-8" class="shopify-product-form" enctype="multipart/form-data" data-type="add-to-cart-form"><input type="hidden" name="form_type" value="product" /><input type="hidden" name="utf8" value="â" /><input 19209 type="hidden" 19210 name="id" 19211 ref="variantId" 19212 value="56389031657638" 19213 ><div 19214 class="product-form-buttons spacing-style product-form-buttons--stacked" 19215 style="--padding-block-start: 0px;--padding-block-end: 0px;--padding-inline-start: 0px;--padding-inline-end: 0px;" 19216 ref="productFormButtons" 19217 ><span 19218 class="product-form-text__error hidden" 19219 ref="addToCartTextError" 19220 > 19221 <span class="svg-wrapper"><svg aria-hidden="true" class="icon icon-error" viewBox="0 0 13 13"> 19222 <circle cx="6.5" cy="6.5" r="5.5" stroke="#fff" stroke-width="var(--icon-stroke-width)"/> 19223 <circle cx="6.5" cy="6.5" r="5.5" fill="#EB001B" stroke="#EB001B" stroke-width=".7"/> 19224 <path fill="#fff" d="m5.874 3.528.1 4.044h1.053l.1-4.044zm.627 6.133c.38 0 .68-.288.68-.656s-.3-.656-.68-.656-.681.288-.681.656.3.656.68.656"/> 19225 <path fill="#fff" stroke="#EB001B" stroke-width=".7" d="M5.874 3.178h-.359l.01.359.1 4.044.008.341h1.736l.008-.341.1-4.044.01-.359H5.873Zm.627 6.833c.56 0 1.03-.432 1.03-1.006s-.47-1.006-1.03-1.006-1.031.432-1.031 1.006.47 1.006 1.03 1.006Z"/> 19226</svg> 19227</span> 19228 </span> 19229 19230 19231 19232<div 19233 class="quantity-selector-wrapper" 19234 ref="quantitySelectorWrapper" 19235> 19236 <quantity-selector-component 19237 class="quantity-selector" 19238 data-variant-id="56389031657638" 19239 19240ref="quantitySelector" 19241 19242 > 19243 <button 19244 class="button quantity-minus button-unstyled" 19245 type="button" 19246 name="minus" 19247 on:click="/decreaseQuantity" 19248 ref="minusButton" 19249 19250 > 19251 <span class="visually-hidden">Decrease quantity</span 19252 ><span class="svg-wrapper icon-plus"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 19253 <path d="M2.75 7H11.25" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round"/> 19254</svg> 19255</span> 19256 </button> 19257 <input 19258 type="number" 19259 name="quantity" 19260 value="1" 19261 data-cart-quantity="0" 19262 min="1" 19263 data-min="1" 19264 on:blur="/setQuantity" 19265 on:focus="/selectInputValue" 19266 ref="quantityInput" 19267 aria-live="polite" 19268 aria-label="Quantity" 19269 19270 19271 step="1" 19272 19273 > 19274 <button 19275 class="button quantity-plus button-unstyled" 19276 type="button" 19277 name="plus" 19278 on:click="/increaseQuantity" 19279 ref="plusButton" 19280 19281 > 19282 <span class="visually-hidden">Increase quantity</span 19283 ><span class="svg-wrapper icon-plus"><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 19284 <path class="vertical" d="M2.75 7H11.25" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round"/> 19285 <path class="horizontal" d="M7 2.75L7 11.25" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round"/> 19286</svg> 19287</span> 19288 </button> 19289 </quantity-selector-component></div> 19290 19291 19292 19293 19294 19295 19296 19297 19298 19299<span 19300 19301 style="--add-to-cart-font-case: ;" 19302> 19303 <add-to-cart-component 19304 ref="addToCartButtonContainer" 19305 data-product-variant-media="//www.bostonproper.com/cdn/shop/files/2CT8071_SET_e6f3e3ce-5a5f-451b-bc3b-ea1f8f71d236.jpg?v=1791009198&width=100" 19306 data-add-to-cart-animation="true" 19307> 19308 <button 19309 19310 id="BuyButtons-ProductSubmitButton-AeE9EOHozN01sMFF3b__add-to-cart" 19311 19312 type="submit" 19313 name="add" 19314 ref="addToCartButton" 19315 on:click="/handleClick" 19316 class="button add-to-cart-button button" 19317 19318 data-testid="standalone-add-to-cart" 19319 19320 19321 > 19322 <span 19323 class="add-to-cart-text" 19324 > 19325
19326 <span 19327 aria-hidden="true" 19328 class="svg-wrapper add-to-cart-icon" 19329 ><svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="none"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="var(--icon-stroke-width)" d="M16.608 9.421V6.906H3.392v8.016c0 .567.224 1.112.624 1.513.4.402.941.627 1.506.627H8.63M8.818 3h2.333c.618 0 1.212.247 1.649.686a2.35 2.35 0 0 1 .683 1.658v1.562H6.486V5.344c0-.622.246-1.218.683-1.658A2.33 2.33 0 0 1 8.82 3"/><path stroke="currentColor" stroke-linecap="round" stroke-width="var(--icon-stroke-width)" d="M14.608 12.563v5m2.5-2.5h-5"/></svg> 19330</span> 19331 19332 <span class="add-to-cart-text__content"> 19333 <span> 19334 <span>Add to bag</span> 19335 </span> 19336 </span> 19337 </span> 19338 19339 <span class="add-to-cart__added"> 19340 <span class="svg-wrapper add-to-cart__added-icon"><svg aria-hidden="true" class="checkmark-burst" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> 19341 <g class="check"> 19342 <circle class="ring" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> 19343 <path class="tick" d="M9 12.75L11.25 15L15 9.75" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> 19344 </g> 19345 <g class="burst"> 19346 <g style="--index: 0;"> 19347 <line class="line" stroke-linecap="round" pathLength="1" x1="12" y1="8.5" x2="12" y2="15.5" stroke="currentColor"/> 19348 </g> 19349 <g style="--index: 1;"> 19350 <line class="line" stroke-linecap="round" pathLength="1" x1="12" y1="8.5" x2="12" y2="15.5" stroke="currentColor"/> 19351 </g> 19352 <g style="--index: 2;"> 19353 <line class="line" stroke-linecap="round" pathLength="1" x1="12" y1="8.5" x2="12" y2="15.5" stroke="currentColor"/> 19354 </g> 19355 <g style="--index: 3;"> 19356 <line class="line" stroke-linecap="round" pathLength="1" x1="12" y1="8.5" x2="12" y2="15.5" stroke="currentColor"/> 19357 </g> 19358 <g style="--index: 4;"> 19359 <line class="line" stroke-linecap="round" pathLength="1" x1="12" y1="8.5" x2="12" y2="15.5" stroke="currentColor"/> 19360 </g> 19361 <g style="--index: 5;"> 19362 <line class="line" stroke-linecap="round" pathLength="1" x1="12" y1="8.5" x2="12" y2="15.5" stroke="currentColor"/> 19363 </g> 19364 <g style="--index: 6;"> 19365 <line class="line" stroke-linecap="round" pathLength="1" x1="12" y1="8.5" x2="12" y2="15.5" stroke="currentColor"/> 19366 </g> 19367 <g style="--index: 7;"> 19368 <line class="line" stroke-linecap="round" pathLength="1" x1="12" y1="8.5" x2="12" y2="15.5" stroke="currentColor"/> 19369 </g> 19370 </g> 19371</svg> 19372</span> 19373 </span> 19374 </button> 19375</add-to-cart-component> 19376 19377</span> 19378 19379 19380 19381 19382 19383 19384 19385 19386 19387 19388 <div 19389 class="accelerated-checkout-block" 19390 ref="acceleratedCheckoutButtonContainer" 19391 19392 19393 19394 > 19395 19396 19397 <div data-shopify="payment-button" class="shopify-payment-button"> <shopify-accelerated-checkout recommended="{"supports_subs":true,"supports_def_opts":false,"name":"shop_pay","wallet_params":{"shopId":52960723110,"merchantName":"Boston Proper","personalized":true}}" fallback="{"supports_subs":true,"supports_def_opts":true,"name":"buy_it_now","wallet_params":{}}" access-token="eb9ee10d84a005c2b8e778780263a9d2" buyer-country="US" buyer-locale="en" buyer-currency="USD" variant-params="[{"id":56389031657638,"requiresShipping":true}]" shop-id="52960723110" enabled-flags="["1172c16a","a1d1f9a1","f5afabb3","370070f3"]" > <div class="shopify-payment-button__button" role="button" disabled aria-hidden="true" style="background-color: transparent; border: none"> <div class="shopify-payment-button__skeleton"> </div> </div> <div class="shopify-payment-button__more-options shopify-payment-button__skeleton" role="button" disabled aria-hidden="true"> </div> </shopify-accelerated-checkout> </div> 19398 19399 19400 </div> 19401 19402 19403 19404 19405 19406 19407 19408 </div><input type="hidden" name="product-id" value="15286962618534" /><input type="hidden" name="section-id" value="template--29563577761958__main" /></form></product-form-component></span> 19409 19410 19411 19412 19413 19414 19415 19416<bp-require-size ></bp-require-size> 19417 19418 19419 19420 19421 19422 19423<bp-line-item-props data-initial-vid="56389031657638"> 19424
19424<script type="application/json" class="bp-lip-data"> 19425 { 19426 "static": { 19427 "_final_sale": "no" 19428 }, 19429 "variants": {"56389031657638": { 19430 "_catalog_id": "799", 19431 "_price_status": "Full Price", 19432 "compare_at_price": 50.0, 19433 "price": "$50.00" 19434 } 19435} 19436} 19437 19438 </script>
19438 19439 </bp-line-item-props> 19440<p class="bp-pay-in-4" > 19441 Pay in full or in 4 interest-free installments with 19442 <img class="bp-pay-in-4__shop-pay-mark" src="//www.bostonproper.com/cdn/shop/t/1521/assets/bp-shop-pay-logo.svg?v=68994295948588378181790713714" alt="Shop Pay" width="42" height="10" loading="lazy" style="width:auto;height:12px;"> 19443</p> 19444 19445 19446 19447 19448<p 19449 class="bp-preorder-line" 19450 data-bp-preorder-line 19451 data-section-product="15286962618534" 19452 hidden 19453 > 19454 <span class="bp-preorder-line__label">Expected to ship:</span> 19455 <span class="bp-preorder-line__date" data-bp-preorder-date></span> 19456 </p> 19457 19458
19458<script type="application/json" id="bp-preorder-variants-15286962618534"> 19459 { 19460 "_originalOverride": null, 19461 "variants": {"56389031657638": { 19462 "isPreOrder": true, 19463 "dcDate": null, 19464 "currentInv": 2617, 19465 "inboundInv": 0 19466} 19467 19468} 19469 } 19470 </script>
19470 19471 19472
19472<script> 19473 (function () { 19474 const line = document.querySelector('[data-bp-preorder-line][data-section-product="15286962618534"]'); 19475 if (!line) return; 19476 const data = JSON.parse(document.getElementById('bp-preorder-variants-15286962618534').textContent || '{}'); 19477 const variants = data.variants || {}; 19478 const originalOverride = data._originalOverride || ''; 19479 const dateEl = line.querySelector('[data-bp-preorder-date]'); 19480 // Atlas hardcodes shippingInventoryThreshold = 1 in 19481 // updateLineItemProperties.js:31. Same here â schema knob is 19482 // ignored to keep the gate deterministic across environments. 19483 const THRESHOLD = 1; 19484 19485 // Auto-enable debug when the URL carries ?bp-preorder-debug=1 so 19486 // merchants can share a link that produces console output without 19487 // asking them to flip a global in DevTools. 19488 try { 19489 if (new URLSearchParams(location.search).get('bp-preorder-debug') === '1') { 19490 window.bpPreorderDebug = true; 19491 } 19492 } catch (_) {} 19493 // Always park the init snapshot on `window.bpPreorderInit` (a 19494 // per-product map so multiple product blocks don't clobber each 19495 // other) so it's inspectable from DevTools even without the 19496 // debug flag: just type `bpPreorderInit` in console. 19497 window.bpPreorderInit = window.bpPreorderInit || {}; 19498 window.bpPreorderInit[15286962618534] = { 19499 productId: 15286962618534, 19500 threshold: THRESHOLD, 19501 originalOverride, 19502 variants 19503 }; 19504 if (window.bpPreorderDebug) { 19505 console.log('[bp-preorder] init', window.bpPreorderInit[15286962618534]); 19506 } 19507 19508 // Mirror the displayed date into a hidden line-item property on the 19509 // product add-to-cart form so the cart line can render it (the cart 19510 // reads it LIP-only â no metafield fallback). Underscore-prefixed 19511 // `_expected_ship_date` matches Atlas (updateLineItemProperties.js) 19512 // and the Save-for-Later path; it stays hidden from Shopify's native
19513 // UI while BP renders it manually. Disabled when no pre-order date so 19514 // FormData omits it entirely (Atlas clears it to "" for the same reason). 19515 const LIP_NAME = 'properties[_expected_ship_date]'; 19516 // BP: was `line.closest('[data-section-id]')`, which never 19517 // matched anything â that attribute only exists on 19518 // <product-form-component> (blocks/buy-buttons.liquid), and this 19519 // block is buy-buttons' SIBLING, not its descendant, so the 19520 // closest() walk up from `line` never reached it. sectionRoot was 19521 // silently always null, ensureLip() always returned null, and the 19522 // hidden LIP input was never created â the date showed on the PDP 19523 // (that doesn't depend on sectionRoot) but never reached the cart. 19524 // #ProductInformation-template--29563577761958__main (blocks/_product-details.liquid) 19525 // is the actual shared ancestor of every block on the product, 19526 // including buy-buttons, so anchor there instead. 19527 const sectionRoot = line.closest('.product-details'); 19528 let lipInput = null; 19529 // Lazily resolve the add-to-cart form + hidden input. The form may 19530 // parse after this block (block order isn't guaranteed), so resolve 19531 // on demand rather than once up front. 19532 const ensureLip = () => { 19533 if (lipInput && lipInput.isConnected) return lipInput; 19534 const form = sectionRoot && sectionRoot.querySelector('form[action$="/cart/add"]'); 19535 if (!form) return null; 19536 lipInput = form.querySelector('input[name="' + LIP_NAME + '"]'); 19537 if (!lipInput) { 19538 lipInput = document.createElement('input'); 19539 lipInput.type = 'hidden'; 19540 lipInput.name = LIP_NAME; 19541 lipInput.setAttribute('data-bp-expected-ship-lip', ''); 19542 lipInput.disabled = true; 19543 form.appendChild(lipInput); 19544 } 19545 return lipInput; 19546 }; 19547 const syncLip = (value) => { 19548 const input = ensureLip(); 19549 if (!input) return; 19550 if (value) { 19551 input.value = value; 19552 input.disabled = false; 19553 } else { 19554 input.value = ''; 19555 input.disabled = true; 19556 } 19557 }; 19558 19559 // Gate check + weekend/holiday date math now live in 19560 // window.bpPreorder (assets/bp-preorder-dates.js), shared with 19561 // the cart drawer/cart page (snippets/bp-cart-line-item.liquid) 19562 // so both surfaces show/hide and format the date identically. 19563 const shouldShow = (entry) => { 19564 const ok = window.bpPreorder.shouldShow(entry); 19565 if (!ok) console.log('[bp-preorder] hidden', entry || 'no entry for variant'); 19566 return ok; 19567 }; 19568 const formatDate = (raw) => window.bpPreorder.formatDate(raw, originalOverride); 19569 19570 // Move the rendered <p> next to the Color label, right after 19571 // "Color Olive Night" (Figma: "Color Olive Night EXPECTED 19572 // SHIP DATE: MAY 18, 2026"). The Color row is NOT part of 19573 // Horizon's native variant-option fieldset system â BP fully 19574 // replaces it with blocks/bp-color-siblings.liquid, which renders 19575 // <p class="bp-colors__label"><span class="â¦prefix">Color</span> 19576 // <span class="â¦name">Olive Night</span></p> inside either 19577 // [data-bp-colors] (INV5/INV7 sibling-product colorways) or 19578 // [data-bp-variant-colors] (same-product Color variant, no 19579 // sibling products). Append inside that <p> â .bp-colors__label 19580 // is a flex row with `gap`, so the line spaces itself 19581 // automatically without extra margin. 19582 // 19583 // Falls back to the first native variant-option fieldset (usually 19584 // Size) when neither BP color block rendered â e.g. a product 19585 // with no color concept at all â so the line still shows up 19586 // somewhere instead of staying silently detached. 19587 const relocateToLegend = () => { 19588 const root = sectionRoot || document; 19589 // Evict any stale preorder line parked elsewhere by a previous 19590 // swatch/product morph (incl. a different colorway's product id) 19591 // before re-placing ours â prevents duplicate/stacked ship-date lines. 19592 root.querySelectorAll('.bp-preorder-line').forEach((el) => { 19593 if (el !== line) el.remove(); 19594 }); 19595 // Place the ship-date line directly BELOW the size chart row (QA: 19596 // Amber â move "Expected to ship" below the size chart, on the PDP and 19597 // in the quick-add flyout). Insert as a sibling right after the size 19598 // chart link block. 19599 const sizeChart = root.querySelector('.bp-size-chart-link'); 19600 if (sizeChart) { 19601 if (sizeChart.nextElementSibling !== line) { 19602 sizeChart.insertAdjacentElement('afterend', line); 19603 } 19604 return; 19605 } 19606 // Fallback (no size chart, e.g. BWL/collective): sit it next to the 19607 // Color label as before, so it still shows somewhere. 19608 const colorLabel = root.querySelector( 19609 '[data-bp-colors] .bp-colors__label, [data-bp-variant-colors] .bp-colors__label' 19610 ); 19611 const mount = colorLabel || root.querySelector('fieldset.variant-option legend'); 19612 if (mount && line.parentElement !== mount) { 19613 mount.appendChild(line); 19614 } 19615 }; 19616 relocateToLegend(); 19617 19618 // A size must be actively chosen before we commit to a ship date â 19619 // it's variant-specific, and the buy box requires selection. If any 19620 // multi-value option fieldset has no checked radio, treat it as 19621 // "no size selected" and hide the line. (Merged back from main â 19622 // the refactor dropped this gate, so the date showed pre-selection.) 19623 const bpSizeSelected = () => { 19624 const root = sectionRoot || document; 19625 const picker = root.querySelector('variant-picker'); 19626 if (!picker) return true; 19627 const fs = picker.querySelectorAll('fieldset.variant-option'); 19628 for (let i = 0; i < fs.length; i++) { 19629 if (fs[i].querySelectorAll('input[type="radio"]').length <= 1) continue; 19630 if (!fs[i].querySelector('input[type="radio"]:checked')) return false;
19631 } 19632 return true; 19633 }; 19634 19635 const update = (variantId, source) => { 19636 console.log('[bp-preorder] update()', { variantId, source }); 19637 if (!bpSizeSelected()) { 19638 line.hidden = true; 19639 syncLip(''); 19640 return; 19641 } 19642 const entry = variants[String(variantId)]; 19643 if (!shouldShow(entry)) { 19644 line.hidden = true; 19645 syncLip(''); 19646 return; 19647 } 19648 const formatted = formatDate(entry.dcDate); 19649 if (!formatted) { 19650 console.log('[bp-preorder] formatDate returned null', { dcDate: entry.dcDate, originalOverride }); 19651 line.hidden = true; 19652 syncLip(''); 19653 return; 19654 } 19655 console.log('[bp-preorder] SHOWING', { variantId, formatted, entry }); 19656 dateEl.textContent = formatted; 19657 line.hidden = false; 19658 syncLip(formatted); 19659 }; 19660 19661 // Initial selected variant 19662 update(window.bpCurrentVariantId || 56389031657638, 'init'); 19663 19664 // React to variant changes. Horizon fires `variant:update` on 19665 // document via events.js â bubbles=true, detail.resource.id is 19666 // the new variant id. Keep the other names as fallbacks in case 19667 // another integration dispatches them. 19668 const onChange = (e) => { 19669 const vid = 19670 e?.detail?.resource?.id || 19671 e?.detail?.variant?.id || 19672 e?.detail?.id; 19673 console.log('[bp-preorder] event', e.type, { vid, detail: e.detail }); 19674 if (!vid) return; 19675 update(vid, e.type); 19676 }; 19677 document.addEventListener('variant:update', onChange); 19678 document.addEventListener('variant:change', onChange); 19679 document.addEventListener('variantchange', onChange); 19680 document.addEventListener('variant:selected', onChange); 19681 19682 // Horizon's variant picker morphs its own <fieldset>/<legend> on 19683 // variant change (variant-picker.js:399). That morph removes the 19684 // <p> we just appended to the legend. Re-attach after each morph. 19685 document.addEventListener('variant:update', () => setTimeout(relocateToLegend, 0)); 19686 19687 // Fallback â watch the product form's hidden input for id changes. 19688 // sectionRoot was resolved above (inside ensureLip); reuse it here. 19689 if (sectionRoot) { 19690 let lastSeen = null; 19691 const obs = new MutationObserver(() => { 19692 const input = sectionRoot.querySelector('input[name="id"]'); 19693 if (!input) return; 19694 const vid = parseInt(input.value, 10); 19695 if (vid && vid !== lastSeen) { 19696 lastSeen = vid; 19697 update(vid); 19698 } 19699 }); 19700 obs.observe(sectionRoot, { attributes: true, childList: true, subtree: true }); 19701 } 19702 })(); 19703 </script>
19703 19704 19705 19706 19707 19708 19709 19710 19711 19712 19713 19714 19715 19716 19717 19718<div class="bp-acc" data-bp-acc ><details class="bp-acc__item" name="bp-acc-AaTJqeCs3S1EvVmVPc__bp_product_accordion"> 19719 <summary class="bp-acc__summary"> 19720 <span>Product Detail</span> 19721 <svg class="bp-acc__chevron" viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> 19722 <polyline points="6 9 12 15 18 9"/> 19723 </svg> 19724 </summary> 19725 <div class="bp-acc__body"><p class="bp-acc__intro">Add a versatile pop of style with our Boston Proper Printed Scarf. This lightweight essential is perfect as a headscarf, necktie, or bag accent. Effortlessly elevate casual outfits, office wear, or vacation looks with this chic, year-round accessory.</p><ul class="bp-acc__list"><li>100% polyester</li><li>Hand wash</li><li>Imported</li></ul><p class="bp-acc__sku" data-bp-acc-sku> 19726 SKU: <span data-bp-detail-sku>2CT8071</span> 19727 </p> 19728 </div> 19729 </details><details class="bp-acc__item" name="bp-acc-AaTJqeCs3S1EvVmVPc__bp_product_accordion"> 19730 <summary class="bp-acc__summary"> 19731 <span>Size & Fit</span> 19732 <svg class="bp-acc__chevron" viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> 19733 <polyline points="6 9 12 15 18 9"/> 19734 </svg> 19735 </summary> 19736 <div class="bp-acc__body"> 19737 <ul class="bp-acc__list"><li>27 inch L X 27 inch W</li><li>One Size</li></ul> 19738 </div> 19739 </details><details class="bp-acc__item" name="bp-acc-AaTJqeCs3S1EvVmVPc__bp_product_accordion"> 19740 <summary class="bp-acc__summary"> 19741 <span>Shipping & Returns</span> 19742 <svg class="bp-acc__chevron" viewBox="0 0 24 24" width="16" height="16" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> 19743 <polyline points="6 9 12 15 18 9"/> 19744 </svg> 19745 </summary> 19746 <div class="bp-acc__body"><table class="bp-acc__ship-table"> 19747 <thead> 19748 <tr> 19749 <th></th> 19750 <th>Estimated Delivery*</th> 19751 <th>Price</th> 19752 </tr> 19753 </thead> 19754 <tbody> 19755 <tr><td>Standard Shipping</td><td>3-7 Business Days</td><td>$9.95</td></tr> 19756 <tr><td>Two Day Shipping</td><td>2 Business Days</td><td>$18.90</td></tr> 19757 <tr><td>Next Day Shipping</td><td>Next Business Day</td><td>$24.90</td></tr> 19758 </tbody> 19759 </table> 19760 <p>*Delivery estimates are for <strong>in-stock items only</strong>. Two Day and Next Day estimates are for orders placed by 10AM EST.</p> 19761 <p>We offer easy online returns and exchanges within 30 days of shipment date, <strong>excluding clearance items marked as Final Sale</strong>. Exchanges for like items (same style and color in a different size) are free. For returns, a $6.95 return postage charge will be deducted from your refund. <a href="/pages/returns-exchanges">View the Returns & Exchanges policy.</a></p></div> 19762 </details></div>
19762<script type="application/json" class="bp-acc-sku-data" data-block="AaTJqeCs3S1EvVmVPc__bp_product_accordion"> 19763 {"56389031657638": "2CT8071"} 19764 </script>
19764 19765
19765<script> 19766 (function () { 19767 var dataEl = document.querySelector('script.bp-acc-sku-data[data-block="AaTJqeCs3S1EvVmVPc__bp_product_accordion"]'); 19768 if (!dataEl) return; 19769 var acc = dataEl.previousElementSibling; 19770 var span = acc && acc.querySelector('[data-bp-detail-sku]'); 19771 var wrap = acc && acc.querySelector('[data-bp-acc-sku]'); 19772 if (!span) return; 19773 var map; 19774 try { map = JSON.parse(dataEl.textContent || '{}'); } catch (_) { return; } 19775 document.addEventListener('variant:update', function (e) { 19776 var vid = e && e.detail && e.detail.resource && e.detail.resource.id; 19777 if (!vid) return; 19778 var sku = map[String(vid)]; 19779 if (sku == null) return; 19780 span.textContent = sku; 19781 if (wrap) wrap.hidden = (sku === ''); 19782 }); 19783 })(); 19784 </script>
19784 19785 19786 19787<bp-ctl 19788 class="bp-ctl" 19789 data-bp-ctl 19790 data-bp-ctl-style="2CT8071" 19791 data-bp-ctl-max="4" 19792 data-bp-ctl-amplience="https://media.bostonproper.com" 19793 hidden 19794 19795 > 19796 <h2 class="bp-ctl__heading">Complete The Look</h2> 19797 <div class="bp-ctl__carousel" data-bp-ctl-carousel> 19798 <button 19799 type="button" 19800 class="bp-ctl__arrow bp-ctl__arrow--prev" 19801 data-bp-ctl-prev 19802 aria-label="View previous outfit item" 19803 > 19804 <svg xmlns="http://www.w3.org/2000/svg" width="6" height="13" viewBox="0 0 6 13" fill="none" style="transform: rotate(180deg);"> 19805 <path d="M0 1.26164L1.40796 0.020813L6 6.30252L1.40796 12.7941L0 11.5168L3.67363 6.30252L0 1.26164Z" fill="#232323"/> 19806 </svg> 19807 </button> 19808 <ul class="bp-ctl__rail" data-bp-ctl-rail role="list"></ul> 19809 <button 19810 type="button" 19811 class="bp-ctl__arrow bp-ctl__arrow--next" 19812 data-bp-ctl-next 19813 aria-label="View next outfit item" 19814 > 19815 <svg xmlns="http://www.w3.org/2000/svg" width="6" height="13" viewBox="0 0 6 13" fill="none"> 19816 <path d="M0 1.26164L1.40796 0.020813L6 6.30252L1.40796 12.7941L0 11.5168L3.67363 6.30252L0 1.26164Z" fill="#232323"/> 19817 </svg> 19818 </button> 19819 </div> 19820 </bp-ctl> 19821 19822 19823 19824 19825 19826 19827 19828 19829
19830<script type="application/ld+json"> 19831 { 19832 "@context": "https://schema.org", 19833 "@type": "Product", 19834 "name": "Boston Proper Printed Scarf With Love BP", 19835 "url": "https://www.bostonproper.com/products/boston-proper-printed-scarf-with-love-bp", 19836 "sku": "2CT8071", 19837 "description": "", 19838 "image": ["//www.bostonproper.com/cdn/shop/files/2CT8071_SET_e6f3e3ce-5a5f-451b-bc3b-ea1f8f71d236.jpg?v=1791009198&width=1200", 19839"//www.bostonproper.com/cdn/shop/files/2_af52291f-802d-4184-8815-c64ea15071dd.jpg?v=1791009198&width=1200" 19840], 19841 "brand": { "@type": "Brand", "name": "Boston Proper" }, 19842 "color": "With Love BP", 19843 "material": "100% Polyester", 19844 "pattern": null, 19845 "audience": { "@type": "PeopleAudience", "suggestedGender": "female" }, 19846 "additionalProperty": [{ "@type": "PropertyValue", "name": "Product Detail", "value": "100% polyester" },{ "@type": "PropertyValue", "name": "Product Detail", "value": "Hand wash" },{ "@type": "PropertyValue", "name": "Product Detail", "value": "Imported" },{ "@type": "PropertyValue", "name": "Product Fit", "value": "27 inch L X 27 inch W" },{ "@type": "PropertyValue", "name": "Product Fit", "value": "One Size" },{ "@type": "PropertyValue", "name": "Style", "value": "2CT8071" }, 19847 { "@type": "PropertyValue", "name": "Occasion", "value": "Work " }, 19848 { "@type": "PropertyValue", "name": "Neckline", "value": null }, 19849 { "@type": "PropertyValue", "name": "Care Instructions", "value": "Hand wash" } 19850 ],"aggregateRating": { 19851 "@type": "AggregateRating", 19852 "ratingValue": "4.8", 19853 "reviewCount": "75", 19854 "bestRating": "5", 19855 "worstRating": "1" 19856 },"offers": { 19857 "@type": "AggregateOffer", 19858 "priceCurrency": "USD", 19859 "lowPrice": "50.0", 19860 "highPrice": "50.0", 19861 "offerCount": 1, 19862 "offers": [{ 19863 "@type": "Offer", 19864 "sku": "2CT8071000", 19865 "url": "https://www.bostonproper.com/products/boston-proper-printed-scarf-with-love-bp?variant=56389031657638", 19866 "price": "50.0", 19867 "priceCurrency": "USD", 19868 "priceValidUntil": "2026-12-31", 19869 "availability": "https://schema.org/InStock", 19870 "itemCondition": "https://schema.org/NewCondition", 19871 "hasMerchantReturnPolicy": { 19872 "@type": "MerchantReturnPolicy", 19873 "applicableCountry": "US", 19874 "returnPolicyCategory": "https://schema.org/MerchantReturnFiniteReturnWindow", 19875 "merchantReturnDays": 60, 19876 "returnMethod": "https://schema.org/ReturnByMail", 19877 "returnFees": "https://schema.org/ReturnShippingFees" 19878 }, 19879 "shippingDetails": { 19880 "@type": "OfferShippingDetails", 19881 "shippingRate": { "@type": "MonetaryAmount", "value": "9.95", "currency": "USD" } 19882 } 19883 } 19884] 19885 } 19886 } 19887 </script>
19887<script type="application/ld+json"> 19888 { 19889 "@context": "https://schema.org", 19890 "@type": "BreadcrumbList", 19891 "itemListElement": [ 19892 { "@type": "ListItem", "position": 1, "name": "Home", "item": "https:\/\/www.bostonproper.com\/" },{ "@type": "ListItem", "position": 2, "name": "All", "item": "https://www.bostonproper.com/collections/all" }, 19893 { "@type": "ListItem", "position": 3, "name": "Boston Proper Printed Scarf With Love BP", "item": "https://www.bostonproper.com/products/boston-proper-printed-scarf-with-love-bp" }] 19894 } 19895 </script>
19895 19896 19897 </div> 19898</div> 19899 19900 19901 19902</div> 19903 19904 19905 19906 19907 19908 19909 19910 19911 </div> 19912 19913 19914 19915 19916</div> 19917 19918 19919 19920 19921</div><section id="shopify-section-template--29563577761958__bp_how_to_wear_it" class="shopify-section"><bp-htwi 19922 class="bp-htwi bp-stylitics" 19923 data-bp-htwi 19924 data-bp-htwi-style="2CT8071" 19925 data-bp-htwi-max="6" 19926 data-bp-htwi-product-id="15286962618534" 19927 data-section-id="template--29563577761958__bp_how_to_wear_it" 19928 > 19929 <div id="how-to-wear-it"></div> 19930 <h2 class="bp-htwi__heading bp-stylitics__heading" data-bp-htwi-heading hidden>How to Wear It</h2><p class="bp-htwi__subtitle stylitics-composer-subtitle" data-bp-htwi-subtitle hidden>On Model Look</p><div class="bp-stylitics__target stylitics-composer-widget" data-bp-htwi-target></div> 19931 </bp-htwi> 19932 19933 19934 19935 19936</section><section id="shopify-section-template--29563577761958__bp_collection_details_7WJmXd" class="shopify-section"><style data-bp-section-knobs="template--29563577761958__bp_collection_details_7WJmXd"> 19937 [data-bp-section][data-section-id="template--29563577761958__bp_collection_details_7WJmXd"] { 19938 --bp-knobs-pad-x-d: 40px; 19939 --bp-knobs-pad-x-m: 16px; 19940 --bp-knobs-pad-y-d: 40px; 19941 --bp-knobs-pad-y-m: 32px; 19942 19943 19944 19945 --bp-knobs-align-d: start; 19946 --bp-knobs-align-m: start; 19947 } 19948</style> 19949 19950 19951<bp-collection-details 19952 class="bp-cd" 19953 data-bp-section 19954 data-section-id="template--29563577761958__bp_collection_details_7WJmXd" 19955 data-bp-mode-d="padded" 19956 data-bp-mode-m="padded" 19957 data-collection-handle="" 19958 data-namespace="custom" 19959 data-key="pdp_collection_story" 19960 data-max-cards="5" 19961 data-sf-token="04534ca2c4aa793f02c0c735d5d6160c" 19962 hidden 19963> 19964 <div class="bp-section__inner bp-cd__inner"> 19965 <div class="bp-cd__head" data-bp-cd-head hidden> 19966 <h2 class="bp-section__title bp-cd__headline" data-bp-cd-headline></h2> 19967 <p class="bp-cd__subcopy" data-bp-cd-subcopy></p> 19968 </div> 19969 <div class="bp-cd__grid" data-bp-cd-grid></div> 19970 </div> 19971</bp-collection-details> 19972 19973 19974 19975 19976 19977 19978</section><section id="shopify-section-template--29563577761958__bp_yotpo_reviews" class="shopify-section bp-pdp-extras-section"><div class="bp-pdp-extras" data-section-id="template--29563577761958__bp_yotpo_reviews"> 19979 <section id="bp-pdp-reviews" class="bp-pdp-extras__reviews"><div 19980 class="yotpo-widget-instance" 19981 data-yotpo-instance-id="437236" 19982 data-yotpo-product-id="15286962618534" 19983 data-yotpo-name="Boston Proper Printed Scarf With Love BP" 19984 data-yotpo-url="https://www.bostonproper.com/products/boston-proper-printed-scarf-with-love-bp" 19985 data-yotpo-image-url="https://www.bostonproper.com/cdn/shop/files/2CT8071_SET_e6f3e3ce-5a5f-451b-bc3b-ea1f8f71d236_large.jpg?v=1791009198" 19986 data-yotpo-price="50.0" 19987 data-yotpo-currency="USD" 19988 data-yotpo-description="" 19989 ></div> 19990 </section> 19991 19992
19992<script> 19993 (function () { 19994 // #bp-pdp-reviews landing: the browser's native hash-scroll fires 19995 // before the Yotpo widget + lazy images hydrate, so the user ends 19996 // up at the wrong Y-offset once content settles. Suppress the 19997 // initial jump, then re-scroll once the section stops growing OR a 19998 // hard timeout expires. 19999 if (window.location.hash !== '#bp-pdp-reviews') return; 20000 if (window.__bpPdpReviewsScrollScheduled) return; 20001 window.__bpPdpReviewsScrollScheduled = true; 20002 20003 try { if ('scrollRestoration' in history) history.scrollRestoration = 'manual'; } catch (_) {} 20004 window.scrollTo(0, 0); 20005 20006 var target = document.getElementById('bp-pdp-reviews'); 20007 if (!target) return; 20008 20009 var HEADER_OFFSET_VAR = '--header-group-height'; 20010 var STABLE_MS = 500; 20011 var HARD_TIMEOUT = 6000; 20012 var lastMutation = Date.now(); 20013 var scrolled = false; 20014 20015 function headerOffset() { 20016 var css = getComputedStyle(document.body).getPropertyValue(HEADER_OFFSET_VAR); 20017 var n = parseInt(css, 10); 20018 return isFinite(n) ? n : 0; 20019 } 20020 function doScroll() { 20021 if (scrolled) return; 20022 scrolled = true; 20023 var rect = target.getBoundingClientRect(); 20024 var y = rect.top + window.pageYOffset - headerOffset() - 16; 20025 window.scrollTo({ top: Math.max(0, y), behavior: 'smooth' }); 20026 } 20027 20028 var mo = new MutationObserver(function () { lastMutation = Date.now(); }); 20029 mo.observe(target, { childList: true, subtree: true, attributes: true }); 20030 20031 var start = Date.now(); 20032 var poll = setInterval(function () { 20033 var elapsed = Date.now() - start; 20034 var quiet = Date.now() - lastMutation; 20035 if (quiet >= STABLE_MS || elapsed >= HARD_TIMEOUT) { 20036 clearInterval(poll); 20037 mo.disconnect(); 20038 doScroll(); 20039 } 20040 }, 200); 20041 20042 window.addEventListener('load', function () { 20043 setTimeout(function () { 20044 if (!scrolled) { 20045 clearInterval(poll); 20046 mo.disconnect(); 20047 doScroll(); 20048 } 20049 }, 800); 20050 }); 20051 })(); 20052 </script>
20052 20053 </div> 20054 20055 20056 20057 20058</section><section id="shopify-section-template--29563577761958__bp_youll_also_love" class="shopify-section"><section 20059 class="bp-yall js-recommendation-container" 20060 data-section-type="product-recommendations" 20061 data-section-id="template--29563577761958__bp_youll_also_love" 20062 data-recommendation-url="/recommendations/products" 20063 data-product-id="15286962618534" 20064 data-limit="12" 20065 data-bp-amplience="https://media.bostonproper.com" data-bp-yall-use-br="true" 20066></section>
20066<script src="//www.bostonproper.com/cdn/shop/t/1521/assets/bp-card-hydrate.js?v=169242559010489351881790713714" defer></script>
20066<script> 20067 (function () { 20068 const BP_YALL_BR_WIDGET = '09z4d58l'; 20069 const BP_YALL_BR_HOST = 'https://pathways.dxpapi.com/api/v2/widgets/item/'; 20070 // `sku`/`slug` were requested but never consumed by the render â dropped 20071 // to trim payload (BR audit: minimize fl on non-PDP calls). 20072 const BP_YALL_BR_FIELDS = 'pid,title,url,brand,price,sale_price,thumb_image'; 20073 // Shopping-bag icon for the BR cards' quick-add trigger (matches 20074 // snippets/bp-product-card.liquid). fill=currentColor so it inherits 20075 // the .bp-card__quick color + hover state. 20076 const BAG_SVG = '<svg xmlns="http://www.w3.org/2000/svg" width="20" height="20" viewBox="0 0 20 20" fill="currentColor" aria-hidden="true"><path d="M4.18929 5.74434H16.5343H16.6292L16.6729 5.74549C16.9007 5.75099 17.1328 5.87803 17.2448 6.08089C17.3145 6.20157 17.3382 6.30372 17.3547 6.43048C17.6354 9.18806 17.9008 11.996 18.3051 14.7356C18.3771 15.2467 18.4466 15.758 18.5137 16.2697C18.5163 16.2917 18.5195 16.336 18.5207 16.3582C18.5378 16.6193 18.4156 16.9263 18.1508 17.0687C17.9651 17.1752 17.7807 17.1763 17.5845 17.1755C16.1978 17.1755 4.69428 17.1755 3.34023 17.1755C3.20422 17.1726 3.01496 17.1862 2.86505 17.1451C2.45036 17.0406 2.25213 16.656 2.31666 16.2445C2.41071 15.5271 2.58579 14.2489 2.68621 13.5185C2.95476 11.5399 3.15849 9.46674 3.35962 7.47459C3.37582 7.3571 3.44296 6.63739 3.45714 6.52887L3.46698 6.43424L3.46814 6.42237C3.46814 6.41948 3.46901 6.4134 3.47045 6.40559C3.5156 6.02417 3.79254 5.74028 4.18929 5.74434ZM4.18929 6.6125C4.2136 6.6125 4.24804 6.59601 4.27872 6.57343C4.30302 6.55491 4.3201 6.53089 4.32704 6.51845C4.33457 6.50601 4.33486 6.50138 4.33399 6.50427C4.33341 6.50803 4.33225 6.51353 4.33138 6.51758C4.33138 6.51758 4.33081 6.51932 4.33052 6.52424L4.32936 6.5361L4.31923 6.63044C4.132 8.4151 3.9054 10.3181 3.74624 12.1016C3.63685 13.4481 3.56103 15.0256 3.46727 16.3753L3.46582 16.3915V16.3961C3.46582 16.3961 3.46611 16.3973 3.4664 16.3915C3.4664 16.3872 3.4664 16.375 3.46351 16.3588C3.45396 16.2908 3.41721 16.2161 3.3602 16.1507C3.29306 16.0798 3.20422 16.0246 3.12319 16.0162C3.11856 16.0162 3.13361 16.0173 3.15647 16.0176C4.67199 16.0182 16.0117 16.0176 17.5848 16.0176H17.6719C17.6823 16.017 17.6916 16.0168 17.6962 16.0165C17.7014 16.0162 17.7034 16.0159 17.6991 16.0162C17.5752 16.0286 17.4268 16.1591 17.3857 16.2795C17.3576 16.3556 17.3617 16.3788 17.362 16.3941C17.362 16.3921 17.362 16.393 17.362 16.3886L17.3605 16.3724C17.254 14.8543 17.1872 13.3292 17.0509 11.8148C16.8906 10.0895 16.6752 8.23163 16.4883 6.50456C16.495 6.52019 16.5161 6.55433 16.5468 6.57575C16.5714 6.59485 16.5989 6.60585 16.6145 6.60961L16.6243 6.61192H16.6284C16.6284 6.61192 16.6298 6.61279 16.6304 6.61279C16.6318 6.61279 16.6336 6.61279 16.633 6.61279C16.6324 6.61279 16.635 6.61279 16.6292 6.61279C16.5783 6.61221 16.4 6.61337 16.3445 6.61279H4.18929V6.6125Z"/><path d="M6.08301 8.78064C6.1788 7.5189 6.20658 6.21405 6.58539 4.98907C7.15432 3.11007 8.4421 2.11631 10.414 2.12673C13.8001 2.13338 14.5031 4.87997 14.6721 7.71395C14.6964 8.07453 14.7158 8.43453 14.745 8.78035L13.5874 8.77485C13.6172 8.46057 13.6575 8.02649 13.6754 7.71858C13.801 5.83351 13.5976 3.34129 11.3988 2.81605C10.7575 2.6676 10.0702 2.6676 9.42892 2.81605C7.61503 3.24811 7.15143 5.0374 7.12683 6.6872C7.11121 7.36929 7.17545 8.09132 7.24056 8.77485L6.08301 8.78035V8.78064Z"/></svg>'; 20077 20078 const SELECTORS = { 20079 container: '[data-section-id="template--29563577761958__bp_youll_also_love"]', 20080 arrowNext: '.js-recommendations-next', 20081 arrowPrev: '.js-recommendations-prev', 20082 rail: '.js-recommendations', 20083 }; 20084 20085 const root = document.querySelector(SELECTORS.container); 20086 if (!root) return; 20087 // Note: `bpInitialized` is marked inside start() (the actual load), NOT 20088 // here â start() is deferred behind IntersectionObserver, so marking it 20089 // up-front would let a re-run bail while the recs never rendered. start() 20090 // is idempotent, so a duplicate script tag / double IO can't double-load. 20091 if (root.dataset.bpInitialized === 'true') return; 20092 20093 const limit = root.dataset.limit; 20094 const productId = root.dataset.productId; 20095 const sectionId = root.dataset.sectionId; 20096 const recsUrl = root.dataset.recommendationUrl; 20097 const useBr = root.dataset.bpYallUseBr === 'true'; 20098 20099 async function loadShopify() {
20100 try { 20101 const url = `${recsUrl}?section_id=${sectionId}&product_id=${productId}&limit=${limit}`; 20102 const res = await fetch(url, { credentials: 'same-origin' }); 20103 if (!res.ok) throw new Error('recs HTTP ' + res.status); 20104 const text = await res.text(); 20105 const wrap = document.createElement('div'); 20106 wrap.innerHTML = text; 20107 const fresh = wrap.querySelector('.js-recommendation-container'); 20108 if (fresh && fresh.innerHTML.trim().length) { 20109 root.innerHTML = fresh.innerHTML; 20110 wireArrows(); 20111 bindBags(root); 20112 } else { 20113 root.style.display = 'none'; 20114 } 20115 } catch (err) { 20116 console.warn('[bp-yall] section fetch failed', err); 20117 root.style.display = 'none'; 20118 } 20119 } 20120 20121 async function loadBloomreach() { 20122 try { 20123 const br = (window.shop && window.shop.br) || {}; 20124 const accountId = br.accountId || ''; 20125 const domainKey = br.domainKey || 'bostonproper'; 20126 // Empty (not a fabricated uid=0:v=app:ts=0:hc=1 sentinel) when the BR 20127 // cookie is absent â BR flags that sentinel as invalid. The empty 20128 // param is stripped below so anonymous calls simply omit _br_uid_2. 20129 const uidMatch = document.cookie.match(/(?:^|;\s*)_br_uid_2=([^;]+)/); 20130 const uid = uidMatch ? decodeURIComponent(uidMatch[1]) : ''; 20131 20132 const params = new URLSearchParams({ 20133 account_id: accountId, 20134 domain_key: domainKey, 20135 item_ids: productId, 20136 rows: String(limit), 20137 fields: BP_YALL_BR_FIELDS, 20138 _br_uid_2: uid, 20139 url: window.location.href, 20140 ref_url: document.referrer || window.location.href, 20141 }); 20142 if (!params.get('_br_uid_2')) params.delete('_br_uid_2'); 20143 20144 const fetchUrl = `${BP_YALL_BR_HOST}${BP_YALL_BR_WIDGET}?${params}`; 20145 const res = await fetch(fetchUrl, { credentials: 'omit' }); 20146 if (!res.ok) throw new Error('BR HTTP ' + res.status); 20147 const data = await res.json(); 20148 const docs = (data.response && data.response.docs) || []; 20149 20150 if (!docs.length) { root.style.display = 'none'; return; } 20151 20152 const MONEY = new Intl.NumberFormat('en-US', { style: 'currency', currency: 'USD' }); 20153 // Whole-dollar prices drop the .00 â matches snippets/bp-product-card 20154 // (`money | replace: '.00', ''`) and assets/bp-card-hydrate money(), so 20155 // the initial BR-card price and the swatch-change repaint agree 20156 // (Ernie QA 7/22: $XX.XX on load flipped to $XX on swatch change). 20157 const money = (n) => MONEY.format(Number(n)).replace(/\.00$/, ''); 20158 const esc = (s) => String(s || '').replace(/[&<>"']/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c])); 20159 20160 // Normalize BR's absolute URL to a same-origin `/products/<handle>` 20161 // path â BR staging emits `staging.example.com` as the host on 20162 // every doc, prod emits `www.bostonproper.com` which can be wrong 20163 // depending on the visitor's current subdomain. Same-origin 20164 // relative paths always resolve to the shop the user is on. 20165 const hrefFromDoc = (doc) => { 20166 const m = String(doc.url || '').match(/\/products\/([^/?#]+)/); 20167 return m ? `/products/${m[1]}` : (doc.url || '#'); 20168 }; 20169 const itemsHtml = docs.map((doc) => { 20170 const onSale = doc.sale_price != null && doc.price != null && Number(doc.sale_price) < Number(doc.price) - 0.001; 20171 const price = money(onSale ? doc.sale_price : doc.price); 20172 const compareVal = onSale ? money(doc.price) : ''; 20173 const href = hrefFromDoc(doc); 20174 const compare = compareVal ? `<s class="bp-card__compare">${esc(compareVal)}</s>` : ''; 20175 // Canonical BP product card (matches assets/bp-bloomreach.js 20176 // renderCard + snippets/bp-product-card.liquid) so BR cards get 20177 // the same quick-add bag: hover on desktop, ALWAYS shown on 20178 // mobile (data-bp-card-qv-vis-m="shown"). The bag markup is 20179 // hydrated on click by bindBags() below via BpCardHydrate. 20180 return ` 20181 <li class="bp-yall__item"> 20182 <product-card class="bp-card-host"> 20183 <article class="bp-card" 20184 data-product-id="${esc(doc.pid)}" 20185 data-product-url="${esc(href)}" 20186 data-bp-card-qb-style="button" 20187 data-bp-card-qv-dest="drawer" 20188 data-bp-card-qv-vis-d="hover" 20189 data-bp-card-qv-vis-m="shown" 20190 data-bp-source="bloomreach"> 20191 <div class="bp-card__media"> 20192 <a class="bp-card__media-link" href="${esc(href)}" aria-label="${esc(doc.title)}" ref="productCardLink" data-bp-card-product-link> 20193 <img class="bp-card__image bp-card__image--primary" src="${esc(doc.thumb_image)}" alt="${esc(doc.title)}" loading="lazy" data-bp-card-image-primary> 20194 </a> 20195 <div class="bp-card__badges" data-bp-card-badges></div> 20196 <button type="button" class="bp-card__quick" aria-label="Quick add ${esc(doc.title)}" data-product-id="${esc(doc.pid)}" data-bp-card-qv-trigger>${BAG_SVG}</button> 20197 </div> 20198 <div class="bp-card__body"> 20199 <h3 class="bp-card__title"><a href="${esc(href)}">${esc(doc.title)}</a></h3> 20200 <div class="bp-card__price-row">
20201 <span class="bp-card__price">${esc(price)}</span>${compare} 20202 </div> 20203 </div> 20204 </article> 20205 </product-card> 20206 </li>`; 20207 }).join(''); 20208 20209 root.innerHTML = ` 20210 <div class="bp-yall__head"> 20211 <h2 class="bp-yall__heading">You'll Also Love</h2> 20212 <div class="bp-yall__nav" role="group" aria-label="Carousel navigation"> 20213 <button type="button" class="bp-yall__arrow js-recommendations-prev" aria-label="Previous recommendations"> 20214 <svg xmlns="http://www.w3.org/2000/svg" width="44" height="44" viewBox="0 0 44 44" fill="none" aria-hidden="true"><path d="M25.8674 13.8674C25.9116 13.8244 25.9558 13.7814 26 13.7385C26.0872 13.8256 26.1744 13.9128 26.2615 14C26.2186 14.0442 26.1756 14.0884 26.1326 14.1326C25.8696 14.4029 25.6066 14.6733 25.3436 14.9436C22.9768 17.3768 20.6099 19.8099 18.2431 22.2431V21.7569C20.6762 24.1238 23.1094 26.4906 25.5425 28.8575C25.8129 29.1205 26.0832 29.3835 26.3536 29.6464C26.473 29.7627 26.5925 29.8789 26.712 29.9951C26.473 30.2341 26.2341 30.473 25.9951 30.712C25.8789 30.5925 25.7627 30.473 25.6464 30.3536C25.3835 30.0832 25.1205 29.8129 24.8575 29.5425C22.4906 27.1094 20.1238 24.6762 17.7569 22.2431L17.514 21.9933L17.7569 21.7569C20.1901 19.3901 22.6232 17.0232 25.0564 14.6564C25.3267 14.3934 25.5971 14.1304 25.8674 13.8674Z" fill="#171609"/><path d="M25.6464 13.6464C25.7627 13.527 25.8789 13.4075 25.9951 13.288C26.2341 13.527 26.473 13.7659 26.712 14.0049C26.5925 14.1211 26.473 14.2373 26.3536 14.3536C26.0832 14.6165 25.8129 14.8795 25.5425 15.1425C23.1094 17.5094 20.6762 19.8762 18.2431 22.2431V21.7569C20.6099 24.1901 22.9768 26.6232 25.3436 29.0564C25.6066 29.3267 25.8696 29.5971 26.1326 29.8674C26.1756 29.9116 26.2186 29.9558 26.2615 30C26.1744 30.0872 26.0872 30.1744 26 30.2615C25.9558 30.2186 25.9116 30.1756 25.8674 30.1326C25.5971 29.8696 25.3267 29.6066 25.0564 29.3436C22.6232 26.9768 20.1901 24.6099 17.7569 22.2431L17.514 21.9933L17.7569 21.7569C20.1238 19.3238 22.4906 16.8906 24.8575 14.4575C25.1205 14.1871 25.3835 13.9168 25.6464 13.6464Z" fill="#171609"/></svg> 20215 </button> 20216 <button type="button" class="bp-yall__arrow js-recommendations-next" aria-label="Next recommendations"> 20217 <svg xmlns="http://www.w3.org/2000/svg" width="44" height="44" viewBox="0 0 44 44" fill="none" aria-hidden="true"><path d="M18.1326 13.8674C18.0884 13.8244 18.0442 13.7814 18 13.7385C17.9128 13.8256 17.8256 13.9128 17.7385 14C17.7814 14.0442 17.8244 14.0884 17.8674 14.1326C18.1304 14.4029 18.3934 14.6733 18.6564 14.9436C21.0232 17.3768 23.3901 19.8099 25.7569 22.2431V21.7569C23.3238 24.1238 20.8906 26.4906 18.4575 28.8575C18.1871 29.1205 17.9168 29.3835 17.6464 29.6464C17.527 29.7627 17.4075 29.8789 17.288 29.9951C17.527 30.2341 17.7659 30.473 18.0049 30.712C18.1211 30.5925 18.2373 30.473 18.3536 30.3536C18.6165 30.0832 18.8795 29.8129 19.1425 29.5425C21.5094 27.1094 23.8762 24.6762 26.2431 22.2431L26.486 21.9933L26.2431 21.7569C23.8099 19.3901 21.3768 17.0232 18.9436 14.6564C18.6733 14.3934 18.4029 14.1304 18.1326 13.8674Z" fill="#171609"/><path d="M18.3536 13.6464C18.2373 13.527 18.1211 13.4075 18.0049 13.288C17.7659 13.527 17.527 13.7659 17.288 14.0049C17.4075 14.1211 17.527 14.2373 17.6464 14.3536C17.9168 14.6165 18.1871 14.8795 18.4575 15.1425C20.8906 17.5094 23.3238 19.8762 25.7569 22.2431V21.7569C23.3901 24.1901 21.0232 26.6232 18.6564 29.0564C18.3934 29.3267 18.1304 29.5971 17.8674 29.8674C17.8244 29.9116 17.7814 29.9558 17.7385 30C17.8256 30.0872 17.9128 30.1744 18 30.2615C18.0442 30.2186 18.0884 30.1756 18.1326 30.1326C18.4029 29.8696 18.6733 29.6066 18.9436 29.3436C21.3768 26.9768 23.8099 24.6099 26.2431 22.2431L26.486 21.9933L26.2431 21.7569C23.8762 19.3238 21.5094 16.8906 19.1425 14.4575C18.8795 14.1871 18.6165 13.9168 18.3536 13.6464Z" fill="#171609"/></svg> 20218 </button> 20219 </div> 20220 </div> 20221 <div class="bp-yall__carousel"> 20222 <ul class="bp-yall__rail js-recommendations" role="list">${itemsHtml}</ul> 20223 </div>`; 20224 20225 wireArrows(); 20226 handOffToHydrate(root); 20227 bindBags(root); 20228 } catch (err) { 20229 console.warn('[bp-yall] BR fetch failed', err); 20230 root.style.display = 'none'; 20231 } 20232 } 20233 20234 // BR's item widget doesn't return sibling-color data, so swatches 20235 // are hydrated the same way the PLP does it: assets/bp-card-hydrate.js 20236 // reads each card's data-product-url and fetches 20237 // /products/<handle>?view=hydrate for the sibling list. That script 20238 // loads deferred (see the conditional script tag above), so poll 20239 // briefly in case this fetch resolves first. 20240 function handOffToHydrate(scope) { 20241 const tick = (left) => { 20242 if (window.BpCardHydrate && typeof window.BpCardHydrate.observeCards === 'function') { 20243 window.BpCardHydrate.observeCards(scope); 20244 return; 20245 } 20246 if (left <= 0) return; 20247 setTimeout(() => tick(left - 100), 100); 20248 }; 20249 tick(3000); 20250 } 20251 20252 function wireArrows() { 20253 const rail = root.querySelector(SELECTORS.rail); 20254 const prev = root.querySelector(SELECTORS.arrowPrev); 20255 const next = root.querySelector(SELECTORS.arrowNext); 20256 if (!rail) return; 20257 20258 const update = () => { 20259 const max = rail.scrollWidth - rail.clientWidth; 20260 if (prev) prev.toggleAttribute('disabled', rail.scrollLeft <= 1); 20261 if (next) next.toggleAttribute('disabled', rail.scrollLeft >= max - 1); 20262 }; 20263 const step = () => rail.clientWidth * 0.9; 20264 if (prev) prev.addEventListener('click', () => rail.scrollBy({ left: -step(), behavior: 'smooth' })); 20265 if (next) next.addEventListener('click', () => rail.scrollBy({ left: step(), behavior: 'smooth' })); 20266 rail.addEventListener('scroll', update, { passive: true }); 20267 update(); 20268 } 20269 20270 // Wire each card's quick-add bag to the BP product drawer (mirrors 20271 // bp-collection.bindBagIcons). Liquid / section-fetch cards 20272 // (bp-product-card) carry the quick-add markup inline, so the trigger 20273 // is found immediately; BR cards are hydrated on demand via 20274 // BpCardHydrate, then opened. Idempotent per bag. 20275 function bindBags(scope) { 20276 (scope || root).querySelectorAll('.bp-card__quick').forEach((bag) => { 20277 if (bag.dataset.bpBound) return; 20278 bag.dataset.bpBound = '1'; 20279 bag.addEventListener('click', (e) => { 20280 e.preventDefault(); 20281 e.stopPropagation(); 20282 const card = bag.closest('.bp-card'); 20283 if (!card) return; 20284 const open = () => { 20285 const trigger = card.querySelector('.quick-add__button--choose'); 20286 if (!trigger) return false;
20287 if (typeof window.bpOpenProductDrawer === 'function') window.bpOpenProductDrawer(trigger); 20288 else trigger.click(); 20289 return true; 20290 }; 20291 if (open()) return; 20292 const handle = (card.dataset.productUrl || '').match(/\/products\/([^/?#]+)/); 20293 if (!handle || card.dataset.bpQaLoading === '1' || !window.BpCardHydrate) return; 20294 card.dataset.bpQaLoading = '1'; 20295 window.BpCardHydrate.hydrateHandle(handle[1]) 20296 .then((data) => { window.BpCardHydrate.applyHydration(card, data); }) 20297 .then(() => { card.dataset.bpQaLoading = '0'; open(); }) 20298 .catch(() => { card.dataset.bpQaLoading = '0'; }); 20299 }); 20300 }); 20301 } 20302 20303 function start() { 20304 // Idempotent: mark only on real load, and bail if already loaded so a 20305 // duplicate IO/script can't double-fetch. 20306 if (root.dataset.bpInitialized === 'true') return; 20307 root.dataset.bpInitialized = 'true'; 20308 if (useBr) { 20309 loadBloomreach(); 20310 } else if (root.querySelector(SELECTORS.rail)) { 20311 wireArrows(); 20312 bindBags(root); 20313 } else { 20314 loadShopify(); 20315 } 20316 } 20317 20318 // Below-the-fold on the PDP â defer the recommendations fetch/render 20319 // until the section is near the viewport so it doesn't compete with 20320 // the LCP at page load. The section can be display:none while empty 20321 // (the :empty rule on the non-BR path) and IntersectionObserver can't 20322 // observe a box-less element, so anchor a 1px sentinel at the section's 20323 // DOM position (always in layout) and observe that. Once, ~600px lead. 20324 if ('IntersectionObserver' in window && root.parentNode) { 20325 const sentinel = document.createElement('div'); 20326 sentinel.setAttribute('aria-hidden', 'true'); 20327 sentinel.style.cssText = 'width:1px;height:1px;'; 20328 root.parentNode.insertBefore(sentinel, root); 20329 const io = new IntersectionObserver((entries, obs) => { 20330 if (entries.some((e) => e.isIntersecting)) { obs.disconnect(); sentinel.remove(); start(); } 20331 }, { rootMargin: '600px 0px' }); 20332 io.observe(sentinel); 20333 } else { 20334 start(); 20335 } 20336 })(); 20337</script>
20337 20338 20339 20340 20341 20342</section> 20343 </main> 20344 20345 <footer> 20346 <!-- BEGIN sections: footer-group --> 20347<div id="shopify-section-sections--29563569504422__bp_footer_main" class="shopify-section shopify-section-group-footer-group section-wrapper"><bp-footer 20348 class="bp-footer" 20349 data-section-id="sections--29563569504422__bp_footer_main" 20350 style=" 20351 --bp-footer-heading-color: #171609; 20352 --bp-footer-body-color: #595750; 20353 --bp-footer-link-color: #595750; 20354 --bp-footer-social-border: #e1e0d4; 20355 "><div class="bp-footer__top"> 20356 20357 <!-- Newsletter + Social: mobile copy (visible < 1024px only) --> 20358 <div class="bp-footer__newsletter bp-footer__newsletter--mobile"><p class="bp-footer__newsletter-heading">Join the list</p><p class="bp-footer__newsletter-copy">Sign up for email and SMS and get 20% OFF your order.</p><button 20359 type="button" 20360 class="bp-footer__signup-btn klaviyo_form_trigger_footer" 20361 data-bp-klaviyo-form-id="X8v8qM" 20362 >Sign Up</button><div class="bp-footer__legal"><p>By signing up via email or text, you agree to our <a href="/pages/terms-of-use" title="Terms of Use">Terms</a> & <a href="/pages/privacy-policy" title="Privacy Policy">Privacy</a>.</p></div><div class="bp-footer__social" role="list" aria-label="Social media links"><span role="listitem"> 20363 <a 20364 href="https://www.instagram.com/bostonproperofficial/" 20365 class="bp-footer__social-icon" 20366 target="_blank" 20367 rel="noopener noreferrer" 20368 aria-label="Instagram" 20369 ><svg class="bp-footer__social-svg" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"> 20370 20371 20372<path 20373 fill-rule="evenodd" 20374 d="M13.23 3.492c-.84-.037-1.096-.046-3.23-.046-2.144 0-2.39.01-3.238.055-.776.027-1.195.164-1.487.273a2.43 2.43 0 0 0-.912.593 2.486 2.486 0 0 0-.602.922c-.11.282-.238.702-.274 1.486-.046.84-.046 1.095-.046 3.23 0 2.134.01 2.39.046 3.229.004.51.097 1.016.274 1.495.145.365.319.639.602.913.282.282.538.456.92.602.474.176.974.268 1.479.273.848.046 1.103.046 3.238.046 2.134 0 2.39-.01 3.23-.046.784-.036 1.203-.164 1.486-.273.374-.146.648-.329.921-.602.283-.283.447-.548.602-.922.177-.476.27-.979.274-1.486.037-.84.046-1.095.046-3.23 0-2.134-.01-2.39-.055-3.229-.027-.784-.164-1.204-.274-1.495a2.43 2.43 0 0 0-.593-.913 2.604 2.604 0 0 0-.92-.602c-.284-.11-.703-.237-1.488-.273ZM6.697 2.05c.857-.036 1.131-.045 3.302-.045 1.1-.014 2.202.001 3.302.045.664.014 1.321.14 1.943.374a3.968 3.968 0 0 1 1.414.922c.41.397.728.88.93 1.414.23.622.354 1.279.365 1.942C18 7.56 18 7.824 18 10.005c0 2.17-.01 2.444-.046 3.292-.036.858-.173 1.442-.374 1.943-.2.53-.474.976-.92 1.423a3.896 3.896 0 0 1-1.415.922c-.51.191-1.095.337-1.943.374-.857.036-1.122.045-3.302.045-2.171 0-2.445-.009-3.302-.055-.849-.027-1.432-.164-1.943-.364a4.152 4.152 0 0 1-1.414-.922 4.128 4.128 0 0 1-.93-1.423c-.183-.51-.329-1.085-.365-1.943C2.009 12.45 2 12.167 2 10.004c0-2.161 0-2.435.055-3.302.027-.848.164-1.432.365-1.942a4.44 4.44 0 0 1 .92-1.414 4.18 4.18 0 0 1 1.415-.93c.51-.183 1.094-.33 1.943-.366Zm.427 4.806a4.105 4.105 0 1 1 5.805 5.805 4.105 4.105 0 0 1-5.805-5.805Zm1.882 5.371a2.668 2.668 0 1 0 2.042-4.93 2.668 2.668 0 0 0-2.042 4.93Zm5.922-5.942a.958.958 0 1 1-1.355-1.355.958.958 0 0 1 1.355 1.355Z" 20375 clip-rule="evenodd" 20376 /></svg></a> 20377 </span><span role="listitem"> 20378 <a 20379 href="https://www.tiktok.com/@bostonproperofficial" 20380 class="bp-footer__social-icon" 20381 target="_blank" 20382 rel="noopener noreferrer" 20383 aria-label="Tiktok" 20384 ><svg class="bp-footer__social-svg" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"> 20385 20386 20387<path d="M10.511 1.705h2.74s-.157 3.51 3.795 3.768v2.711s-2.114.129-3.796-1.158l.028 5.606A5.073 5.073 0 1 1 8.213 7.56h.708v2.785a2.298 2.298 0 1 0 1.618 2.205L10.51 1.705Z" /></svg></a> 20388 </span><span role="listitem"> 20389 <a 20390 href="https://www.youtube.com/@bostonproper" 20391 class="bp-footer__social-icon" 20392 target="_blank" 20393 rel="noopener noreferrer" 20394 aria-label="Youtube" 20395 ><svg class="bp-footer__social-svg bp-footer__social-svg--youtube" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="13 13 14 14"> 20396 <path d="M26.3926 16.7286C26.2389 16.1458 25.7886 15.6865 25.2175 15.5295C24.1741 15.2383 20.0005 15.2383 20.0005 15.2383C20.0005 15.2383 15.827 15.2383 14.7837 15.5185C14.2235 15.6752 13.7622 16.1459 13.6086 16.7286C13.334 17.7929 13.334 20.0002 13.334 20.0002C13.334 20.0002 13.334 22.2187 13.6086 23.2718C13.7624 23.8545 14.2126 24.3139 14.7838 24.4708C15.8381 24.7621 20.0007 24.7621 20.0007 24.7621C20.0007 24.7621 24.1741 24.7621 25.2175 24.4819C25.7887 24.325 26.2389 23.8657 26.3927 23.283C26.6672 22.2187 26.6672 20.0114 26.6672 20.0114C26.6672 20.0114 26.6783 17.7929 26.3926 16.7286ZM18.6717 22.0394V17.9609L22.1423 20.0002L18.6717 22.0394Z" fill="currentColor"/> 20397 </svg></a> 20398 </span><span role="listitem"> 20399 <a 20400 href="https://www.facebook.com/bostonproper" 20401 class="bp-footer__social-icon" 20402 target="_blank" 20403 rel="noopener noreferrer" 20404 aria-label="Facebook" 20405 ><svg class="bp-footer__social-svg bp-footer__social-svg--facebook" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 6 12"> 20406 <path d="M1.26991 2.53971V3.80952H0V5.71429H1.26991V11.4286H3.80952V5.71429H5.49848L5.71429 3.80952H3.80952V2.69848C3.80952 2.18419 3.86029 1.91114 4.65391 1.91114H5.71429V0H4.01276C1.98095 9.52381e-05 1.26991 0.952381 1.26991 2.53971Z" fill="currentColor"/> 20407 </svg></a> 20408 </span><span role="listitem"> 20409 <a 20410 href="https://www.pinterest.com/bostonproper/" 20411 class="bp-footer__social-icon" 20412 target="_blank" 20413 rel="noopener noreferrer" 20414 aria-label="Pinterest" 20415 ><svg class="bp-footer__social-svg" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"> 20416 20417 20418<path d="M10 2.01c2.124.01 4.16.855 5.666 2.353a8.087 8.087 0 0 1 1.277 9.68A7.952 7.952 0 0 1 10 18.04a8.164 8.164 0 0 1-2.276-.307c.403-.653.672-1.24.816-1.729l.567-2.2c.134.27.393.5.768.702.384.192.768.297 1.19.297.836 0 1.585-.24 2.248-.72a4.678 4.678 0 0 0 1.537-1.969c.37-.89.554-1.848.537-2.8
2041813 0-1.249-.48-2.315-1.43-3.227a5.061 5.061 0 0 0-3.65-1.374c-.893 0-1.729.154-2.478.461a5.023 5.023 0 0 0-3.236 4.552c0 .72.134 1.355.413 1.902.269.538.672.922 1.22 1.152.096.039.182.039.25 0 .066-.028.114-.096.143-.192l.173-.653c.048-.144.02-.288-.105-.432a2.257 2.257 0 0 1-.548-1.565 3.803 3.803 0 0 1 3.976-3.861c1.047 0 1.863.288 2.44.855.585.576.883 1.315.883 2.228 0 .768-.106 1.479-.317 2.122a3.813 3.813 0 0 1-.893 1.556c-.384.384-.836.576-1.345.576-.413 0-.749-.144-1.018-.451-.259-.307-.345-.672-.25-1.085.147-.514.298-1.026.452-1.537l.173-.701c.057-.25.086-.451.086-.624 0-.346-.096-.634-.269-.855-.192-.22-.451-.336-.797-.336-.432 0-.797.192-1.085.595-.288.394-.442.893-.442 1.499.005.374.063.746.173 1.104l.058.144c-.576 2.478-.913 3.938-1.037 4.36-.116.528-.154 1.153-.125 1.863A8.067 8.067 0 0 1 2 10.03c0-2.208.778-4.11 2.343-5.666A7.721 7.721 0 0 1 10 2.001v.01Z" /></svg></a> 20419 </span></div></div> 20420 20421 <!-- Nav columns: up to 3 top-level items from the selected menu --> 20422 <nav class="bp-footer__nav" aria-label="Footer navigation"><div class="bp-footer__col"> 20423 <p class="bp-footer__col-heading"><a href="/collections/all" class="bp-footer__col-heading-link">Shop</a></p><ul class="bp-footer__links" role="list"><li> 20424 <a href="/pages/catalog-request" class="bp-footer__link">Request a Catalog</a> 20425 </li><li> 20426 <a href="/pages/coupons-promotions" class="bp-footer__link">Coupons & Promotions</a> 20427 </li><li> 20428 <a href="/pages/digital-catalog" class="bp-footer__link">Online Catalog</a> 20429 </li><li> 20430 <a href="https://www.bostonproper.com/products/gift-card" class="bp-footer__link">Purchase a Gift Card</a> 20431 </li><li> 20432 <a href="/pages/check-gift-card-balance" class="bp-footer__link">Check Gift Card Balance</a> 20433 </li><li> 20434 <a href="/blogs/styleedit" class="bp-footer__link">styleEDIT Blog</a> 20435 </li><li> 20436 <a href="/pages/refer-a-friend" class="bp-footer__link">Refer a Friend</a> 20437 </li></ul></div><div class="bp-footer__col"> 20438 <p class="bp-footer__col-heading"><a href="/pages/about-us" class="bp-footer__col-heading-link">Learn</a></p><ul class="bp-footer__links" role="list"><li> 20439 <a href="/pages/about-us" class="bp-footer__link">About Us</a> 20440 </li><li> 20441 <a href="/pages/careers" class="bp-footer__link">Careers</a> 20442 </li><li> 20443 <a href="https://www.ascendpartner.com/affiliate/registration" class="bp-footer__link">Affiliate Program</a> 20444 </li><li> 20445 <a href="https://www.bostonproper.com/pages/accessibility-statement" class="bp-footer__link">Accessibility Statement</a> 20446 </li><li> 20447 <a href="/pages/use-of-ai" class="bp-footer__link">Use of AI</a> 20448 </li></ul></div><div class="bp-footer__col"> 20449 <p class="bp-footer__col-heading"><a href="/pages/customer-service" class="bp-footer__col-heading-link">Connect</a></p><ul class="bp-footer__links" role="list"><li> 20450 <a href="/pages/customer-service" class="bp-footer__link">Customer Care</a> 20451 </li><li> 20452 <a href="/pages/order-lookup" class="bp-footer__link">Order Status</a> 20453 </li><li> 20454 <a href="https://www.bostonproper.com/pages/shipping-returns" class="bp-footer__link">Shipping</a> 20455 </li><li> 20456 <a href="https://www.bostonproper.com/pages/size-chart" class="bp-footer__link">Size Chart</a> 20457 </li><li> 20458 <a href="/pages/proper-insider-rewards" class="bp-footer__link">Proper Insider Rewards</a> 20459 </li><li> 20460 <a href="/pages/marketplace-faqs" class="bp-footer__link">Brands We Love Marketplace</a> 20461 </li><li> 20462 <a href="https://www.bostonproper.com/pages/returns-exchanges" class="bp-footer__link">Returns & Exchanges</a> 20463 </li><li>
20464 <a href="/pages/charity" class="bp-footer__link">Style Meets Purpose</a> 20465 </li><li> 20466 <a href="https://www.facebook.com/groups/4044543892348249/" class="bp-footer__link">Join Our Signature Circle</a> 20467 </li></ul></div></nav> 20468 20469 <!-- Newsletter + Social: desktop copy (visible >= 1024px only) --> 20470 <div class="bp-footer__newsletter bp-footer__newsletter--desktop"><p class="bp-footer__newsletter-heading">Join the list</p><p class="bp-footer__newsletter-copy">Sign up for email and SMS and get 20% OFF your order.</p><button 20471 type="button" 20472 class="bp-footer__signup-btn klaviyo_form_trigger_footer" 20473 data-bp-klaviyo-form-id="X8v8qM" 20474 >Sign Up</button><div class="bp-footer__legal"><p>By signing up via email or text, you agree to our <a href="/pages/terms-of-use" title="Terms of Use">Terms</a> & <a href="/pages/privacy-policy" title="Privacy Policy">Privacy</a>.</p></div><div class="bp-footer__social" role="list" aria-label="Social media links"><span role="listitem"> 20475 <a 20476 href="https://www.instagram.com/bostonproperofficial/" 20477 class="bp-footer__social-icon" 20478 target="_blank" 20479 rel="noopener noreferrer" 20480 aria-label="Instagram" 20481 ><svg class="bp-footer__social-svg" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"> 20482 20483 20484<path 20485 fill-rule="evenodd" 20486 d="M13.23 3.492c-.84-.037-1.096-.046-3.23-.046-2.144 0-2.39.01-3.238.055-.776.027-1.195.164-1.487.273a2.43 2.43 0 0 0-.912.593 2.486 2.486 0 0 0-.602.922c-.11.282-.238.702-.274 1.486-.046.84-.046 1.095-.046 3.23 0 2.134.01 2.39.046 3.229.004.51.097 1.016.274 1.495.145.365.319.639.602.913.282.282.538.456.92.602.474.176.974.268 1.479.273.848.046 1.103.046 3.238.046 2.134 0 2.39-.01 3.23-.046.784-.036 1.203-.164 1.486-.273.374-.146.648-.329.921-.602.283-.283.447-.548.602-.922.177-.476.27-.979.274-1.486.037-.84.046-1.095.046-3.23 0-2.134-.01-2.39-.055-3.229-.027-.784-.164-1.204-.274-1.495a2.43 2.43 0 0 0-.593-.913 2.604 2.604 0 0 0-.92-.602c-.284-.11-.703-.237-1.488-.273ZM6.697 2.05c.857-.036 1.131-.045 3.302-.045 1.1-.014 2.202.001 3.302.045.664.014 1.321.14 1.943.374a3.968 3.968 0 0 1 1.414.922c.41.397.728.88.93 1.414.23.622.354 1.279.365 1.942C18 7.56 18 7.824 18 10.005c0 2.17-.01 2.444-.046 3.292-.036.858-.173 1.442-.374 1.943-.2.53-.474.976-.92 1.423a3.896 3.896 0 0 1-1.415.922c-.51.191-1.095.337-1.943.374-.857.036-1.122.045-3.302.045-2.171 0-2.445-.009-3.302-.055-.849-.027-1.432-.164-1.943-.364a4.152 4.152 0 0 1-1.414-.922 4.128 4.128 0 0 1-.93-1.423c-.183-.51-.329-1.085-.365-1.943C2.009 12.45 2 12.167 2 10.004c0-2.161 0-2.435.055-3.302.027-.848.164-1.432.365-1.942a4.44 4.44 0 0 1 .92-1.414 4.18 4.18 0 0 1 1.415-.93c.51-.183 1.094-.33 1.943-.366Zm.427 4.806a4.105 4.105 0 1 1 5.805 5.805 4.105 4.105 0 0 1-5.805-5.805Zm1.882 5.371a2.668 2.668 0 1 0 2.042-4.93 2.668 2.668 0 0 0-2.042 4.93Zm5.922-5.942a.958.958 0 1 1-1.355-1.355.958.958 0 0 1 1.355 1.355Z" 20487 clip-rule="evenodd" 20488 /></svg></a> 20489 </span><span role="listitem"> 20490 <a 20491 href="https://www.tiktok.com/@bostonproperofficial" 20492 class="bp-footer__social-icon" 20493 target="_blank" 20494 rel="noopener noreferrer" 20495 aria-label="Tiktok" 20496 ><svg class="bp-footer__social-svg" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"> 20497 20498 20499<path d="M10.511 1.705h2.74s-.157 3.51 3.795 3.768v2.711s-2.114.129-3.796-1.158l.028 5.606A5.073 5.073 0 1 1 8.213 7.56h.708v2.785a2.298 2.298 0 1 0 1.618 2.205L10.51 1.705Z" /></svg></a> 20500 </span><span role="listitem"> 20501 <a 20502 href="https://www.youtube.com/@bostonproper" 20503 class="bp-footer__social-icon" 20504 target="_blank" 20505 rel="noopener noreferrer" 20506 aria-label="Youtube" 20507 ><svg class="bp-footer__social-svg bp-footer__social-svg--youtube" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="13 13 14 14"> 20508 <path d="M26.3926 16.7286C26.2389 16.1458 25.7886 15.6865 25.2175 15.5295C24.1741 15.2383 20.0005 15.2383 20.0005 15.2383C20.0005 15.2383 15.827 15.2383 14.7837 15.5185C14.2235 15.6752 13.7622 16.1459 13.6086 16.7286C13.334 17.7929 13.334 20.0002 13.334 20.0002C13.334 20.0002 13.334 22.2187 13.6086 23.2718C13.7624 23.8545 14.2126 24.3139 14.7838 24.4708C15.8381 24.7621 20.0007 24.7621 20.0007 24.7621C20.0007 24.7621 24.1741 24.7621 25.2175 24.4819C25.7887 24.325 26.2389 23.8657 26.3927 23.283C26.6672 22.2187 26.6672 20.0114 26.6672 20.0114C26.6672 20.0114 26.6783 17.7929 26.3926 16.7286ZM18.6717 22.0394V17.9609L22.1423 20.0002L18.6717 22.0394Z" fill="currentColor"/> 20509 </svg></a> 20510 </span><span role="listitem"> 20511 <a 20512 href="https://www.facebook.com/bostonproper" 20513 class="bp-footer__social-icon" 20514 target="_blank" 20515 rel="noopener noreferrer" 20516 aria-label="Facebook" 20517 ><svg class="bp-footer__social-svg bp-footer__social-svg--facebook" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 6 12"> 20518 <path d="M1.26991 2.53971V3.80952H0V5.71429H1.26991V11.4286H3.80952V5.71429H5.49848L5.71429 3.80952H3.80952V2.69848C3.80952 2.18419 3.86029 1.91114 4.65391 1.91114H5.71429V0H4.01276C1.98095 9.52381e-05 1.26991 0.952381 1.26991 2.53971Z" fill="currentColor"/> 20519 </svg></a> 20520 </span><span role="listitem"> 20521 <a 20522 href="https://www.pinterest.com/bostonproper/" 20523 class="bp-footer__social-icon" 20524 target="_blank" 20525 rel="noopener noreferrer" 20526 aria-label="Pinterest" 20527 ><svg class="bp-footer__social-svg" aria-hidden="true" focusable="false" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"> 20528 20529 20530<path d="M10 2.01c2.124.01 4.16.855 5.666 2.353a8.087 8.087 0 0 1 1.277 9.68A7.952 7.952 0 0 1 10 18.04a8.164 8.164 0 0 1-2.276-.307c.403-.653.672-1.24.816-1.729l.567-2.2c.134.27.393.5.768.702.384.192.768.297 1.19.297.836 0 1.585-.24 2.248-.72a4.678 4.678 0 0 0 1.537-1.969c.37-.89.554-1.848.537-2.8
2053013 0-1.249-.48-2.315-1.43-3.227a5.061 5.061 0 0 0-3.65-1.374c-.893 0-1.729.154-2.478.461a5.023 5.023 0 0 0-3.236 4.552c0 .72.134 1.355.413 1.902.269.538.672.922 1.22 1.152.096.039.182.039.25 0 .066-.028.114-.096.143-.192l.173-.653c.048-.144.02-.288-.105-.432a2.257 2.257 0 0 1-.548-1.565 3.803 3.803 0 0 1 3.976-3.861c1.047 0 1.863.288 2.44.855.585.576.883 1.315.883 2.228 0 .768-.106 1.479-.317 2.122a3.813 3.813 0 0 1-.893 1.556c-.384.384-.836.576-1.345.576-.413 0-.749-.144-1.018-.451-.259-.307-.345-.672-.25-1.085.147-.514.298-1.026.452-1.537l.173-.701c.057-.25.086-.451.086-.624 0-.346-.096-.634-.269-.855-.192-.22-.451-.336-.797-.336-.432 0-.797.192-1.085.595-.288.394-.442.893-.442 1.499.005.374.063.746.173 1.104l.058.144c-.576 2.478-.913 3.938-1.037 4.36-.116.528-.154 1.153-.125 1.863A8.067 8.067 0 0 1 2 10.03c0-2.208.778-4.11 2.343-5.666A7.721 7.721 0 0 1 10 2.001v.01Z" /></svg></a> 20531 </span></div></div> 20532 20533 </div><!-- /.bp-footer__top --> 20534 20535 <!-- Full-bleed brand wordmark --> 20536 <div class="bp-footer__wordmark" role="img" aria-label="Boston Proper"><svg xmlns="http://www.w3.org/2000/svg" width="3125.373" height="477.863" viewBox="0 0 3125.373 477.863" xmlns:v="https://vecta.io/nano"><defs><clipPath id="A"><path d="M0 358.397h2344.032V0H0z" transform="translate(-1192.84 -358.397)"/></clipPath><clipPath id="B"><path d="M0 358.397h2344.032V0H0z" transform="translate(-1231.018 -147.311)"/></clipPath><clipPath id="C"><path d="M0 358.397h2344.032V0H0z" transform="translate(-324.5 -185.766)"/></clipPath><clipPath id="D"><path d="M0 358.397h2344.032V0H0z" transform="translate(-374.634 -235.749)"/></clipPath><clipPath id="E"><path d="M0 358.397h2344.032V0H0z" transform="translate(-560.78 -113.875)"/></clipPath><clipPath id="F"><path d="M0 358.397h2344.032V0H0z" transform="translate(-179.077 -113.875)"/></clipPath><clipPath id="G"><path d="M0 358.397h2344.032V0H0z" transform="translate(-38.235 -116.862)"/></clipPath><clipPath id="H"><path d="M0 358.397h2344.032V0H0z" transform="translate(-755.738 -140.308)"/></clipPath><clipPath id="I"><path d="M0 358.397h2344.032V0H0z" transform="translate(-1904.567 -113.876)"/></clipPath><clipPath id="J"><path d="M0 358.397h2344.032V0H0z" transform="translate(-2146.066 -116.663)"/></clipPath><clipPath id="K"><path d="M0 358.397h2344.032V0H0z" transform="translate(-2042.293 -247.698)"/></clipPath><clipPath id="L"><path d="M0 358.397h2344.032V0H0z" transform="translate(-1809.258 -211.852)"/></clipPath><clipPath id="M"><path d="M0 358.397h2344.032V0H0z" transform="translate(-2286.082 -175.808)"/></clipPath><clipPath id="N"><path d="M0 358.397h2344.032V0H0z" transform="translate(-1642.062 -247.698)"/></clipPath><clipPath id="O"><path d="M0 358.397h2344.032V0H0z" transform="translate(-1192.84 -358.397)"/></clipPath><clipPath id="P"><path d="M0 358.397h2344.032V0H0z" transform="translate(-1231.018 -147.311)"/></clipPath><clipPath id="Q"><path d="M0 358.397h2344.032V0H0z" transform="translate(-324.5 -185.766)"/></clipPath><clipPath id="R"><path d="M0 358.397h2344.032V0H0z" transform="translate(-374.634 -235.749)"/></clipPath><clipPath id="S"><path d="M0 358.397h2344.032V0H0z" transform="translate(-560.78 -113.875)"/></clipPath><clipPath id="T"><path d="M0 358.397h2344.032V0H0z" transform="translate(-179.077 -113.875)"/></clipPath><clipPath id="U"><path d="M0 358.397h2344.032V0H0z" transform="translate(-38.235 -116.862)"/></clipPath><clipPath id="V"><path d="M0 358.397h2344.032V0H0z" transform="translate(-755.738 -140.308)"/></clipPath><clipPath id="W"><path d="M0 358.397h2344.032V0H0z" transform="translate(-1904.567 -113.876)"/></clipPath><clipPath id="X"><path d="M0 358.397h2344.032V0H0z" transform="translate(-2146.066 -116.663)"/></clipPath><clipPath id="Y"><path d="M0 358.397h2344.032V0H0z" transform="translate(-2042.293 -247.698)"/></clipPath><clipPath id="Z"><path d="M0 358.397h2344.032V0H0z" transform="translate(-1809.258 -211.852)"/></clipPath><clipPath id="a"><path d="M0 358.397h2344.032V0H0z" transform="translate(-2286.082 -175.808)"/></clipPath><clipPath id="b"><path d="M0 358.397h2344.032V0H0z" transform="translate(-1642.062 -247.698)"/></clipPath><path id="c" d="M0 0c.641-.454 1.721-.352 3.069-.428C71.046-4.284 131.83-41.973 168.985-98.563c22.383-34.091 35.214-73.313 37.788-113.93.252-3.977-.131-11.232.692-12.352v-5.956c.077.206-.436-.595-.441-.838l-.229-11.502c-2.614-41.097-15.515-80.404-38.474-115.192l-34.367-.022c14.563 17.472 25.313 36.778 32.868 57.999 7.514 21.208 11.391 43.038 12.422 65.592 1.197 26.189-2.028 51.989-10.102 76.887-16.078 49.577-50.287 91.772-96.564 115.686-54.842 28.34-120.314 26.363-173.426-5.074-54.594-32.313-89.695-90.034-96.401-153.074-3.221-30.282-.402-60.904 8.097-90.08 7.52-24.947 19.282-47.731 36.128-67.923l-34.335-.015c-22.438 34-35.353 72.424-38.319 112.649l-.209 6.75c-.004.13-.511.873-.439.712v20.847c0 2.305.513 5.111.721 7.898 2.961 39.581 15.764 77.783 37.623 111.031C-150.969-42.17-90.567-4.577-22.925-.477c1.561.095 2.604.025 3.568.477z"/><path id="d" d="M0 0c-36.873 3.668-58.007-28.965-60.558-61.979l-6.592-85.296-30.568-.003 6.677 86.898c3.06 35.896 21.866 54.274 51.958 70.374 6.455 3.454 12.433 6.982 17.89 11.876 8.033 7.205 13.487 16.237 15.915 26.831 3.542 15.455 2.452 33.471-5.328 47.51-10.605 19.137-31.194 25.342-51.859 21.56-14.199-2.599-25.74-11.636-31.911-24.655-3.51-7.101-5.214-14.44-6.
20536045-22.492l-16.759-217.906-30.565.025 16.755 217.822c1.543 15.86 8.716 30.125 20.124 41.023 24.807 23.589 64.163 26.807 94.634 13.338 11.696-5.169 21.633-12.709 29.425-22.747C34.946 74.154 31.573 33.961.134 13.982c36.017-3.624 64.039-23.923 72.893-59.584 4.16-18.066 2.991-37.092-3.463-54.549-13.087-35.395-48.719-47.873-83.897-47.129l1.081 13.706c29.353-.102 49.053 18.644 55.519 46.349 3.201 13.712 3.65 27.668 1.338 41.537C39.625-21.806 25.349-2.522 0 0"/><path id="e" d="M0 0l-13.741 5.576c-16.926 6.572-24.295 14.736-24.295 25.688 0 14.737 12.148 25.292 29.871 25.292S17.78 49.8 24.494 40.026v19.119c-9.16 3.784-20.71 5.576-29.671 5.576-29.871 0-48.591-15.135-48.591-39.231 0-16.927 10.754-29.273 31.265-37.438l14.936-5.776c15.732-6.173 23.499-14.139 23.499-26.684 0-16.13-13.144-27.482-31.465-27.482-17.723 0-27.787 8.157-39.629 22.304v-20.312c9.36-6.372 24.295-10.356 38.036-10.356 28.676 0 48.392 17.126 48.392 43.015C31.266-19.915 22.105-8.762 0 0"/><path id="f" d="M0 0c11.749 2.39 24.694 3.386 43.413 3.386v-83.839c0-21.706-.399-33.853-1.195-46.996h21.507c-.996 13.341-1.195 25.29-1.195 46.996V3.386c18.719 0 31.464-.995 43.214-3.386v11.949H0z"/><path id="g" d="M0 0c-30.269 0-50.98 25.889-50.98 64.124S-30.269 128.445 0 128.445c30.07 0 50.781-26.087 50.781-64.321S30.07 0 0 0m0 136.611c-41.023 0-71.292-30.668-71.292-72.487S-41.023-8.363 0-8.363s71.093 30.668 71.093 72.487S41.023 136.611 0 136.611"/><path id="h" d="M0 0c-30.269 0-50.98 25.889-50.98 64.124S-30.269 128.446 0 128.446c30.071 0 50.781-26.088 50.781-64.322S30.071 0 0 0m0 136.611c-41.023 0-71.292-30.668-71.292-72.487S-41.023-8.363 0-8.363s71.093 30.668 71.093 72.487S41.023 136.611 0 136.611"/><path id="i" d="M0 0h-18.321v122.671H0c16.927 0 27.481-9.359 27.481-24.892C27.481 81.25 15.732 69.7-.2 69.7l-13.797-.001 6.913-8.563h8.279c19.117 0 31.265-10.952 31.265-29.871C32.46 11.949 20.511 0 0 0zm14.935 65.916C35.048 69.7 46.599 82.445 46.599 98.973c0 20.114-17.326 31.863-45.803 31.863h-39.031c.796-12.347 1.194-24.295 1.194-47.196V38.434c0-22.901-.398-34.848-1.194-46.997H.598c31.862 0 51.776 15.533 51.776 39.63 0 19.914-12.745 32.459-37.439 34.849"/><path id="j" d="M0 0l-87.712 107.39h-24.254l15.334-18.774V-32.008h12.347V73.499L1.889-32.008h10.457V107.39H0z"/><path id="k" d="M0 0c-30.269 0-50.98 25.888-50.98 64.123S-30.269 128.445 0 128.445c30.07 0 50.781-26.087 50.781-64.321S30.07 0 0 0m0 136.61c-41.023 0-71.292-30.668-71.292-72.487S-41.023-8.364 0-8.364s71.093 30.668 71.093 72.488S41.023 136.61 0 136.61"/><path id="l" d="M0 0v61.335h13.343c15.732 0 26.086-.399 36.641-1.594v11.55c-9.957-1.194-20.711-1.792-36.641-1.792H0v53.371h8.364c20.511 0 33.854-1.196 45.404-3.585v11.75h-74.081c.797-13.343 1.195-25.49 1.195-47.197V38.634c0-21.508-.398-33.854-1.195-46.997h75.276V3.585C43.412 1.195 29.672 0 9.36 0z"/><path id="m" d="M0 0h-35.447c.796-12.347 1.194-24.295 1.194-47.196v-45.205c0-21.906-.398-33.854-1.194-46.997h21.507c-.996 13.541-1.195 25.091-1.195 46.997v84.236h13.94c20.114 0 33.058-11.55 33.058-30.07C31.863-57.75 19.118-69.7-1.394-69.7l-9.255.001 6.43-7.965h3.621c31.066 0 51.778 15.732 51.778 39.628S31.663 0 0 0"/><path id="n" d="M0 0c0 22.702-19.316 35.846-49.387 35.846h-38.434c.797-13.144 1.195-25.49 1.195-46.997v-45.405c0-21.706-.398-33.853-1.195-46.997h21.308c-.996 13.343-1.195 25.291-1.195 46.997v84.237h17.525c18.32 0 30.667-10.953 30.667-27.681 0-17.922-12.545-29.473-30.667-29.473h-14.594l59.798-74.079-.001.001.001-.002h23.698l-57.95 67.509C-15.931-34.053 0-19.913 0 0"/><path id="o" d="M0 0c23.3 1.991 39.231 16.131 39.231 36.044 0 22.702-19.317 35.846-49.387 35.846h-38.435c.797-13.144 1.195-25.491 1.195-46.997v-45.405c0-21.706-.397-33.852-1.194-46.996h21.308c-.996 13.342-1.195 25.29-1.195 46.996v84.237h17.525c18.32 0 30.667-10.953 30.667-27.681 0-17.922-12.546-29.473-30.667-29.473h-14.594l59.798-74.078h-.001l.001-.001H57.95z"/><path id="p" d="M0 0h-35.447c.796-12.347 1.194-24.295 1.194-47.196v-45.205c0-21.906-.398-33.853-1.194-46.997h21.507c-.996 13.541-1.195 25.091-1.195 46.997v84.236h13.94c20.113 0 33.057-11.55 33.057-30.07C31.862-57.75 19.118-69.7-1.394-69.7l-9.255.001 6.43-7.966h3.621c31.066 0 51.777 15.732 51.777 39.629S31.663 0 0 0"/></defs><g fill="currentColor"><use href="#c" transform="matrix(1.333333 0 0 -1.333333 1590.454 -.000267)" clip-path="url(#A)"/><use href="#d" transform="matrix(1.333333 0 0 -1.333333 1641.3571 281.44787)" clip-path="url(#B)"/><use href="#e" transform="matrix(1.333333 0 0 -1.333333 432.66653 230.17507)" clip-path="url(#C)"/><use href="#f" transform="matrix(1.333333 0 0 -1.333333 499.51187 163.53053)" clip-path="url(#D)"/><use href="#g" transform="matrix(1.333333 0 0 -1.333333 747.70707 326.02893)" clip-path="url(#E)"/><use href="#h" transform="matrix(1.333333 0 0 -1.333333 238.77 326.02893)" clip-path="url(#F)"/><use href="#i" transform="matrix(1.333333 0 0 -1.333333 50.9804 322.04613)" clip-path="url(#G)"/><use href="#j" transform="matrix(1.333333 0 0 -1.333333 1007.6507 290.7852)" clip-path="url(#H)"/><use href="#k" transform="matrix(1.333333 0 0 -1.333333 2539.4224 326.02827)" clip-path="url(#I)"/><use href="#l" transform="matrix(1.333333 0 0 -1.333333 2861.4209 322.31187)" clip-path="url(#J)"/><use href="#m" transform="matrix(1.333333 0 0 -1.333333 2723.0571 147.59867)" clip-path="url(#K)"/><use href="#n" transform="matrix(1.333333 0 0 -1.333333 2412.3441 195.39293)" clip-path="url(#L)"/><use href="#o" transform="matrix(1.333333 0 0 -1.333333 3048.1087 243.45173)" clip-path="url(#M)"/><use href="#p" transform="matrix(1.333333 0 0 -1.333333 2189.416 147.59867)" clip-path="url(#N)"/><use href="#c" transform="matrix(1.333333 0 0 -1.333333 1590.454 -.000267)" clip-path="url(#O)"/><use href="#d" transform="matrix(1.333333 0 0 -1.333333 1641.3571 281.44787)" clip-path="url(#P)"/><use href="#e" transform="matrix(1.333333 0 0 -1.333333 432.66653 230.17507)" clip-path="url(#Q)"/><use href="#f" transform="matrix(1.333333 0 0 -1.333333 499.51187 163.53053)" clip-path="url(#R)"/><use href="#g" transform="matrix(1.333333 0 0 -1.333333 747.70707 326.02893)" clip-path="url(#S)"/><use href="#h" transform="matrix(1.333333 0 0 -1.333333 238.77 326.02893)" clip-path="url(#T)"/><use href="#i" transform="matrix(1.333333 0 0 -1.333333 50.9804 322.04613)" clip-path="url(#U)"/><use href="#j" transform="matrix(1.333333 0 0 -1.333333 1007.6507 290.7852)" clip-path="url(#V
20536)"/><use href="#k" transform="matrix(1.333333 0 0 -1.333333 2539.4224 326.02827)" clip-path="url(#W)"/><use href="#l" transform="matrix(1.333333 0 0 -1.333333 2861.4209 322.31187)" clip-path="url(#X)"/><use href="#m" transform="matrix(1.333333 0 0 -1.333333 2723.0571 147.59867)" clip-path="url(#Y)"/><use href="#n" transform="matrix(1.333333 0 0 -1.333333 2412.3441 195.39293)" clip-path="url(#Z)"/><use href="#o" transform="matrix(1.333333 0 0 -1.333333 3048.1087 243.45173)" clip-path="url(#a)"/><use href="#p" transform="matrix(1.333333 0 0 -1.333333 2189.416 147.59867)" clip-path="url(#b)"/></g></svg></div> 20537 20538 <!-- Policy bar: copyright left, links right --> 20539 <div class="bp-footer__policy-bar"><span class="bp-footer__copyright">© 2026 Boston Proper.</span><div class="bp-footer__policy-links"><a href="/pages/check-gift-card-balance" class="bp-footer__policy-link">Check Gift Card Balance</a><a href="/pages/privacy-policy" class="bp-footer__policy-link">Privacy Policy</a> 20540 <a href="/pages/shipping-returns" class="bp-footer__policy-link">Shipping Policy</a> 20541 <a href="/pages/terms-of-use" class="bp-footer__policy-link">Terms of Service</a><a href="/pages/site-map" class="bp-footer__policy-link">Site Map</a></div> 20542 </div> 20543 20544 20545</bp-footer> 20546 20547 20548 20549 20550 20551 20552</div> 20553<!-- END sections: footer-group --> 20554 </footer> 20555 20556 20557
20558<script 20559 src="//www.bostonproper.com/cdn/shop/t/1521/assets/dialog.js?v=150888536568694194021790713714" 20560 type="module" 20561></script>
20561 20562
20563<script 20564 src="//www.bostonproper.com/cdn/shop/t/1521/assets/predictive-search.js?v=174099676085626643091790713714" 20565 type="module" 20566 fetchpriority="low" 20567></script>
20567 20568 20569<dialog-component 20570 id="search-modal" 20571 class="search-modal" 20572 20573> 20574 <dialog 20575 ref="dialog" 20576 on:click="/closeDialogOnClickOutside" 20577 on:keydown="/closeDialogOnEscapePress" 20578 class="search-modal__content dialog-modal" 20579 scroll-lock 20580 aria-labelledby="search-modal-heading" 20581 > 20582 <h2 20583 id="search-modal-heading" 20584 class="visually-hidden" 20585 > 20586 Search 20587 </h2> 20588 <predictive-search-component 20589 class="predictive-search color-scheme-1" 20590 style="--product-corner-radius: 0px; --card-corner-radius: 4px;" 20591 data-section-id="predictive-search" 20592 data-testid="search-component--modal" 20593 role="search" 20594 aria-label="Search" 20595 > 20596 <form 20597 action="/search" 20598 method="get" 20599 role="search" 20600 class="predictive-search-form" 20601 ref="form" 20602 on:keydown="/onSearchKeyDown" 20603 > 20604 <div 20605 class="predictive-search-form__header" 20606 > 20607 <div class="predictive-search-form__header-inner"> 20608 <label 20609 for="cmdk-input" 20610 class="visually-hidden" 20611 >Search</label> 20612 <input 20613 class="search-input" 20614 id="cmdk-input" 20615 type="search" 20616 name="q" 20617 role="combobox" 20618 aria-expanded="false" 20619 aria-owns="predictive-search-results" 20620 aria-controls="predictive-search-results" 20621 aria-haspopup="listbox" 20622 aria-autocomplete="list" 20623 autocomplete="off" 20624 placeholder="Search" 20625 ref="searchInput" 20626 on:input="/search" 20627 on:keydown="/onSearchKeyDown" 20628 > 20629 <input 20630 name="options[prefix]" 20631 type="hidden" 20632 value="last" 20633 > 20634 <span class="svg-wrapper predictive-search__icon"> 20635 <svg width="44" height="44" viewBox="0 0 44 44" fill="none" xmlns="http://www.w3.org/2000/svg"> 20636<path d="M30.6967 19.7055C30.7183 23.8405 27.932 27.3926 24.0949 28.7508C17.6703 31.1232 10.0267 26.8687 10 19.7055C10.0272 12.5414 17.6703 8.28779 24.0953 10.6602C27.932 12.0184 30.7187 15.5704 30.6967 19.7055ZM28.5408 19.7055C28.5477 16.9739 27.3344 14.2088 25.1509 12.5164C22.4003 10.326 18.2959 10.3256 15.5454 12.5164C13.3619 14.2088 12.1486 16.9735 12.1555 19.7055C12.1486 22.437 13.3619 25.2022 15.5454 26.8946C18.2959 29.085 22.4003 29.0854 25.1509 26.8946C27.3344 25.2022 28.5477 22.4374 28.5408 19.7055Z" fill="currentColor"/> 20637<path d="M33.1718 33.9998C31.5945 32.7679 30.2255 31.2984 28.9126 29.7966C28.4814 29.2934 28.0606 28.7816 27.6699 28.2439L28.9794 27.1216C29.4511 27.5898 29.8927 28.084 30.3238 28.5872C31.3971 29.854 32.4207 31.1661 33.3132 32.5696C33.4917 32.8528 33.6659 33.1396 33.8263 33.4388L33.1714 34.0002L33.1718 33.9998Z" fill="currentColor"/> 20638</svg> 20639 20640 </span> 20641 <button 20642 type="button" 20643 class="button-unstyled predictive-search__reset-button" 20644 ref="resetButton" 20645 hidden 20646 on:click="/resetSearch" 20647 > 20648 Clear 20649 </button> 20650 </div> 20651 <button 20652 type="button" 20653 class="button predictive-search__close-modal-button" 20654 aria-label="Close dialog" 20655 on:click="dialog-component/closeDialog" 20656 ref="closeModalButton" 20657 > 20658 <span class="svg-wrapper"> 20659 <svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 20660 <path d="M12 2L2 12" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 20661 <path d="M12 12L2 2" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 20662</svg> 20663 20664 </span> 20665 </button> 20666 </div> 20667 20668 <div class="predictive-search-form__content-wrapper"> 20669 <div 20670 class="predictive-search-form__content" 20671 ref="predictiveSearchResults" 20672 on:click="/handleModalClick" 20673 > 20674 20675<div 20676 id="predictive-search-results" 20677 class="predictive-search-dropdown" 20678 role="listbox"
20679 aria-expanded="true" 20680 style="--color-shadow: rgb(var(--color-foreground-rgb) / 0.1);" 20681> 20682 20683 <div class="predictive-search-results__inner"> 20684 20685 20686 20687 20688 20689 <div 20690 id="predictive-search-products" 20691 class="predictive-search-results__products" 20692 20693 data-testid="search-component-default--modal" 20694 20695> 20696 20697 <h4 class="predictive-search-results__title"> 20698 Products 20699 </h4> 20700 <ul 20701 class="predictive-search-results__list predictive-search-results__wrapper-products list-unstyled" 20702 > 20703 20704 20705 <li 20706 class="predictive-search-results__card predictive-search-results__card--product" 20707 ref="resultsItems[]" 20708 > 20709 <product-card class="bp-card-host"> 20710 <article 20711 class="bp-card" 20712 data-product-id="15399681392806" 20713 data-product-url="/products/leopard-print-skirt-brown-multi" 20714 data-bp-card-secondary-hover="true" 20715 data-bp-card-qv-dest="drawer" 20716 data-bp-card-qv-vis-d="hover" 20717 data-bp-card-qv-vis-m="shown" 20718 data-bp-card-qb-style="button" 20719 data-bp-track-product='{ "id": 15399681392806, "title": "Leopard Print Skirt Brown Multi", "vendor": "Bagatelle", "handle": "leopard-print-skirt-brown-multi", "url": "\/products\/leopard-print-skirt-brown-multi", "image": "\/\/www.bostonproper.com\/cdn\/shop\/files\/2DO3390_SET.jpg?v=1791009176\u0026width=720", "category": "Skirts", "product_type":null, "selected_variant": { "id": 56779671077030, "sku": "2DO3390800", "price": 110.0, "compare_at_price": 110.0, "option1": "Brown Multi", "option2": "L", "product_id": 15399681392806, "available": true, "inventory_quantity": 51 } }' 20720 > 20721 <div class="bp-card__media"> 20722 <a class="bp-card__media-link" href="/products/leopard-print-skirt-brown-multi" aria-label="Leopard Print Skirt Brown Multi" ref="productCardLink"><img 20723 class="bp-card__image bp-card__image--primary" 20724 src="//www.bostonproper.com/cdn/shop/files/2DO3390_SET.jpg?v=1791009176&width=720" 20725 srcset="//www.bostonproper.com/cdn/shop/files/2DO3390_SET.jpg?v=1791009176&width=360 360w, //www.bostonproper.com/cdn/shop/files/2DO3390_SET.jpg?v=1791009176&width=540 540w, //www.bostonproper.com/cdn/shop/files/2DO3390_SET.jpg?v=1791009176&width=720 720w, //www.bostonproper.com/cdn/shop/files/2DO3390_SET.jpg?v=1791009176&width=1080 1080w" 20726 sizes="(min-width: 1024px) 25vw, 50vw" 20727 width="1639" 20728 height="2048" 20729 alt="Leopard Print Skirt Brown Multi" 20730 loading="lazy" 20731 data-bp-card-image-primary 20732 ><img 20733 class="bp-card__image bp-card__image--secondary" 20734 src="//www.bostonproper.com/cdn/shop/files/2_c2593330-685e-4f0d-affc-d3a27bbd70d9.jpg?v=1791009175&width=720" 20735 srcset="//www.bostonproper.com/cdn/shop/files/2_c2593330-685e-4f0d-affc-d3a27bbd70d9.jpg?v=1791009175&width=360 360w, //www.bostonproper.com/cdn/shop/files/2_c2593330-685e-4f0d-affc-d3a27bbd70d9.jpg?v=1791009175&width=540 540w, //www.bostonproper.com/cdn/shop/files/2_c2593330-685e-4f0d-affc-d3a27bbd70d9.jpg?v=1791009175&width=720 720w" 20736 sizes="(min-width: 1024px) 25vw, 50vw" 20737 width="1639" 20738 height="2048" 20739 alt="" 20740 loading="lazy" 20741 aria-hidden="true" 20742 data-bp-card-image-secondary 20743 ></a><div class="bp-card__badges" data-bp-card-badges></div><button 20744 type="button" 20745 class="bp-card__quick" 20746 aria-label="Quick add Leopard Print Skirt Brown Multi" 20747 data-product-id="15399681392806" 20748 data-bp-card-qv-trigger 20749 > 20750 <svg viewBox="0 0 20 20" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> 20751 <path d="M4.18929 5.74434H16.5343H16.6292L16.6729 5.74549C16.9007 5.75099 17.1328 5.87803 17.2448 6.08089C17.3145 6.20157 17.3382 6.30372 17.3547 6.43048C17.6354 9.18806 17.9008 11.996 18.3051 14.7356C18.3771 15.2467 18.4466 15.758 18.5137 16.2697C18.5163 16.2917 18.5195 16.336 18.5207 16.3582C18.5378 16.6193 18.4156 16.9263 18.1508 17.0687C17.9651 17.1752 17.7807 17.1763 17.5845 17.1755C16.1978 17.1755 4.69428 17.1755 3.34023 17.1755C3.20422 17.1726 3.01496 17.1862 2.86505 17.1451C2.45036 17.0406 2.25213 16.656 2.31666 16.2445C2.41071 15.5271 2.58579 14.2489 2.68621 13.5185C2.95476 11.5399 3.15849 9.46674 3.35962 7.47459C3.37582 7.3571 3.44296 6.63739 3.45714 6.52887L3.46698 6.43424L3.46814 6.42237C3.46814 6.41948 3.46901 6.4134 3.47045 6.40559C3.5156 6.02417 3.79254 5.74
20751028 4.18929 5.74434ZM4.18929 6.6125C4.2136 6.6125 4.24804 6.59601 4.27872 6.57343C4.30302 6.55491 4.3201 6.53089 4.32704 6.51845C4.33457 6.50601 4.33486 6.50138 4.33399 6.50427C4.33341 6.50803 4.33225 6.51353 4.33138 6.51758C4.33138 6.51758 4.33081 6.51932 4.33052 6.52424L4.32936 6.5361L4.31923 6.63044C4.132 8.4151 3.9054 10.3181 3.74624 12.1016C3.63685 13.4481 3.56103 15.0256 3.46727 16.3753L3.46582 16.3915V16.3961C3.46582 16.3961 3.46611 16.3973 3.4664 16.3915C3.4664 16.3872 3.4664 16.375 3.46351 16.3588C3.45396 16.2908 3.41721 16.2161 3.3602 16.1507C3.29306 16.0798 3.20422 16.0246 3.12319 16.0162C3.11856 16.0162 3.13361 16.0173 3.15647 16.0176C4.67199 16.0182 16.0117 16.0176 17.5848 16.0176H17.6719C17.6823 16.017 17.6916 16.0168 17.6962 16.0165C17.7014 16.0162 17.7034 16.0159 17.6991 16.0162C17.5752 16.0286 17.4268 16.1591 17.3857 16.2795C17.3576 16.3556 17.3617 16.3788 17.362 16.3941C17.362 16.3921 17.362 16.393 17.362 16.3886L17.3605 16.3724C17.254 14.8543 17.1872 13.3292 17.0509 11.8148C16.8906 10.0895 16.6752 8.23163 16.4883 6.50456C16.495 6.52019 16.5161 6.55433 16.5468 6.57575C16.5714 6.59485 16.5989 6.60585 16.6145 6.60961L16.6243 6.61192H16.6284C16.6284 6.61192 16.6298 6.61279 16.6304 6.61279C16.6318 6.61279 16.6336 6.61279 16.633 6.61279C16.6324 6.61279 16.635 6.61279 16.6292 6.61279C16.5783 6.61221 16.4 6.61337 16.3445 6.61279H4.18929V6.6125Z"/> 20752 <path d="M6.08301 8.78064C6.1788 7.5189 6.20658 6.21405 6.58539 4.98907C7.15432 3.11007 8.4421 2.11631 10.414 2.12673C13.8001 2.13338 14.5031 4.87997 14.6721 7.71395C14.6964 8.07453 14.7158 8.43453 14.745 8.78035L13.5874 8.77485C13.6172 8.46057 13.6575 8.02649 13.6754 7.71858C13.801 5.83351 13.5976 3.34129 11.3988 2.81605C10.7575 2.6676 10.0702 2.6676 9.42892 2.81605C7.61503 3.24811 7.15143 5.0374 7.12683 6.6872C7.11121 7.36929 7.17545 8.09132 7.24056 8.77485L6.08301 8.78035V8.78064Z"/> 20753 </svg> 20754 </button><div class="bp-card__quick-add"> 20755 20756<quick-add-component 20757 class="quick-add color-scheme-1 " 20758 ref="quickAdd" 20759 data-product-title="Leopard Print Skirt Brown Multi" 20760 data-quick-add-button="choose" 20761 data-product-options-count="2" 20762> 20763 <product-form-component 20764 data-section-id="bp-card" 20765 data-product-id="15399681392806" 20766 on:submit="/handleSubmit" 20767 class="quick-add__product-form-component" 20768 > 20769 <div 20770 class="visually-hidden" 20771 aria-live="assertive" 20772 role="status" 20773 aria-atomic="true" 20774 ref="liveRegion" 20775 ></div><form method="post" action="/cart/add" id="QuickAdd-ProductForm-15399681392806-" accept-charset="UTF-8" class="shopify-product-form" enctype="multipart/form-data" novalidate="novalidate" data-type="add-to-cart-form"><input type="hidden" name="form_type" value="product" /><input type="hidden" name="utf8" value="â" /><input 20776 type="hidden" 20777 name="id" 20778 ref="variantId" 20779 value="56779671077030" 20780 20781 > 20782 <input 20783 type="hidden" 20784 name="quantity" 20785 value="1" 20786 > 20787 20788 <add-to-cart-component 20789 ref="addToCartButtonContainer" 20790 data-product-variant-media="//www.bostonproper.com/cdn/shop/files/2DO3390_SET.jpg?v=1791009176&width=100" 20791 data-add-to-cart-animation="true" 20792> 20793 <button 20794 20795 type="submit" 20796 name="add" 20797 ref="addToCartButton" 20798 on:click="/handleClick" 20799 class="button button quick-add__button quick-add__button--add add-to-cart-button" 20800 20801 20802 > 20803 <span 20804 class="add-to-cart-text" 20805 > 20806 20807 <span 20808 aria-hidden="true" 20809 class="svg-wrapper add-to-cart-icon" 20810 ><svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="none"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="var(--icon-stroke-width)" d="M16.608 9.421V6.906H3.392v8.016c0 .567.224 1.112.624 1.513.4.402.941.627 1.506.627H8.63M8.818 3h2.333c.618 0 1.212.247 1.649.686a2.35 2.35 0 0 1 .683 1.658v1.562H6.486V5.344c0-.622.246-1.218.683-1.658A2.33 2.33 0 0 1 8.82 3"/><path stroke="currentColor" stroke-linecap="round" stroke-width="var(--icon-stroke-width)" d="M14.608 12.563v5m2.5-2.5h-5"/></svg> 20811</span> 20812
20813 <span class="add-to-cart-text__content is-visually-hidden-mobile"> 20814 <span> 20815 <span>Add</span> 20816 </span> 20817 </span> 20818 </span> 20819 20820 <span class="add-to-cart__added"> 20821 <span class="svg-wrapper add-to-cart__added-icon"><svg aria-hidden="true" class="checkmark-burst" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> 20822 <g class="check"> 20823 <circle class="ring" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> 20824 <path class="tick" d="M9 12.75L11.25 15L15 9.75" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> 20825 </g> 20826 <g class="burst"> 20827 <g style="--index: 0;"> 20828 <line class="line" stroke-linecap="round" pathLength="1" x1="12" y1="8.5" x2="12" y2="15.5" stroke="currentColor"/> 20829 </g> 20830 <g style="--index: 1;"> 20831 <line class="line" stroke-linecap="round" pathLength="1" x1="12" y1="8.5" x2="12" y2="15.5" stroke="currentColor"/> 20832 </g> 20833 <g style="--index: 2;"> 20834 <line class="line" stroke-linecap="round" pathLength="1" x1="12" y1="8.5" x2="12" y2="15.5" stroke="currentColor"/> 20835 </g> 20836 <g style="--index: 3;"> 20837 <line class="line" stroke-linecap="round" pathLength="1" x1="12" y1="8.5" x2="12" y2="15.5" stroke="currentColor"/> 20838 </g> 20839 <g style="--index: 4;"> 20840 <line class="line" stroke-linecap="round" pathLength="1" x1="12" y1="8.5" x2="12" y2="15.5" stroke="currentColor"/> 20841 </g> 20842 <g style="--index: 5;"> 20843 <line class="line" stroke-linecap="round" pathLength="1" x1="12" y1="8.5" x2="12" y2="15.5" stroke="currentColor"/> 20844 </g> 20845 <g style="--index: 6;"> 20846 <line class="line" stroke-linecap="round" pathLength="1" x1="12" y1="8.5" x2="12" y2="15.5" stroke="currentColor"/> 20847 </g> 20848 <g style="--index: 7;"> 20849 <line class="line" stroke-linecap="round" pathLength="1" x1="12" y1="8.5" x2="12" y2="15.5" stroke="currentColor"/> 20850 </g> 20851 </g> 20852</svg> 20853</span> 20854 </span> 20855 </button> 20856</add-to-cart-component> 20857 20858 20859 <button 20860 class="button quick-add__button quick-add__button--choose add-to-cart-button" 20861 20862 type="button" 20863 name="add" 20864 on:click="quick-add-component/handleClick" 20865 > 20866 <span 20867 class="add-to-cart-text" 20868 > 20869 20870 <span 20871 aria-hidden="true" 20872 class="svg-wrapper add-to-cart-icon" 20873 ><svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="none"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="var(--icon-stroke-width)" d="M16.608 9.421V6.906H3.392v8.016c0 .567.224 1.112.624 1.513.4.402.941.627 1.506.627H8.63M8.818 3h2.333c.618 0 1.212.247 1.649.686a2.35 2.35 0 0 1 .683 1.658v1.562H6.486V5.344c0-.622.246-1.218.683-1.658A2.33 2.33 0 0 1 8.82 3"/><path stroke="currentColor" stroke-linecap="round" stroke-width="var(--icon-stroke-width)" d="M14.608 12.563v5m2.5-2.5h-5"/></svg> 20874</span> 20875 <span class="add-to-cart-text__content is-visually-hidden-mobile"> 20876 <span> 20877 20878 <span> 20879 Choose 20880 </span> 20881 </span> 20882 </span> 20883 </span> 20884 </button><input type="hidden" name="product-id" value="15399681392806" /></form></product-form-component> 20885</quick-add-component> 20886 20887 20888 20889</div></div> 20890 20891 <div class="bp-card__body"><h3 class="bp-card__title"> 20892 <a href="/products/leopard-print-skirt-brown-multi">Leopard Print Skirt</a> 20893 </h3> 20894 20895 <div class="bp-card__price-row"><span class="bp-card__price">$110</span> 20896 <s class="bp-card__compare" hidden></s></div></div> 20897 </article> 20898</product-card> 20899 20900 </li> 20901 20902 <li 20903 class="predictive-search-results__card predictive-search-results__card--product" 20904 ref="resultsItems[]" 20905 > 20906 <product-card class="bp-card-host"> 20907 <article 20908 class="bp-card" 20909 data-product-id="15399681360038" 20910 data-product-url="/products/metallic-zebra-printed-dress-tan-multi" 20911 data-bp-card-secondary-hover="true" 20912 data-bp-card-qv-dest="drawer" 20913 data-bp-card-qv-vis-d="hover" 20914 data-bp-card-qv-vis-m="shown" 20915 data-bp-card-qb-style="button" 20916 data-bp-track-product='{ "id": 15399681360038, "title": "Metallic Zebra Printed Dress Tan Multi", "vendor": "Bagatelle", "handle": "metallic-zebra-printed-dress-tan-multi", "url": "\/products\/metallic-zebra-printed-dress-tan-multi", "image": "\/\/www.bostonproper.com\/cdn\/shop\/files\/2DO0590_
20916SET.jpg?v=1791009160\u0026width=720", "category": "Dresses", "product_type":null, "selected_variant": { "id": 56779670782118, "sku": "2DO0590006", "price": 180.0, "compare_at_price": 180.0, "option1": "Tan Multi", "option2": "6", "product_id": 15399681360038, "available": true, "inventory_quantity": 50 } }' 20917 > 20918 <div class="bp-card__media"> 20919 <a class="bp-card__media-link" href="/products/metallic-zebra-printed-dress-tan-multi" aria-label="Metallic Zebra Printed Dress Tan Multi" ref="productCardLink"><img 20920 class="bp-card__image bp-card__image--primary" 20921 src="//www.bostonproper.com/cdn/shop/files/2DO0590_SET.jpg?v=1791009160&width=720" 20922 srcset="//www.bostonproper.com/cdn/shop/files/2DO0590_SET.jpg?v=1791009160&width=360 360w, //www.bostonproper.com/cdn/shop/files/2DO0590_SET.jpg?v=1791009160&width=540 540w, //www.bostonproper.com/cdn/shop/files/2DO0590_SET.jpg?v=1791009160&width=720 720w, //www.bostonproper.com/cdn/shop/files/2DO0590_SET.jpg?v=1791009160&width=1080 1080w" 20923 sizes="(min-width: 1024px) 25vw, 50vw" 20924 width="1639" 20925 height="2048" 20926 alt="Metallic Zebra Printed Dress Tan Multi" 20927 loading="lazy" 20928 data-bp-card-image-primary 20929 ><img 20930 class="bp-card__image bp-card__image--secondary" 20931 src="//www.bostonproper.com/cdn/shop/files/2_0161cf9a-a917-4e26-98d6-d48db12f058a.jpg?v=1791009160&width=720" 20932 srcset="//www.bostonproper.com/cdn/shop/files/2_0161cf9a-a917-4e26-98d6-d48db12f058a.jpg?v=1791009160&width=360 360w, //www.bostonproper.com/cdn/shop/files/2_0161cf9a-a917-4e26-98d6-d48db12f058a.jpg?v=1791009160&width=540 540w, //www.bostonproper.com/cdn/shop/files/2_0161cf9a-a917-4e26-98d6-d48db12f058a.jpg?v=1791009160&width=720 720w" 20933 sizes="(min-width: 1024px) 25vw, 50vw" 20934 width="1639" 20935 height="2048" 20936 alt="" 20937 loading="lazy" 20938 aria-hidden="true" 20939 data-bp-card-image-secondary 20940 ></a><div class="bp-card__badges" data-bp-card-badges></div><button 20941 type="button" 20942 class="bp-card__quick" 20943 aria-label="Quick add Metallic Zebra Printed Dress Tan Multi" 20944 data-product-id="15399681360038" 20945 data-bp-card-qv-trigger 20946 > 20947 <svg viewBox="0 0 20 20" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> 20948 <path d="M4.18929 5.74434H16.5343H16.6292L16.6729 5.74549C16.9007 5.75099 17.1328 5.87803 17.2448 6.08089C17.3145 6.20157 17.3382 6.30372 17.3547 6.43048C17.6354 9.18806 17.9008 11.996 18.3051 14.7356C18.3771 15.2467 18.4466 15.758 18.5137 16.2697C18.5163 16.2917 18.5195 16.336 18.5207 16.3582C18.5378 16.6193 18.4156 16.9263 18.1508 17.0687C17.9651 17.1752 17.7807 17.1763 17.5845 17.1755C16.1978 17.1755 4.69428 17.1755 3.34023 17.1755C3.20422 17.1726 3.01496 17.1862 2.86505 17.1451C2.45036 17.0406 2.25213 16.656 2.31666 16.2445C2.41071 15.5271 2.58579 14.2489 2.68621 13.5185C2.95476 11.5399 3.15849 9.46674 3.35962 7.47459C3.37582 7.3571 3.44296 6.63739 3.45714 6.52887L3.46698 6.43424L3.46814 6.42237C3.46814 6.41948 3.46901 6.4134 3.47045 6.40559C3.5156 6.02417 3.79254 5.74028 4.18929 5.74434ZM4.18929 6.6125C4.2136 6.6125 4.24804 6.59601 4.27872 6.57343C4.30302 6.55491 4.3201 6.53089 4.32704 6.51845C4.33457 6.50601 4.33486 6.50138 4.33399 6.50427C4.33341 6.50803 4.33225 6.51353 4.33138 6.51758C4.33138 6.51758 4.33081 6.51932 4.33052 6.52424L4.32936 6.5361L4.31923 6.63044C4.132 8.4151 3.9054 10.3181 3.74624 12.1016C3.63685 13.4481 3.56103 15.0256 3.46727 16.3753L3.46582 16.3915V16.3961C3.46582 16.3961 3.46611 16.3973 3.4664 16.3915C3.4664 16.3872 3.4664 16.375 3.46351 16.3588C3.45396 16.2908 3.41721 16.2161 3.3602 16.1507C3.29306 16.0798 3.20422 16.0246 3.12319 16.0162C3.11856 16.0162 3.13361 16.0173 3.15647 16.0176C4.67199 16.0182 16.0117 16.0176 17.5848 16.0176H17.6719C17.6823 16.017 17.6916 16.0168 17.6962 16.0165C17.7014 16.0162 17.7034 16.0159 17.6991 16.0162C17.5752 16.0286 17.4268 16.1591 17.3857 16.2795C17.3576 16.3556 17.3617 16.3788 17.362 16.3941C17.362 16.3921 17.362 16.393 17.362 16.3886L17.3605 16.3724C17.254 14.8543 17.1872 13.3292 17.0509 11.8148C16.8906 10.0895 16.6752 8.23163 16.4883 6.50456C16.495 6.52019 16.5161 6.55433 16.5468 6.57575C16.5714 6.59485 16.5989 6.60585 16.6145 6.60961L16.6243 6.61192H16.6284C16.6284 6.61192 16.6298 6.61279 16.6304 6.61279C16.6318 6.61279 16.6336 6.61279 16.633 6.61279C16.6324 6.61279 16.635 6.61279 16.6292 6.61279C16.5783 6.61221 16.4 6.61337 16.3445 6.61279H4.18929V6.6125Z"/> 20949 <path d="M6.08301 8.78064C6.1788 7.5189 6.20658 6.21405 6.58539 4.98907C7.15432 3.11007 8.4421 2.11631 10.414 2.12673C13.8001 2.13338 14.5031 4.87997 14.6721 7.71395C14.6964 8.07453 14.7158 8.43453 14.745 8.78035L13.5874 8.77485C13.6172 8.46057 13.6575 8.02649 13.6754 7.71858C13.801 5.83351 13.5976 3.34129 11.3988 2.81605C10.7575 2.6676 10.0702 2.6676 9.42892 2.81605C7.61503 3.24811 7.15143 5.0374 7.12683 6.6872C7.11121 7.36929 7.17545 8.09132 7.24056 8.77485L6.08301 8.78035V8.78064Z"/> 20950 </svg> 20951 </button><div class="bp-card__quick-add"> 20952 20953<quick-add-component 20954 class="quick-add color-scheme-1 " 20955 ref="quickAdd" 20956 data-product-title="Metallic Zebra Printed Dress Tan Multi" 20957 data-quick-add-button="choose" 20958 data-product-options-count="2" 20959> 20960 <product-form-component 20961 data-section-id="bp-card" 20962 data-product-id="15399681360038" 20963 on:submit="/handleSubmit" 20964 class="quick-add__product-form-component" 20965 > 20966 <div 20967 class="visually-hidden"
20968 aria-live="assertive" 20969 role="status" 20970 aria-atomic="true" 20971 ref="liveRegion" 20972 ></div><form method="post" action="/cart/add" id="QuickAdd-ProductForm-15399681360038-" accept-charset="UTF-8" class="shopify-product-form" enctype="multipart/form-data" novalidate="novalidate" data-type="add-to-cart-form"><input type="hidden" name="form_type" value="product" /><input type="hidden" name="utf8" value="â" /><input 20973 type="hidden" 20974 name="id" 20975 ref="variantId" 20976 value="56779670782118" 20977 20978 > 20979 <input 20980 type="hidden" 20981 name="quantity" 20982 value="1" 20983 > 20984 20985 <add-to-cart-component 20986 ref="addToCartButtonContainer" 20987 data-product-variant-media="//www.bostonproper.com/cdn/shop/files/2DO0590_SET.jpg?v=1791009160&width=100" 20988 data-add-to-cart-animation="true" 20989> 20990 <button 20991 20992 type="submit" 20993 name="add" 20994 ref="addToCartButton" 20995 on:click="/handleClick" 20996 class="button button quick-add__button quick-add__button--add add-to-cart-button" 20997 20998 20999 > 21000 <span 21001 class="add-to-cart-text" 21002 > 21003 21004 <span 21005 aria-hidden="true" 21006 class="svg-wrapper add-to-cart-icon" 21007 ><svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="none"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="var(--icon-stroke-width)" d="M16.608 9.421V6.906H3.392v8.016c0 .567.224 1.112.624 1.513.4.402.941.627 1.506.627H8.63M8.818 3h2.333c.618 0 1.212.247 1.649.686a2.35 2.35 0 0 1 .683 1.658v1.562H6.486V5.344c0-.622.246-1.218.683-1.658A2.33 2.33 0 0 1 8.82 3"/><path stroke="currentColor" stroke-linecap="round" stroke-width="var(--icon-stroke-width)" d="M14.608 12.563v5m2.5-2.5h-5"/></svg> 21008</span> 21009 21010 <span class="add-to-cart-text__content is-visually-hidden-mobile"> 21011 <span> 21012 <span>Add</span> 21013 </span> 21014 </span> 21015 </span> 21016 21017 <span class="add-to-cart__added"> 21018 <span class="svg-wrapper add-to-cart__added-icon"><svg aria-hidden="true" class="checkmark-burst" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> 21019 <g class="check"> 21020 <circle class="ring" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> 21021 <path class="tick" d="M9 12.75L11.25 15L15 9.75" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> 21022 </g> 21023 <g class="burst"> 21024 <g style="--index: 0;"> 21025 <line class="line" stroke-linecap="round" pathLength="1" x1="12" y1="8.5" x2="12" y2="15.5" stroke="currentColor"/> 21026 </g> 21027 <g style="--index: 1;"> 21028 <line class="line" stroke-linecap="round" pathLength="1" x1="12" y1="8.5" x2="12" y2="15.5" stroke="currentColor"/> 21029 </g> 21030 <g style="--index: 2;"> 21031 <line class="line" stroke-linecap="round" pathLength="1" x1="12" y1="8.5" x2="12" y2="15.5" stroke="currentColor"/> 21032 </g> 21033 <g style="--index: 3;"> 21034 <line class="line" stroke-linecap="round" pathLength="1" x1="12" y1="8.5" x2="12" y2="15.5" stroke="currentColor"/> 21035 </g> 21036 <g style="--index: 4;"> 21037 <line class="line" stroke-linecap="round" pathLength="1" x1="12" y1="8.5" x2="12" y2="15.5" stroke="currentColor"/> 21038 </g> 21039 <g style="--index: 5;"> 21040 <line class="line" stroke-linecap="round" pathLength="1" x1="12" y1="8.5" x2="12" y2="15.5" stroke="currentColor"/> 21041 </g> 21042 <g style="--index: 6;"> 21043 <line class="line" stroke-linecap="round" pathLength="1" x1="12" y1="8.5" x2="12" y2="15.5" stroke="currentColor"/> 21044 </g> 21045 <g style="--index: 7;"> 21046 <line class="line" stroke-linecap="round" pathLength="1" x1="12" y1="8.5" x2="12" y2="15.5" stroke="currentColor"/> 21047 </g> 21048 </g> 21049</svg> 21050</span> 21051 </span> 21052 </button> 21053</add-to-cart-component> 21054 21055 21056 <button 21057 class="button quick-add__button quick-add__button--choose add-to-cart-button" 21058 21059 type="button" 21060 name="add" 21061 on:click="quick-add-component/handleClick" 21062 > 21063 <span 21064 class="add-to-cart-text" 21065 > 21066
21067 <span 21068 aria-hidden="true" 21069 class="svg-wrapper add-to-cart-icon" 21070 ><svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="none"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="var(--icon-stroke-width)" d="M16.608 9.421V6.906H3.392v8.016c0 .567.224 1.112.624 1.513.4.402.941.627 1.506.627H8.63M8.818 3h2.333c.618 0 1.212.247 1.649.686a2.35 2.35 0 0 1 .683 1.658v1.562H6.486V5.344c0-.622.246-1.218.683-1.658A2.33 2.33 0 0 1 8.82 3"/><path stroke="currentColor" stroke-linecap="round" stroke-width="var(--icon-stroke-width)" d="M14.608 12.563v5m2.5-2.5h-5"/></svg> 21071</span> 21072 <span class="add-to-cart-text__content is-visually-hidden-mobile"> 21073 <span> 21074 21075 <span> 21076 Choose 21077 </span> 21078 </span> 21079 </span> 21080 </span> 21081 </button><input type="hidden" name="product-id" value="15399681360038" /></form></product-form-component> 21082</quick-add-component> 21083 21084 21085 21086</div></div> 21087 21088 <div class="bp-card__body"><h3 class="bp-card__title"> 21089 <a href="/products/metallic-zebra-printed-dress-tan-multi">Metallic Zebra Printed Dress</a> 21090 </h3> 21091 21092 <div class="bp-card__price-row"><span class="bp-card__price">$180</span> 21093 <s class="bp-card__compare" hidden></s></div></div> 21094 </article> 21095</product-card> 21096 21097 </li> 21098 21099 <li 21100 class="predictive-search-results__card predictive-search-results__card--product" 21101 ref="resultsItems[]" 21102 > 21103 <product-card class="bp-card-host"> 21104 <article 21105 class="bp-card" 21106 data-product-id="15399680114854" 21107 data-product-url="/products/sam-edelman-fur-lace-up-bootie-tan" 21108 data-bp-card-secondary-hover="true" 21109 data-bp-card-qv-dest="drawer" 21110 data-bp-card-qv-vis-d="hover" 21111 data-bp-card-qv-vis-m="shown" 21112 data-bp-card-qb-style="button" 21113 data-bp-track-product='{ "id": 15399680114854, "title": "Sam Edelman Fur Lace Up Bootie Tan", "vendor": "Sam Edelman", "handle": "sam-edelman-fur-lace-up-bootie-tan", "url": "\/products\/sam-edelman-fur-lace-up-bootie-tan", "image": "\/\/www.bostonproper.com\/cdn\/shop\/files\/2DO5680_SET.jpg?v=1791009185\u0026width=720", "category": "Shoes", "product_type":null, "selected_variant": { "id": 56779666161830, "sku": "2DO5680060", "price": 180.0, "compare_at_price": 180.0, "option1": "Tan", "option2": "6.0", "product_id": 15399680114854, "available": true, "inventory_quantity": 13 } }' 21114 > 21115 <div class="bp-card__media"> 21116 <a class="bp-card__media-link" href="/products/sam-edelman-fur-lace-up-bootie-tan" aria-label="Sam Edelman Fur Lace Up Bootie Tan" ref="productCardLink"><img 21117 class="bp-card__image bp-card__image--primary" 21118 src="//www.bostonproper.com/cdn/shop/files/2DO5680_SET.jpg?v=1791009185&width=720" 21119 srcset="//www.bostonproper.com/cdn/shop/files/2DO5680_SET.jpg?v=1791009185&width=360 360w, //www.bostonproper.com/cdn/shop/files/2DO5680_SET.jpg?v=1791009185&width=540 540w, //www.bostonproper.com/cdn/shop/files/2DO5680_SET.jpg?v=1791009185&width=720 720w, //www.bostonproper.com/cdn/shop/files/2DO5680_SET.jpg?v=1791009185&width=1080 1080w" 21120 sizes="(min-width: 1024px) 25vw, 50vw" 21121 width="1639" 21122 height="2048" 21123 alt="Sam Edelman Fur Lace Up Bootie Tan" 21124 loading="lazy" 21125 data-bp-card-image-primary 21126 ><img 21127 class="bp-card__image bp-card__image--secondary" 21128 src="//www.bostonproper.com/cdn/shop/files/2_811cc34e-278e-4bea-a759-24085c37ff4f.jpg?v=1791009185&width=720" 21129 srcset="//www.bostonproper.com/cdn/shop/files/2_811cc34e-278e-4bea-a759-24085c37ff4f.jpg?v=1791009185&width=360 360w, //www.bostonproper.com/cdn/shop/files/2_811cc34e-278e-4bea-a759-24085c37ff4f.jpg?v=1791009185&width=540 540w, //www.bostonproper.com/cdn/shop/files/2_811cc34e-278e-4bea-a759-24085c37ff4f.jpg?v=1791009185&width=720 720w" 21130 sizes="(min-width: 1024px) 25vw, 50vw" 21131 width="1639" 21132 height="2048" 21133 alt="" 21134 loading="lazy" 21135 aria-hidden="true" 21136 data-bp-card-image-secondary 21137 ></a><div class="bp-card__badges" data-bp-card-badges></div><button 21138 type="button" 21139 class="bp-card__quick" 21140 aria-label="Quick add Sam Edelman Fur Lace Up Bootie Tan" 21141 data-product-id="15399680114854" 21142 data-bp-card-qv-trigger 21143 > 21144 <svg viewBox="0 0 20 20" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> 21145 <path d="M4.18929 5.74434H16.5343H16.6292L16.6729 5.74549C16.9007 5.75099 17.1328 5.87803 17.2448 6.08089C17.3145 6.20157 17.3382 6.30372 17.3547 6.43048C17.6354 9.18806 17.9008 11.996 18.3051 14.7356C18.3771 15.2467 18.4466 15.758 18.5137 16.2697C18.5163 16.2917 18.5195 16.336 18.5207 16.3582C18.5378 16.6193 18.4156 16.9263 18.1508 17.0687C17.9651 17.1752 17.7807 17.1763 17.5845 17.1755C16.1978 17.1755 4.69428 17.1755 3.34023 17.1755C3.20422 17.1726 3.01496 17.1862 2.86505 17.1451C2.45036 17.0406 2.25213 16.656 2.31666 16.2445C2.41071 15.5271 2.58579 14.2489 2.68621 13.5185C2.95476 11.5399 3.15849 9.46674 3.35962 7.47459C3.37582 7.3571 3.44296 6.63739 3.45714 6.52887L3.46698 6.43424L3.46814 6.42237C3.46814 6.41948 3.46901 6.4134 3.47045 6.40559C3.5156 6.02417 3.79254 5.74
21145028 4.18929 5.74434ZM4.18929 6.6125C4.2136 6.6125 4.24804 6.59601 4.27872 6.57343C4.30302 6.55491 4.3201 6.53089 4.32704 6.51845C4.33457 6.50601 4.33486 6.50138 4.33399 6.50427C4.33341 6.50803 4.33225 6.51353 4.33138 6.51758C4.33138 6.51758 4.33081 6.51932 4.33052 6.52424L4.32936 6.5361L4.31923 6.63044C4.132 8.4151 3.9054 10.3181 3.74624 12.1016C3.63685 13.4481 3.56103 15.0256 3.46727 16.3753L3.46582 16.3915V16.3961C3.46582 16.3961 3.46611 16.3973 3.4664 16.3915C3.4664 16.3872 3.4664 16.375 3.46351 16.3588C3.45396 16.2908 3.41721 16.2161 3.3602 16.1507C3.29306 16.0798 3.20422 16.0246 3.12319 16.0162C3.11856 16.0162 3.13361 16.0173 3.15647 16.0176C4.67199 16.0182 16.0117 16.0176 17.5848 16.0176H17.6719C17.6823 16.017 17.6916 16.0168 17.6962 16.0165C17.7014 16.0162 17.7034 16.0159 17.6991 16.0162C17.5752 16.0286 17.4268 16.1591 17.3857 16.2795C17.3576 16.3556 17.3617 16.3788 17.362 16.3941C17.362 16.3921 17.362 16.393 17.362 16.3886L17.3605 16.3724C17.254 14.8543 17.1872 13.3292 17.0509 11.8148C16.8906 10.0895 16.6752 8.23163 16.4883 6.50456C16.495 6.52019 16.5161 6.55433 16.5468 6.57575C16.5714 6.59485 16.5989 6.60585 16.6145 6.60961L16.6243 6.61192H16.6284C16.6284 6.61192 16.6298 6.61279 16.6304 6.61279C16.6318 6.61279 16.6336 6.61279 16.633 6.61279C16.6324 6.61279 16.635 6.61279 16.6292 6.61279C16.5783 6.61221 16.4 6.61337 16.3445 6.61279H4.18929V6.6125Z"/> 21146 <path d="M6.08301 8.78064C6.1788 7.5189 6.20658 6.21405 6.58539 4.98907C7.15432 3.11007 8.4421 2.11631 10.414 2.12673C13.8001 2.13338 14.5031 4.87997 14.6721 7.71395C14.6964 8.07453 14.7158 8.43453 14.745 8.78035L13.5874 8.77485C13.6172 8.46057 13.6575 8.02649 13.6754 7.71858C13.801 5.83351 13.5976 3.34129 11.3988 2.81605C10.7575 2.6676 10.0702 2.6676 9.42892 2.81605C7.61503 3.24811 7.15143 5.0374 7.12683 6.6872C7.11121 7.36929 7.17545 8.09132 7.24056 8.77485L6.08301 8.78035V8.78064Z"/> 21147 </svg> 21148 </button><div class="bp-card__quick-add"> 21149 21150<quick-add-component 21151 class="quick-add color-scheme-1 " 21152 ref="quickAdd" 21153 data-product-title="Sam Edelman Fur Lace Up Bootie Tan" 21154 data-quick-add-button="choose" 21155 data-product-options-count="2" 21156> 21157 <product-form-component 21158 data-section-id="bp-card" 21159 data-product-id="15399680114854" 21160 on:submit="/handleSubmit" 21161 class="quick-add__product-form-component" 21162 > 21163 <div 21164 class="visually-hidden" 21165 aria-live="assertive" 21166 role="status" 21167 aria-atomic="true" 21168 ref="liveRegion" 21169 ></div><form method="post" action="/cart/add" id="QuickAdd-ProductForm-15399680114854-" accept-charset="UTF-8" class="shopify-product-form" enctype="multipart/form-data" novalidate="novalidate" data-type="add-to-cart-form"><input type="hidden" name="form_type" value="product" /><input type="hidden" name="utf8" value="â" /><input 21170 type="hidden" 21171 name="id" 21172 ref="variantId" 21173 value="56779666161830" 21174 21175 > 21176 <input 21177 type="hidden" 21178 name="quantity" 21179 value="1" 21180 > 21181 21182 <add-to-cart-component 21183 ref="addToCartButtonContainer" 21184 data-product-variant-media="//www.bostonproper.com/cdn/shop/files/2DO5680_SET.jpg?v=1791009185&width=100" 21185 data-add-to-cart-animation="true" 21186> 21187 <button 21188 21189 type="submit" 21190 name="add" 21191 ref="addToCartButton" 21192 on:click="/handleClick" 21193 class="button button quick-add__button quick-add__button--add add-to-cart-button" 21194 21195 21196 > 21197 <span 21198 class="add-to-cart-text" 21199 > 21200 21201 <span 21202 aria-hidden="true" 21203 class="svg-wrapper add-to-cart-icon" 21204 ><svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="none"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="var(--icon-stroke-width)" d="M16.608 9.421V6.906H3.392v8.016c0 .567.224 1.112.624 1.513.4.402.941.627 1.506.627H8.63M8.818 3h2.333c.618 0 1.212.247 1.649.686a2.35 2.35 0 0 1 .683 1.658v1.562H6.486V5.344c0-.622.246-1.218.683-1.658A2.33 2.33 0 0 1 8.82 3"/><path stroke="currentColor" stroke-linecap="round" stroke-width="var(--icon-stroke-width)" d="M14.608 12.563v5m2.5-2.5h-5"/></svg> 21205</span> 21206
21207 <span class="add-to-cart-text__content is-visually-hidden-mobile"> 21208 <span> 21209 <span>Add</span> 21210 </span> 21211 </span> 21212 </span> 21213 21214 <span class="add-to-cart__added"> 21215 <span class="svg-wrapper add-to-cart__added-icon"><svg aria-hidden="true" class="checkmark-burst" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> 21216 <g class="check"> 21217 <circle class="ring" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> 21218 <path class="tick" d="M9 12.75L11.25 15L15 9.75" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> 21219 </g> 21220 <g class="burst"> 21221 <g style="--index: 0;"> 21222 <line class="line" stroke-linecap="round" pathLength="1" x1="12" y1="8.5" x2="12" y2="15.5" stroke="currentColor"/> 21223 </g> 21224 <g style="--index: 1;"> 21225 <line class="line" stroke-linecap="round" pathLength="1" x1="12" y1="8.5" x2="12" y2="15.5" stroke="currentColor"/> 21226 </g> 21227 <g style="--index: 2;"> 21228 <line class="line" stroke-linecap="round" pathLength="1" x1="12" y1="8.5" x2="12" y2="15.5" stroke="currentColor"/> 21229 </g> 21230 <g style="--index: 3;"> 21231 <line class="line" stroke-linecap="round" pathLength="1" x1="12" y1="8.5" x2="12" y2="15.5" stroke="currentColor"/> 21232 </g> 21233 <g style="--index: 4;"> 21234 <line class="line" stroke-linecap="round" pathLength="1" x1="12" y1="8.5" x2="12" y2="15.5" stroke="currentColor"/> 21235 </g> 21236 <g style="--index: 5;"> 21237 <line class="line" stroke-linecap="round" pathLength="1" x1="12" y1="8.5" x2="12" y2="15.5" stroke="currentColor"/> 21238 </g> 21239 <g style="--index: 6;"> 21240 <line class="line" stroke-linecap="round" pathLength="1" x1="12" y1="8.5" x2="12" y2="15.5" stroke="currentColor"/> 21241 </g> 21242 <g style="--index: 7;"> 21243 <line class="line" stroke-linecap="round" pathLength="1" x1="12" y1="8.5" x2="12" y2="15.5" stroke="currentColor"/> 21244 </g> 21245 </g> 21246</svg> 21247</span> 21248 </span> 21249 </button> 21250</add-to-cart-component> 21251 21252 21253 <button 21254 class="button quick-add__button quick-add__button--choose add-to-cart-button" 21255 21256 type="button" 21257 name="add" 21258 on:click="quick-add-component/handleClick" 21259 > 21260 <span 21261 class="add-to-cart-text" 21262 > 21263 21264 <span 21265 aria-hidden="true" 21266 class="svg-wrapper add-to-cart-icon" 21267 ><svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="none"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="var(--icon-stroke-width)" d="M16.608 9.421V6.906H3.392v8.016c0 .567.224 1.112.624 1.513.4.402.941.627 1.506.627H8.63M8.818 3h2.333c.618 0 1.212.247 1.649.686a2.35 2.35 0 0 1 .683 1.658v1.562H6.486V5.344c0-.622.246-1.218.683-1.658A2.33 2.33 0 0 1 8.82 3"/><path stroke="currentColor" stroke-linecap="round" stroke-width="var(--icon-stroke-width)" d="M14.608 12.563v5m2.5-2.5h-5"/></svg> 21268</span> 21269 <span class="add-to-cart-text__content is-visually-hidden-mobile"> 21270 <span> 21271 21272 <span> 21273 Choose 21274 </span> 21275 </span> 21276 </span> 21277 </span> 21278 </button><input type="hidden" name="product-id" value="15399680114854" /></form></product-form-component> 21279</quick-add-component> 21280 21281 21282 21283</div></div> 21284 21285 <div class="bp-card__body"><h3 class="bp-card__title"> 21286 <a href="/products/sam-edelman-fur-lace-up-bootie-tan">Sam Edelman Fur Lace Up Bootie</a> 21287 </h3> 21288 21289 <div class="bp-card__price-row"><span class="bp-card__price">$180</span> 21290 <s class="bp-card__compare" hidden></s></div></div> 21291 </article> 21292</product-card> 21293 21294 </li> 21295 21296 <li 21297 class="predictive-search-results__card predictive-search-results__card--product" 21298 ref="resultsItems[]" 21299 > 21300 <product-card class="bp-card-host"> 21301 <article 21302 class="bp-card" 21303 data-product-id="15399680082086" 21304 data-product-url="/products/ultimate-velvet-double-breasted-shaped-blazer-black" 21305 data-bp-card-secondary-hover="true" 21306 data-bp-card-qv-dest="drawer" 21307 data-bp-card-qv-vis-d="hover" 21308 data-bp-card-qv-vis-m="shown" 21309 data-bp-card-qb-style="button" 21310 data-bp-track-product='{ "id": 15399680082086, "title": "Ultimate Velvet Double Breasted Shaped Blazer Black", "vendor": "Boston Proper", "handle": "ultimate-velvet-double-breasted-shaped-blazer-black", "url": "\/products\/ultimate-velvet-double-breasted-shaped-blazer-black", "image": "\/\/www.bostonproper.com\/cdn\/shop\/files\/2DP1920_SET.jpg?v=1791009211\u0026width=720", "category": "Jackets", "product_type":null, "selected_variant": { "id": 56779665801382, "sku": "2DP1920002", "price": 150.0, "compare_at_price": 150.0, "option1": "Black", "option2": "2", "product_id": 15399680082086, "available": true, "inventory_quantity": 21 } }' 21311 > 21312 <div class="bp-card__media"> 21313 <a class="bp-card__media-link" href="/products/ultimate-velvet-double-breasted-shaped-blazer-black" aria-label="Ultimate Velvet Double Breasted Shaped Blazer Black" ref="productCardLink"><img 21314 class="bp-card__image bp-card__image--primary" 21315 src="//www.bostonproper.com/cdn/shop/files/2DP1920_SET.jpg?v=1791009211&width=720" 21316 srcset="//www.bostonproper.com/cdn/shop/files/2DP1920_SET.jpg?v=1791009211&width=360 360w, //www.bostonproper.com/cdn/shop/files/2DP1920_SET.jpg?v=1791009211&width=540 540w, //www.bostonproper.com/cdn/shop/files/2DP1920_SET.jpg?v=1791009211&width=720 720w, //www.bostonproper.com/cdn/shop/files/2DP1920_SET.jpg?v=1791009211&width=1080 1080w" 21317 sizes="(min-width: 1024px) 25vw, 50vw" 21318 width="1639" 21319 height="2048" 21320 alt="Ultimate Velvet Double Breasted Shaped Blazer Black" 21321 loading="lazy" 21322 data-bp-card-image-primary 21323 ><img 21324 class="bp-card__image bp-card__image--secondary" 21325 src="//www.bostonproper.com/cdn/shop/files/2_1560b9bf-904f-46e5-b2c8-2e27b8946935.jpg?v=1791009211&width=720" 21326 srcset="//www.bostonproper.com/cdn/shop/files/2_1560b9bf-904f-46e5-b2c8-2e27b8946935.jpg?v=1791009211&width=360 360w, //www.bostonproper.com/cdn/shop/files/2_1560b9bf-904f-46e5-b2c8-2e27b8946935.jpg?v=1791009211&width=540 540w, //www.bostonproper.com/cdn/shop/files/2_1560b9bf-904f-46e5-b2c8-2e27b8946935.jpg?v=1791009211&width=720 720w" 21327 sizes="(min-width: 1024px) 25vw, 50vw" 21328 width="1639" 21329 height="2048" 21330 alt="" 21331 loading="lazy" 21332 aria-hidden="true" 21333 data-bp-card-image-secondary 21334 ></a><div class="bp-card__badges" data-bp-card-badges></div><button 21335 type="button" 21336 class="bp-card__quick" 21337 aria-label="Quick add Ultimate Velvet Double Breasted Shaped Blazer Black" 21338 data-product-id="15399680082086" 21339 data-bp-card-qv-trigger 21340 > 21341 <svg viewBox="0 0 20 20" fill="currentColor" xmlns="http://www.w3.org/2000/svg"> 21342 <path d="M4.18929 5.74434H16.5343H16.6292L16.6729 5.74549C16.9007 5.75099 17.1328 5.87803 17.2448 6.08089C17.3145 6.20157 17.3382 6.30372 17.3547 6.43048C17.6354 9.18806 17.9008 11.996 18.3051 14.7356C18.3771 15.2467 18.4466 15.758 18.5137 16.2697C18.5163 16.2917 18.5195 16.336 18.5207 16.3582C18.5378 16.6193 18.4156 16.9263 18.1508 17.0687C17.9651 17.1752 17.7807 17.1763 17.5845 17.1755C16.1978 17.1755 4.69428 17.1755 3.34023 17.1755C3.20422 17.1726 3.01496 17.1862 2.86505 17.1451C2.45036 17.0406 2.25213 16.656 2.31666 16.2445C2.41071 15.5271 2.58579 14.2489 2.68621 13.5185C2.95476 11.5399 3.15849 9.46674 3.35962 7.47459C3.37582 7.3571 3.44296 6.63739 3.45714 6.52887L3.46698 6.43424L3.46814 6.42237C3.46814 6.41948 3.46901 6.4134 3.47045 6.40559C3.5156 6.02417 3.79254 5.74
21342028 4.18929 5.74434ZM4.18929 6.6125C4.2136 6.6125 4.24804 6.59601 4.27872 6.57343C4.30302 6.55491 4.3201 6.53089 4.32704 6.51845C4.33457 6.50601 4.33486 6.50138 4.33399 6.50427C4.33341 6.50803 4.33225 6.51353 4.33138 6.51758C4.33138 6.51758 4.33081 6.51932 4.33052 6.52424L4.32936 6.5361L4.31923 6.63044C4.132 8.4151 3.9054 10.3181 3.74624 12.1016C3.63685 13.4481 3.56103 15.0256 3.46727 16.3753L3.46582 16.3915V16.3961C3.46582 16.3961 3.46611 16.3973 3.4664 16.3915C3.4664 16.3872 3.4664 16.375 3.46351 16.3588C3.45396 16.2908 3.41721 16.2161 3.3602 16.1507C3.29306 16.0798 3.20422 16.0246 3.12319 16.0162C3.11856 16.0162 3.13361 16.0173 3.15647 16.0176C4.67199 16.0182 16.0117 16.0176 17.5848 16.0176H17.6719C17.6823 16.017 17.6916 16.0168 17.6962 16.0165C17.7014 16.0162 17.7034 16.0159 17.6991 16.0162C17.5752 16.0286 17.4268 16.1591 17.3857 16.2795C17.3576 16.3556 17.3617 16.3788 17.362 16.3941C17.362 16.3921 17.362 16.393 17.362 16.3886L17.3605 16.3724C17.254 14.8543 17.1872 13.3292 17.0509 11.8148C16.8906 10.0895 16.6752 8.23163 16.4883 6.50456C16.495 6.52019 16.5161 6.55433 16.5468 6.57575C16.5714 6.59485 16.5989 6.60585 16.6145 6.60961L16.6243 6.61192H16.6284C16.6284 6.61192 16.6298 6.61279 16.6304 6.61279C16.6318 6.61279 16.6336 6.61279 16.633 6.61279C16.6324 6.61279 16.635 6.61279 16.6292 6.61279C16.5783 6.61221 16.4 6.61337 16.3445 6.61279H4.18929V6.6125Z"/> 21343 <path d="M6.08301 8.78064C6.1788 7.5189 6.20658 6.21405 6.58539 4.98907C7.15432 3.11007 8.4421 2.11631 10.414 2.12673C13.8001 2.13338 14.5031 4.87997 14.6721 7.71395C14.6964 8.07453 14.7158 8.43453 14.745 8.78035L13.5874 8.77485C13.6172 8.46057 13.6575 8.02649 13.6754 7.71858C13.801 5.83351 13.5976 3.34129 11.3988 2.81605C10.7575 2.6676 10.0702 2.6676 9.42892 2.81605C7.61503 3.24811 7.15143 5.0374 7.12683 6.6872C7.11121 7.36929 7.17545 8.09132 7.24056 8.77485L6.08301 8.78035V8.78064Z"/> 21344 </svg> 21345 </button><div class="bp-card__quick-add"> 21346 21347<quick-add-component 21348 class="quick-add color-scheme-1 " 21349 ref="quickAdd" 21350 data-product-title="Ultimate Velvet Double Breasted Shaped Blazer Black" 21351 data-quick-add-button="choose" 21352 data-product-options-count="2" 21353> 21354 <product-form-component 21355 data-section-id="bp-card" 21356 data-product-id="15399680082086" 21357 on:submit="/handleSubmit" 21358 class="quick-add__product-form-component" 21359 > 21360 <div 21361 class="visually-hidden" 21362 aria-live="assertive" 21363 role="status" 21364 aria-atomic="true" 21365 ref="liveRegion" 21366 ></div><form method="post" action="/cart/add" id="QuickAdd-ProductForm-15399680082086-" accept-charset="UTF-8" class="shopify-product-form" enctype="multipart/form-data" novalidate="novalidate" data-type="add-to-cart-form"><input type="hidden" name="form_type" value="product" /><input type="hidden" name="utf8" value="â" /><input 21367 type="hidden" 21368 name="id" 21369 ref="variantId" 21370 value="56779665801382" 21371 21372 > 21373 <input 21374 type="hidden" 21375 name="quantity" 21376 value="1" 21377 > 21378 21379 <add-to-cart-component 21380 ref="addToCartButtonContainer" 21381 data-product-variant-media="//www.bostonproper.com/cdn/shop/files/2DP1920_SET.jpg?v=1791009211&width=100" 21382 data-add-to-cart-animation="true" 21383> 21384 <button 21385 21386 type="submit" 21387 name="add" 21388 ref="addToCartButton" 21389 on:click="/handleClick" 21390 class="button button quick-add__button quick-add__button--add add-to-cart-button" 21391 21392 21393 > 21394 <span 21395 class="add-to-cart-text" 21396 > 21397 21398 <span 21399 aria-hidden="true" 21400 class="svg-wrapper add-to-cart-icon" 21401 ><svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="none"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="var(--icon-stroke-width)" d="M16.608 9.421V6.906H3.392v8.016c0 .567.224 1.112.624 1.513.4.402.941.627 1.506.627H8.63M8.818 3h2.333c.618 0 1.212.247 1.649.686a2.35 2.35 0 0 1 .683 1.658v1.562H6.486V5.344c0-.622.246-1.218.683-1.658A2.33 2.33 0 0 1 8.82 3"/><path stroke="currentColor" stroke-linecap="round" stroke-width="var(--icon-stroke-width)" d="M14.608 12.563v5m2.5-2.5h-5"/></svg> 21402</span> 21403
21404 <span class="add-to-cart-text__content is-visually-hidden-mobile"> 21405 <span> 21406 <span>Add</span> 21407 </span> 21408 </span> 21409 </span> 21410 21411 <span class="add-to-cart__added"> 21412 <span class="svg-wrapper add-to-cart__added-icon"><svg aria-hidden="true" class="checkmark-burst" viewBox="0 0 24 24" fill="none" xmlns="http://www.w3.org/2000/svg"> 21413 <g class="check"> 21414 <circle class="ring" cx="12" cy="12" r="10" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> 21415 <path class="tick" d="M9 12.75L11.25 15L15 9.75" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> 21416 </g> 21417 <g class="burst"> 21418 <g style="--index: 0;"> 21419 <line class="line" stroke-linecap="round" pathLength="1" x1="12" y1="8.5" x2="12" y2="15.5" stroke="currentColor"/> 21420 </g> 21421 <g style="--index: 1;"> 21422 <line class="line" stroke-linecap="round" pathLength="1" x1="12" y1="8.5" x2="12" y2="15.5" stroke="currentColor"/> 21423 </g> 21424 <g style="--index: 2;"> 21425 <line class="line" stroke-linecap="round" pathLength="1" x1="12" y1="8.5" x2="12" y2="15.5" stroke="currentColor"/> 21426 </g> 21427 <g style="--index: 3;"> 21428 <line class="line" stroke-linecap="round" pathLength="1" x1="12" y1="8.5" x2="12" y2="15.5" stroke="currentColor"/> 21429 </g> 21430 <g style="--index: 4;"> 21431 <line class="line" stroke-linecap="round" pathLength="1" x1="12" y1="8.5" x2="12" y2="15.5" stroke="currentColor"/> 21432 </g> 21433 <g style="--index: 5;"> 21434 <line class="line" stroke-linecap="round" pathLength="1" x1="12" y1="8.5" x2="12" y2="15.5" stroke="currentColor"/> 21435 </g> 21436 <g style="--index: 6;"> 21437 <line class="line" stroke-linecap="round" pathLength="1" x1="12" y1="8.5" x2="12" y2="15.5" stroke="currentColor"/> 21438 </g> 21439 <g style="--index: 7;"> 21440 <line class="line" stroke-linecap="round" pathLength="1" x1="12" y1="8.5" x2="12" y2="15.5" stroke="currentColor"/> 21441 </g> 21442 </g> 21443</svg> 21444</span> 21445 </span> 21446 </button> 21447</add-to-cart-component> 21448 21449 21450 <button 21451 class="button quick-add__button quick-add__button--choose add-to-cart-button" 21452 21453 type="button" 21454 name="add" 21455 on:click="quick-add-component/handleClick" 21456 > 21457 <span 21458 class="add-to-cart-text" 21459 > 21460 21461 <span 21462 aria-hidden="true" 21463 class="svg-wrapper add-to-cart-icon" 21464 ><svg aria-hidden="true" xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20" fill="none"><path stroke="currentColor" stroke-linecap="round" stroke-linejoin="round" stroke-width="var(--icon-stroke-width)" d="M16.608 9.421V6.906H3.392v8.016c0 .567.224 1.112.624 1.513.4.402.941.627 1.506.627H8.63M8.818 3h2.333c.618 0 1.212.247 1.649.686a2.35 2.35 0 0 1 .683 1.658v1.562H6.486V5.344c0-.622.246-1.218.683-1.658A2.33 2.33 0 0 1 8.82 3"/><path stroke="currentColor" stroke-linecap="round" stroke-width="var(--icon-stroke-width)" d="M14.608 12.563v5m2.5-2.5h-5"/></svg> 21465</span> 21466 <span class="add-to-cart-text__content is-visually-hidden-mobile"> 21467 <span> 21468 21469 <span> 21470 Choose 21471 </span> 21472 </span> 21473 </span> 21474 </span> 21475 </button><input type="hidden" name="product-id" value="15399680082086" /></form></product-form-component> 21476</quick-add-component> 21477 21478 21479 21480</div></div> 21481 21482 <div class="bp-card__body"><h3 class="bp-card__title"> 21483 <a href="/products/ultimate-velvet-double-breasted-shaped-blazer-black">Ultimate Velvet Double Breasted Shaped Blazer</a> 21484 </h3> 21485 21486 <div class="bp-card__price-row"><span class="bp-card__price">$150</span> 21487 <s class="bp-card__compare" hidden></s></div></div> 21488 </article> 21489</product-card> 21490 21491 </li> 21492 21493 </ul> 21494 21495</div> 21496 21497 21498 21499 </div> 21500 21501</div> 21502</div> 21503 21504 <div class="predictive-search-form__footer"> 21505 <button 21506 class="button predictive-search__search-button" 21507 ref="viewAllButton" 21508 > 21509 View all 21510 </button> 21511 </div> 21512 </div> 21513 </form> 21514 </predictive-search-component> 21515 </dialog> 21516</dialog-component> 21517 21518 21519 21520<bp-search-drawer 21521 class="bp-sd" 21522 aria-hidden="true" 21523 inert 21524 data-search-url="/search" 21525 data-br-enabled="1" 21526> 21527 <div class="bp-sd__panel"> 21528 <form class="bp-sd__form" action="/search" method="get"> 21529 <input type="hidden" name="options[prefix]" value="last"> 21530 21531 <div class="bp-sd__header">
21532 <span class="bp-sd__icon svg-wrapper"><svg width="44" height="44" viewBox="0 0 44 44" fill="none" xmlns="http://www.w3.org/2000/svg"> 21533<path d="M30.6967 19.7055C30.7183 23.8405 27.932 27.3926 24.0949 28.7508C17.6703 31.1232 10.0267 26.8687 10 19.7055C10.0272 12.5414 17.6703 8.28779 24.0953 10.6602C27.932 12.0184 30.7187 15.5704 30.6967 19.7055ZM28.5408 19.7055C28.5477 16.9739 27.3344 14.2088 25.1509 12.5164C22.4003 10.326 18.2959 10.3256 15.5454 12.5164C13.3619 14.2088 12.1486 16.9735 12.1555 19.7055C12.1486 22.437 13.3619 25.2022 15.5454 26.8946C18.2959 29.085 22.4003 29.0854 25.1509 26.8946C27.3344 25.2022 28.5477 22.4374 28.5408 19.7055Z" fill="currentColor"/> 21534<path d="M33.1718 33.9998C31.5945 32.7679 30.2255 31.2984 28.9126 29.7966C28.4814 29.2934 28.0606 28.7816 27.6699 28.2439L28.9794 27.1216C29.4511 27.5898 29.8927 28.084 30.3238 28.5872C31.3971 29.854 32.4207 31.1661 33.3132 32.5696C33.4917 32.8528 33.6659 33.1396 33.8263 33.4388L33.1714 34.0002L33.1718 33.9998Z" fill="currentColor"/> 21535</svg> 21536</span> 21537 <input 21538 type="search" 21539 name="q" 21540 id="bp-sd-input" 21541 class="bp-sd__input" 21542 placeholder="Search" 21543 autocomplete="off" 21544 aria-label="Search" 21545 role="combobox" 21546 aria-expanded="false" 21547 aria-controls="bp-sp-products" 21548 aria-autocomplete="list" 21549 > 21550 <button type="button" class="bp-sd__clear" hidden>Clear</button> 21551 </div><p class="visually-hidden" role="status" aria-live="polite" data-bp-search-status></p><div class="bp-sp"> 21552 <div class="bp-sp__body"> 21553 21554 <aside class="bp-sp__sidebar" data-br-sidebar> 21555 <div class="bp-sp__group"> 21556 <h3 class="bp-sp__group-title">Suggestions</h3><ul class="bp-sp__links" role="list"> 21557 <li><a class="bp-sp__link" href="/search?q=dresses" data-bp-search="dresses">dresses</a></li> 21558 <li><a class="bp-sp__link" href="/search?q=swimwear" data-bp-search="swimwear">swimwear</a></li> 21559 <li><a class="bp-sp__link" href="/search?q=tops" data-bp-search="tops">tops</a></li> 21560 <li><a class="bp-sp__link" href="/search?q=summer+dresses" data-bp-search="summer dresses">summer dresses</a></li> 21561 </ul> 21562 </div> 21563 21564 <div class="bp-sp__group"> 21565 <h3 class="bp-sp__group-title">Category</h3> 21566 <ul class="bp-sp__links" role="list"> 21567 <li><a class="bp-sp__link" href="/collections/dresses">Dresses</a></li> 21568 <li><a class="bp-sp__link" href="/collections/swimwear">Swimwear</a></li> 21569 <li><a class="bp-sp__link" href="/collections/tops">Tops</a></li> 21570 <li><a class="bp-sp__link" href="/collections/sale">Sale</a></li> 21571 <li><a class="bp-sp__link" href="/collections/new-arrivals">New Arrivals</a></li> 21572 </ul> 21573 </div> 21574 </aside> 21575 21576 <div class="bp-sp__products" id="bp-sp-products" data-br-products data-bp-track-list-name="Search Results" hidden></div> 21577 21578 </div> 21579</div> 21580</form> 21581 </div> 21582</bp-search-drawer> 21583 21584 21585 21586 21587 21588 21589 21590 <div id="shopify-section-bp-quick-drawer" class="shopify-section"> 21591 21592 21593 21594
21595<script 21596 src="//www.bostonproper.com/cdn/shop/t/1521/assets/gift-card-recipient-form.js?v=129634859299628245901790713714" 21597 type="module" 21598 fetchpriority="low" 21599></script>
21599 21600 21601<quick-add-dialog id="quick-add-dialog"> 21602 <dialog 21603 class="quick-add-modal bp-qd dialog-modal color-scheme-1" 21604 ref="dialog" 21605 scroll-lock 21606 aria-label="Quick view" 21607 > 21608 <button 21609 ref="closeButton" 21610 on:click="/closeDialog" 21611 class="button button-unstyled close-button quick-add-modal__close bp-qd__close" 21612 aria-label="Close dialog" 21613 ><svg aria-hidden="true" width="14" height="14" viewBox="0 0 14 14" fill="none" xmlns="http://www.w3.org/2000/svg"> 21614 <path d="M12 2L2 12" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 21615 <path d="M12 12L2 2" stroke="currentColor" stroke-width="var(--icon-stroke-width)" stroke-linecap="round" stroke-linejoin="round"/> 21616</svg> 21617</button> 21618 21619 <div class="bp-qd__layout"> 21620 <aside class="bp-qd__media-slot" data-bp-qd-media-slot></aside> 21621 21622 <div class="bp-qd__main"> 21623 <div 21624 id="quick-add-modal-content" 21625 class="quick-add-modal__content bp-qd__body" 21626 ></div> 21627 21628 <footer class="bp-qd__cta-slot" data-bp-qd-cta-slot><p class="bp-qd__pay-in-4" data-bp-qd-pay-in-4> 21629 Pay in full or in 4 interest-free installments with 21630 <img class="bp-qd__shop-pay-mark" src="//www.bostonproper.com/cdn/shop/t/1521/assets/bp-shop-pay-logo.svg?v=68994295948588378181790713714" alt="Shop Pay" width="42" height="10" loading="lazy" style="width:auto;height:12px;"> 21631 </p> 21632 </footer> 21633 </div> 21634 </div> 21635 </dialog> 21636</quick-add-dialog> 21637 21638 21639 21640 21641 21642 21643</div> 21644 21645 21646
21647<script> 21648(function () { 21649 var SERVER_COLOR = "With Love BP"; 21650 // Product scopes: main PDP + quick-add / quick-view drawer, so a title's 21651 // colour comes from ITS OWN product's label, not another on the page. 21652 var SCOPE_SEL = '.product-details, .product-information, #quick-add-modal-content, .quick-add-modal, .quick-add-modal__content, .bp-qd__body, dialog[open]'; 21653 function colorFor(el) { 21654 var scope = el.closest(SCOPE_SEL) || document; 21655 // Use the SELECTED colour, not the label text â bp-color-siblings mutates 21656 // [data-bp-active-color] on swatch HOVER (preview), which would make us 21657 // strip the wrong colour and flash the full title back. The active swatch 21658 // only changes on real selection / in-place swap, so it's stable. 21659 var active = scope.querySelector( 21660 '.bp-colors__item.is-active[data-bp-color-name], .bp-colors__item--active[data-bp-color-name], .bp-colors__item[aria-current][data-bp-color-name]' 21661 ); 21662 var sel = active && active.getAttribute('data-bp-color-name'); 21663 if (sel && sel.trim()) return sel.trim(); 21664 return SERVER_COLOR || ''; 21665 } 21666 function strip(title, color) { 21667 if (!title || !color) return title; 21668 var c = String(color).replace(/[.*+?^${}()|[\]\\]/g, '\\$&'); 21669 var re = new RegExp('\\s*[-\\u2013\\u2014]?\\s*' + c + '\\s*$', 'i'); 21670 var out = String(title).replace(re, '').trim(); 21671 return out || title; 21672 } 21673 function titleEls() { 21674 var sels = [ 21675 '.product-information h1', 21676 '.product-details h1', 21677 '[id^="ProductInformation"] h1', 21678 '.view-product-title a', 21679 '[ref="productTitleLink"] [role="heading"]', 21680 // Quick-add / quick-view drawer main title. The drawer's controller 21681 // (sections/bp-quick-drawer.liquid wrapTitleLink) wraps the morphed 21682 // <h1> text-block in an <a href="{pdp}">, so the colour lives in 21683 // "<h1 data-bp-qd-placed><a>Everyday Hoop Gold</a></h1>". apply() skips 21684 // container elements (the h1 now has an <a> child), so target the inner 21685 // <a> leaf directly. Also match the pre-wrap <h1> (text leaf, before the 21686 // controller runs) and the un-relocated node still in #quick-add-modal- 21687 // content, so whichever timing wins gets stripped. 21688 '.bp-qd__body h1 a', 21689 '.bp-qd__body h1', 21690 '#quick-add-modal-content h1 a', 21691 '#quick-add-modal-content h1' 21692 ]; 21693 var seen = [], out = []; 21694 sels.forEach(function (s) { 21695 document.querySelectorAll(s).forEach(function (el) { 21696 if (seen.indexOf(el) < 0) { seen.push(el); out.push(el); } 21697 }); 21698 }); 21699 return out; 21700 } 21701 function apply() { 21702 titleEls().forEach(function (t) { 21703 if (t.children && t.children.length > 0) return; // text-only leaves 21704 var color = colorFor(t); 21705 if (!color) return; 21706 if (t.getAttribute('data-bp-full-title') == null) { 21707 t.setAttribute('data-bp-full-title', (t.textContent || '').trim()); 21708 } 21709 var full = t.getAttribute('data-bp-full-title') || (t.textContent || '').trim(); 21710 var clean = strip(full, color); 21711 if (clean && clean !== (t.textContent || '').trim()) t.textContent = clean; 21712 }); 21713 } 21714 // Reveal the PDP title once we've had a pass at stripping it. bp-fonts.liquid 21715 // hides '.product-information h1 / .product-details h1' until this attribute 21716 // is set, so the full "â¦Colour" title never paints before it's cleaned. 21717 // Always reveal (even if there was no colour to strip) so a plain title is 21718 // never left hidden; the CSS also has a 1.2s fail-safe reveal. 21719 function revealTitles() { 21720 document 21721 .querySelectorAll('.product-information h1, .product-details h1') 21722 .forEach(function (h) { h.setAttribute('data-bp-title-ready', ''); }); 21723 } 21724 function boot() { 21725 apply(); 21726 revealTitles(); 21727 var tries = 0; 21728 var iv = setInterval(function () { apply(); if (++tries > 24) clearInterval(iv); }, 300); 21729 if (document.body) { 21730 var obs = new MutationObserver(function () { apply(); }); 21731 obs.observe(document.body, { childList: true, subtree: true, characterData: true }); 21732 setTimeout(function () { obs.disconnect(); }, 10000); 21733 } 21734 } 21735 if (document.readyState !== 'loading') boot(); 21736 else document.addEventListener('DOMContentLoaded', boot); 21737 // In-place colour swap / quick-view open re-render titles + labels. 21738 document.addEventListener('bp:pdp:swapped', boot); 21739 document.addEventListener('click', function (e) { 21740 if (e.target.closest && (e.target.closest('[data-bp-swatch]') || e.target.closest('[data-bp-card-qv-trigger]') || e.target.closest('.quick-add__button'))) 21741 setTimeout(boot, 120); 21742 }, true); 21743})(); 21744</script>
21744 21745 21746 21747 <dialog 21748 class="bp-size-chart" 21749 id="bp-size-chart-drawer" 21750 data-bp-size-chart-drawer 21751 aria-labelledby="bp-size-chart-title" 21752 > 21753 <div class="bp-size-chart__inner"> 21754 <header class="bp-size-chart__head"> 21755 <h2 id="bp-size-chart-title" class="bp-size-chart__title">Size Chart</h2> 21756 <button 21757 type="button" 21758 class="bp-size-chart__close" 21759 data-bp-size-chart-close 21760 aria-label="Close size chart" 21761 > 21762 <svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"> 21763 <line x1="18" y1="6" x2="6" y2="18"/> 21764 <line x1="6" y1="6" x2="18" y2="18"/> 21765 </svg> 21766 </button> 21767 </header> 21768 21769 <div class="bp-size-chart__tabs" role="tablist" aria-label="Size guide tabs"> 21770 <button 21771 type="button" 21772 class="bp-size-chart__tab is-active" 21773 role="tab" 21774 aria-selected="true" 21775 aria-controls="bp-size-chart-pane-chart" 21776 data-bp-size-chart-tab="chart" 21777 >Size Chart</button> 21778 <button 21779 type="button" 21780 class="bp-size-chart__tab" 21781 role="tab" 21782 aria-selected="false" 21783 aria-controls="bp-size-chart-pane-htm" 21784 data-bp-size-chart-tab="htm" 21785 >How to Measure</button> 21786 </div> 21787 21788 <div class="bp-size-chart__body"> 21789 <section 21790 id="bp-size-chart-pane-chart" 21791 class="bp-size-chart__pane is-active" 21792 role="tabpanel" 21793 data-bp-size-chart-pane="chart" 21794 ><div style="margin: 10px 0 10px;" class="flex-measuring-text size-measuring-section modal-sizechart"> 21795<p>We take the guesswork out of choosing the right size. If you are unsure of exactly which size to order, there are a few steps you can follow to help ensure the best fit.</p> 21796<ul> 21797<li>When selecting your size, it is important to note our Boston Proper sizes may differ in the sizes you wear from other brands. Use our size ranges to help determine which sizing will work best for you based on the numeric equivalents provided here and on product pages. Specifically: XXS(0), XS(2-4), S(6-8), M(10-12), L(14-16), XL(18).</li> 21798<li>We also note when an item may run small/big or if you are between sizes, with direction to order a smaller or larger size.</li> 21799<li>We offer <strong>True Fit</strong>Â to assist with size selection by using your measurements, shape, and what you wear in other brands to find the right Boston Proper size for you. Simply click on <u>What's My Size?</u> next to the True Fit icon below the select size options on any product page or visit the <a href="https://www.bostonproper.com/pages/true-fit" title="True Fit">True Fit</a> page.</li> 21800</ul> 21801<p>And just in case you need a different size, exchanges are always free.</p> 21802<p>For additional sizing information, please reference the charts below or contact our Customer Care team with any questions you may have.</p> 21803</div> 21804<div style="margin-top: 10px;" class="customerservice--sizecharts"> 21805<div class="modal-sizechart customerservice--sizecharts"> 21806<div class="flex-chart"> 21807<h3>Measurements</h3> 21808<table class="table table-bordered table-size-chart"> 21809<thead> 21810<tr> 21811<th colspan="2">US SIZE</th> 21812<th>BUST</th> 21813<th>WAIST</th> 21814<th>HIPS</th> 21815</tr> 21816</thead> 21817<tbody> 21818<tr> 21819<td class="sizechart-col1">XXS</td> 21820<td class="sizechart-colX">0</td> 21821<td class="sizechart-colX">32.5"</td> 21822<td class="sizechart-colX">26"</td> 21823<td class="sizechart-colX">35.5"</td> 21824</tr> 21825<tr> 21826<td class="sizechart-col1">XS</td> 21827<td class="sizechart-colX">2</td> 21828<td class="sizechart-colX">33.5"</td> 21829<td class="sizechart-colX">27"</td> 21830<td class="sizechart-colX">36.5"</td> 21831</tr> 21832<tr> 21833<td class="sizechart-col1">XS</td> 21834<td class="sizechart-colX">4</td> 21835<td class="sizechart-colX">34.5"</td> 21836<td class="sizechart-colX">28"</td> 21837<td class="sizechart-colX">37.5"</td> 21838</tr> 21839<tr> 21840<td class="sizechart-col1">S</td> 21841<td class="sizechart-colX">6</td> 21842<td class="sizechart-colX">35.5"</td> 21843<td class="sizechart-colX">29"</td> 21844<td class="sizechart-colX">38.5"</td> 21845</tr> 21846<tr> 21847<td class="sizechart-col1">S</td> 21848<td class="sizechart-colX">8</td> 21849<td class="sizechart-colX">36.5"</td> 21850<td class="sizechart-colX">30"</td> 21851<td class="sizechart-colX">39.5"</td> 21852</tr> 21853<tr> 21854<td class="sizechart-col1">M</td> 21855<td class="sizechart-colX">10</td> 21856<td class="sizechart-colX">37.5"</td> 21857<td class="sizechart-colX">31"</td> 21858<td class="sizechart-colX">40.5"</td> 21859</tr> 21860<tr> 21861<td class="sizechart-col1">M</td> 21862<td class="sizechart-colX">12</td> 21863<td class="sizechart-colX">39"</td> 21864<td class="sizechart-colX">32.5"</td> 21865<td class="sizechart-colX">42"</td> 21866</tr> 21867<tr> 21868<td class="sizechart-col1">L</td> 21869<td class="sizechart-colX">14</td> 21870<td class="sizechart-colX">40.5"</td> 21871<td class="sizechart-colX">34"</td> 21872<td class="sizechart-colX">43.5"</td> 21873</tr> 21874<tr> 21875<td class="sizechart-col1">L</td> 21876<td class="sizechart-colX">16</td> 21877<td class="sizechart-colX">42"</td> 21878<td class="sizechart-colX">35.5"</td> 21879<td class="sizechart-colX">45"</td> 21880</tr> 21881<tr> 21882<td class="sizechart-col1">XL</td> 21883<td class="sizechart-colX">18</td> 21884<td class="sizechart-colX">44"</td> 21885<td class="sizechart-colX">37.5"</td> 21886<td class="sizechart-colX">47"</td> 21887</tr> 21888</tbody> 21889</table> 21890</div> 21891<div class="flex-chart"> 21892<h3>Dress & Skirt Lengths</h3> 21893<table class="table table-bordered table-size-chart"> 21894<thead> 21895<tr> 21896<th>DESCRIPTION</th> 21897<th>DRESS</th> 21898<th>SKIRT</th> 21899</tr> 21900</thead> 21901<tbody> 21902<tr> 21903<td class="sizechart-colX">Mid thigh</td> 21904<td class="sizechart-colX">35"</td> 21905<td class="sizechart-colX">18"</td> 21906</tr> 21907<tr> 21908<td class="sizechart-colX">Above the knee</td> 21909<td class="sizechart-colX">37"</td> 21910<td class="sizechart-colX">20"</td> 21911</tr> 21912<tr> 21913<td class="sizechart-colX">At the knee</td> 21914<td class="sizechart-colX">38" -39"</td> 21915<td class="sizechart-colX">22"</td> 21916</tr> 21917<tr> 21918<td class="sizechart-colX">Below the knee</td> 21919<td class="sizechart-colX">40" - 41"</td> 21920<td class="sizechart-colX">23" - 24"</td> 21921</tr> 21922<tr> 21923<td class="sizechart-colX">Midi</td> 21924<td class="sizechart-colX">44" - 45"</td> 21925<td class="sizechart-colX">28"</td> 21926</tr> 21927<tr> 21928<td class="sizechart-colX">Ankle</td> 21929<td class="sizechart-colX">54" - 55"</td> 21930<td class="sizechart-colX">38"</td> 21931</tr> 21932<tr> 21933<td class="sizechart-colX">Maxi</td> 21934<td class="sizechart-colX">57" - 58"</td> 21935<td class="sizechart-colX">40"</td> 21936</tr> 21937</tbody> 21938</table> 21939</div> 21940</div> 21941<div class="modal-measuring" style="display: none;"> 21942<div> 21943<div style="margin-top: 0px;" class="flex-measuring-text"> 21944<h4>Measuring For The Perfect Size</h4> 21945<p class="flex-measuring-text-center">Wearing appropriate undergarments, place the measuring tape comfortably around the bust, waist and hips. To determine the most accurate measurements, ensure that the tape is placed evenly around and parallel to the floor.</p> 21946</div> 21947<div class="flex-measuring"> 21948<div class="customerservice--sizecharts-model"><img src="https://media.bostonproper.com/i/bostonproper/silhouette_v2-01"></div> 21949<div> 21950<ul> 21951<li> 21952<strong>Bust:</strong> Measure around the fullest part of the bust, arms relaxed at your side.</li> 21953<li> 21954<strong>Waist:</strong> Measure around waist smallest point with tape slightly loosened for c
21954omfort.</li> 21955<li> 21956<strong>Hips:</strong> With feet together, measure around the fullest part of hip and rear, usually 7" - 9" below the waist.</li> 21957<li> 21958<strong>Inseam:</strong> Measure a similar style of pants that fit well. Measure from the crotch seam to the hem.</li> 21959<li> 21960<strong>Length:</strong> Lengths are measured at the front, starting from where the shoulder meets the neck.</li> 21961</ul> 21962</div> 21963</div> 21964</div> 21965</div> 21966</div> 21967<div> 21968<div> 21969<p>For additional information, please click on full sizing details below or contact the Customer Care team with any questions you may have.</p> 21970</div> 21971<div class="richtext-cta-container richtext-cta-container--small"> 21972<div class="richtext-cta-container"> 21973<a class="button button--secondary richtext-cta__button" href="/pages/size-chart" style="margin-right: 20px;"><em class="blog-title"><b>FULL SIZING DETAILS</b></em></a> <a class="button button--secondary richtext-cta__button" href="/pages/contact-us"><em class="blog-title"><b>CONTACT CUSTOMER CARE</b></em></a> 21974</div> 21975</div> 21976</div> 21977</section> 21978 21979 <section 21980 id="bp-size-chart-pane-htm" 21981 class="bp-size-chart__pane bp-size-chart__pane--htm" 21982 role="tabpanel" 21983 data-bp-size-chart-pane="htm" 21984 hidden 21985 ><figure class="bp-size-chart__measure-fig"> 21986 <img class="bp-size-chart__measure-img" 21987 src="//www.bostonproper.com/cdn/shop/t/1521/assets/bp-size-chart-measure.png?v=70142022341375340491790713714" 21988 alt="Measurement diagram showing bust, waist, hips, and inseam points on a model"> 21989 </figure><h3>Measuring for the Perfect Size</h3> 21990<p>Wearing a bra and panties, place the measuring tape comfortably around the bust, waist and hips. To determine the most accurate measurements, ensure that the tape is placed evenly around and parallel to the floor.</p> 21991<ul> 21992<li> 21993<strong>Bust:</strong> Measure around the fullest part of the bust, arms relaxed at your side.</li> 21994<li> 21995<strong>Waist:</strong> Measure around waist smallest point with tape slightly loosened for comfort.</li> 21996<li> 21997<strong>Hips:</strong> With feet together, measure around the fullest part of hip and rear, usually 7" - 9" below the waist.</li> 21998<li> 21999<strong>Inseam:</strong> Measure a similar style of pants that fit well. Measure from the crotch seam to the hem.</li> 22000<li> 22001<strong>Length:</strong> Lengths are measured at the front, starting from where the shoulder meets the neck.</li> 22002</ul> 22003<p><a class="btn" href="/pages/size-chart">Full Sizing Details</a></p> 22004<p><a class="btn" href="/pages/contact-us">Contact Customer Care</a></p> 22005</section> 22006 </div> 22007 </div> 22008 </dialog> 22009 22010
22010<script> 22011 (function () { 22012 const dialog = document.getElementById('bp-size-chart-drawer'); 22013 if (!dialog) return; 22014 22015 const openDialog = () => { 22016 try { 22017 if (typeof dialog.showModal === 'function') dialog.showModal(); 22018 else dialog.setAttribute('open', ''); 22019 } catch (_) { 22020 dialog.setAttribute('open', ''); 22021 } 22022 document.body.classList.add('bp-size-chart-open'); 22023 }; 22024 const closeDialog = () => { 22025 try { if (typeof dialog.close === 'function') dialog.close(); else dialog.removeAttribute('open'); } 22026 catch (_) { dialog.removeAttribute('open'); } 22027 document.body.classList.remove('bp-size-chart-open'); 22028 }; 22029 22030 document.addEventListener('click', (e) => { 22031 const t = e.target.closest('[data-bp-size-chart-open]'); 22032 if (!t) return; 22033 e.preventDefault(); 22034 openDialog(); 22035 }); 22036 22037 dialog.addEventListener('click', (e) => { 22038 if (e.target.closest('[data-bp-size-chart-close]')) { closeDialog(); return; } 22039 const tab = e.target.closest('[data-bp-size-chart-tab]'); 22040 if (tab) { 22041 const key = tab.getAttribute('data-bp-size-chart-tab'); 22042 dialog.querySelectorAll('[data-bp-size-chart-tab]').forEach((t) => { 22043 const active = t === tab; 22044 t.classList.toggle('is-active', active); 22045 t.setAttribute('aria-selected', active ? 'true' : 'false'); 22046 }); 22047 dialog.querySelectorAll('[data-bp-size-chart-pane]').forEach((p) => { 22048 const match = p.getAttribute('data-bp-size-chart-pane') === key; 22049 p.classList.toggle('is-active', match); 22050 if (match) p.removeAttribute('hidden'); 22051 else p.setAttribute('hidden', ''); 22052 }); 22053 return; 22054 } 22055 if (e.target === dialog) closeDialog(); 22056 }); 22057 dialog.addEventListener('close', () => { 22058 document.body.classList.remove('bp-size-chart-open'); 22059 }); 22060 })(); 22061 </script>
22061 22062 22063 <style> 22064 dialog.bp-size-chart { 22065 margin: 0; 22066 padding: 0; 22067 border: 0; 22068 max-width: none; 22069 max-height: none; 22070 width: min(640px, 100vw); 22071 height: 100dvh; 22072 inset: 0 0 0 auto; 22073 background: var(--bp-color-cta-text, #FDFFFA); 22074 color: var(--bp-color-text, #171609); 22075 box-shadow: -16px 0 48px rgba(0, 0, 0, 0.12); 22076 overflow: hidden; 22077 transform: translateX(100%); 22078 transition: transform 0.28s cubic-bezier(0.4, 0, 0.2, 1); 22079 } 22080 dialog.bp-size-chart[open] { transform: translateX(0); } 22081 dialog.bp-size-chart::backdrop { 22082 background: rgba(0, 0, 0, 0.32); 22083 animation: bp-size-chart-fade 0.28s ease forwards; 22084 } 22085 @keyframes bp-size-chart-fade { from { opacity: 0; } to { opacity: 1; } } 22086 body.bp-size-chart-open { overflow: hidden; } 22087 22088 .bp-size-chart__inner { 22089 display: flex; 22090 flex-direction: column; 22091 height: 100%; 22092 width: 100%; 22093 } 22094 .bp-size-chart__head { 22095 flex: 0 0 auto; 22096 display: flex; 22097 align-items: center; 22098 justify-content: space-between; 22099 gap: 16px; 22100 padding: 20px 24px; 22101 border-bottom: 1px solid rgba(23, 22, 9, 0.08); 22102 } 22103 @media (max-width: 767px) { 22104 .bp-size-chart__head { padding: 18px 20px; } 22105 .bp-size-chart__title { font-size: 22px; } 22106 } 22107 .bp-size-chart__title { 22108 margin: 0; 22109 color: inherit; 22110 font-family: var(--bp-font-display); 22111 font-size: 28px; 22112 font-weight: 300; 22113 line-height: 120%; 22114 letter-spacing: -0.56px; 22115 } 22116 .bp-size-chart__close { 22117 background: transparent; 22118 border: 0; 22119 padding: 8px; 22120 cursor: pointer; 22121 color: inherit; 22122 line-height: 0; 22123 border-radius: 50%; 22124 } 22125 .bp-size-chart__close:hover { background: rgba(23, 22, 9, 0.06); } 22126 22127 .bp-size-chart__tabs { 22128 flex: 0 0 auto; 22129 display: flex; 22130 gap: 0; 22131 padding: 0; 22132 border-bottom: 1px solid rgba(23, 22, 9, 0.08); 22133 } 22134 .bp-size-chart__tab { 22135 flex: 1 1 0; 22136 background: transparent; 22137 border: 0; 22138 padding: 12px 8px 14px; 22139 cursor: pointer; 22140 font-family: var(--bp-font-body); 22141 font-size: 14px; 22142 font-weight: 500; 22143 line-height: 145%; 22144 letter-spacing: -0.14px; 22145 color: var(--bp-color-body-copy, #74736B); 22146 text-align: center; 22147 position: relative; 22148 transition: color 0.15s ease; 22149 } 22150 .bp-size-chart__tab.is-active { 22151 color: var(--bp-color-text, #171609); 22152 font-weight: 700; 22153 } 22154 .bp-size-chart__tab.is-active::after { 22155 content: ''; 22156 position: absolute; 22157 left: 0; 22158 right: 0; 22159 bottom: -1px; 22160 height: 1.5px; 22161 background: var(--bp-color-text, #171609); 22162 } 22163 .bp-size-chart__tab:hover:not(.is-active) { color: var(--bp-color-text, #171609); } 22164 22165 .bp-size-chart__pane[hidden] { display: none; } 22166 22167 .bp-size-chart__body { 22168 flex: 1 1 0; 22169 min-height: 0; 22170 padding: 24px; 22171 overflow-y: auto; 22172 overflow-x: hidden; 22173 font-family: var(--bp-font-body); 22174 font-size: 14px; 22175 line-height: 145%; 22176 letter-spacing: -0.14px; 22177 color: var(--bp-color-text, #171609); 22178 box-sizing: border-box; 22179 } 22180 .bp-size-chart__body *, 22181 .bp-size-chart__body *::before, 22182 .bp-size-chart__body *::after { 22183 box-sizing: border-box; 22184 max-width: 100%; 22185 } 22186 .bp-size-chart__body img { height: auto; } 22187 @media (max-width: 767px) { 22188 .bp-size-chart__body { padding: 20px; } 22189 } 22190 22191 /* Subheaders inside resolved page content. Body-font / 660 / 22192 uppercase per Figma â admins can mark these up as h2/h3/h4 22193 in the Shopify page editor and the drawer styles them 22194 consistently. */ 22195 .bp-size-chart__body h2, 22196 .bp-size-chart__body h3, 22197 .bp-size-chart__body h4 { 22198 margin: 24px 0 12px; 22199 font-family: var(--bp-font-body); 22200 font-size: 13px; 22201 font-weight: 700; 22202 line-height: 145%; 22203 letter-spacing: 0.06em; 22204 text-transform: uppercase; 22205 color: var(--bp-color-text, #171609); 22206 } 22207 .bp-size-chart__body > section > :first-child { margin-top: 0; } 22208 22209 .bp-size-chart__body p { margin: 0 0 12px; } 22210 .bp-size-chart__body ul, 22211 .bp-size-chart__body ol { 22212 margin: 8px 0 16px; 22213 padding-left: 20px; 22214 } 22215 .bp-size-chart__body li { 22216 margin: 0 0 8px; 22217 padding-left: 4px; 22218 } 22219 .bp-size-chart__body li::marker { color: var(--bp-color-text, #171609); } 22220 22221 /* Tables â !important on width so a page that ships table with 22222 inline width attribute (or wrapped in <center>) still fills 22223 the drawer. center-aligned cells match the Figma. */ 22224 .bp-size-chart__body table { 22225 width: 100% !important; 22226 max-width: 100% !important; 22227 min-width: 0 !important; 22228 border-collapse: collapse; 22229 margin: 12px 0 24px; 22230 font-family: var(--bp-font-body); 22231 display: table !important; 22232 } 22233 .bp-size-chart__body th, 22234 .bp-size-chart__body td { 22235 padding: 12px 14px; 22236 text-align: center; 22237 border-bottom: 1px solid rgba(23, 22, 9, 0.08); 22238 font-size: 14px; 22239 line-height: 145%; 22240 letter-spacing: -0.14px; 22241 color: var(--bp-color-text, #171609); 22242 } 22243 .bp-size-chart__body th { 22244 font-weight: 700; 22245 text-transform: uppercase; 22246 letter-spacing: 0.06em; 22247 font-size: 12px; 22248 background: var(--bp-color-bg-cream, #F7F6EA); 22249 border-bottom: 1px solid rgba(23, 22, 9, 0.12); 22250 } 22251 22252 /* How-to-Measure pane â figure floats left, list flows beside. 22253 Stacks on mobile. */ 22254 .bp-size-chart__pane--htm .bp-size-chart__measure-fig { 22255 margin: 0 20px 16px 0; 22256 float: left; 22257 shape-outside: margin-box; 22258 width: 42%; 22259 max-width: 220px; 22260 } 22261 .bp-size-chart__pane--htm .bp-size-chart__measure-img { 22262 display: block; 22263 width: 100%; 22264 height: auto; 22265 } 22266 .bp-size-chart__pane--htm > :first-child:not(figure) { margin-top: 0; } 22267 @media (max-width: 599px) { 22268 .bp-size-chart__pane--htm .bp-size-chart__measure-fig { 22269 float: none; 22270 width: 100%; 22271 max-width: 280px; 22272 margin: 12px auto 16px; 22273 } 22274 } 22275 22276 /* Tame the Shopify-Page admin "FULL SIZING DETAILS" + "CONTACT 22277 CUSTOMER CARE" anchors â Figma renders them as outlined 22278 buttons (white bg, dark border, dark text), full-width, 22279 stacked, hover flips to filled dark. */ 22280 .bp-size-chart__body a.btn, 22281 .bp-size-chart__body a.button, 22282 .bp-size-chart__body a[class*="button"], 22283 .bp-size-chart__body a[href*="full-sizing"], 22284 .bp-size-chart__body a[href*="customer-care"], 22285 .bp-size-chart__body a[href*="customer-service"], 22286 .bp-size-chart__body button { 22287 display: flex !important; 22288 align-items: center !important; 22289 justify-content: center !important; 22290 width: 100% !important; 22291 max-width: 100% !important; 22292 min-height: 48px !important; 22293 padding: 12px 18px !important; 22294 margin: 10px 0 0 !important; 22295 font-family: var(--bp-font-body) !important; 22296 font-size: 14px !important; 22297 font-weight: 500 !important; 22298 letter-spacing: -0.14px !important; 22299 line-height: 145% !important; 22300 text-transform: none !important; 22301 font-style: normal !important; 22302 border-radius: 0 !important; 22303 background: transparent !important; 22304 color: var(--bp-color-text, #171609) !important; 22305 border: 1px solid var(--bp-color-text, #171609) !important; 22306 text-decoration: none !important; 22307 white-space: normal !important; 22308 transition: background-color 0.15s ease, color 0.15s ease; 22309 } 22310 .bp-size-chart__body a.btn:hover, 22311 .bp-size-chart__body a.button:hover, 22312 .bp-size-chart__body a[class*="button"]:hover, 22313 .bp-size-chart__body a[href*="full-sizing"]:hover, 22314 .bp-size-chart__body a[href*="customer-care"]:hover, 22315 .bp-size-chart__body a[href*="customer-service"]:hover, 22316 .bp-size-chart__body button:hover { 22317 background: var(--bp-color-text, #171609) !important; 22318 color: var(--bp-color-cta-text, #FDFFFA) !important; 22319 } 22320 22321 @media (prefers-reduced-motion: reduce) { 22322 dialog.bp-size-chart { transition: none; } 22323 } 22324 </style> 22325 22326 <dialog 22327 class="bp-notify" 22328 id="bp-notify-drawer" 22329 data-bp-notify-drawer 22330 data-klaviyo-company="VZrTML" 22331 data-klaviyo-company-dev="WgMT43" 22332 data-klaviyo-list="WFEsDC" 22333 data-klaviyo-list-dev="VDxyUk" 22334 aria-labelledby="bp-notify-title" 22335> 22336 <form class="bp-notify__inner" data-bp-notify-form> 22337 <button 22338 type="button" 22339 class="bp-notify__close" 22340 data-bp-notify-close 22341 aria-label="Close" 22342 > 22343 <svg viewBox="0 0 24 24" width="24" height="24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"> 22344 <line x1="18" y1="6" x2="6" y2="18"/> 22345 <line x1="6" y1="6" x2="18" y2="18"/> 22346 </svg> 22347 </button><div class="bp-notify__body" data-bp-notify-state="form"> 22348 <h2 id="bp-notify-title" class="bp-notify__title">Join the Waitlist</h2> 22349 <p class="bp-notify__copy">Be first in line if your size makes a comeback.</p> 22350 22351 <label class="bp-notify__field">
22352 <span class="bp-notify__label">Email address</span> 22353 <input 22354 type="email" 22355 required 22356 autocomplete="email" 22357 data-bp-notify-email 22358 id="bp-notify-email" 22359 aria-describedby="bp-notify-error" 22360 > 22361 </label> 22362 22363 <label class="bp-notify__opt"> 22364 <input type="checkbox" data-bp-notify-optin> 22365 <span>Add me to your email list.</span> 22366 </label> 22367 22368 <button type="submit" class="bp-notify__submit" data-bp-notify-submit> 22369 Notify me when it's back 22370 </button> 22371 22372 <p class="bp-notify__legal"> 22373 By joining the waitlist, you accept our 22374 <a href="/policies/terms-of-service">Terms</a> 22375 & 22376 <a href="/policies/privacy-policy">Privacy policy</a>. 22377 </p> 22378 22379 <p class="bp-notify__error" id="bp-notify-error" data-bp-notify-error role="alert" hidden></p> 22380 </div><div class="bp-notify__body bp-notify__body--success" data-bp-notify-state="success" role="status" hidden> 22381 <h3 class="bp-notify__success-title">You're on the List</h3> 22382 <p class="bp-notify__success-copy">We'll let you know if your size comes back.<br>Until then, there's so much more to discover.</p> 22383 <button type="button" class="bp-notify__submit bp-notify__submit--success" data-bp-notify-close>Keep Shopping</button> 22384 </div> 22385 </form> 22386</dialog> 22387
22388<script> 22389 (function () { 22390 const LOG = '[bp-notify]'; 22391 const dialog = document.getElementById('bp-notify-drawer'); 22392 if (!dialog) return; 22393 22394 const form = dialog.querySelector('[data-bp-notify-form]'); 22395 const emailInput = dialog.querySelector('[data-bp-notify-email]'); 22396 const optinInput = dialog.querySelector('[data-bp-notify-optin]'); 22397 const errorEl = dialog.querySelector('[data-bp-notify-error]'); 22398 const submitBtn = dialog.querySelector('[data-bp-notify-submit]'); 22399 const formState = dialog.querySelector('[data-bp-notify-state="form"]'); 22400 const successState = dialog.querySelector('[data-bp-notify-state="success"]'); 22401 // Prod vs dev Klaviyo account is decided by the LIVE browser host, not 22402 // server-side: prod + staging share one Shopify store (identical 22403 // shop.domain), so only the URL the shopper is actually on distinguishes 22404 // them. Mirrors Atlas's isProd (src/v2/scripts/utils.js) â exact host 22405 // allowlist, top-frame host, localhost via "127". staging.bostonproper.com 22406 // is NOT prod, so it uses the dev company (WgMT43) and stops 404-ing. 22407 let bpHost = ''; 22408 try { bpHost = window.top.location.host; } catch (_) { bpHost = window.location.host; } 22409 const isProd = bpHost === 'bostonproper.com' 22410 || bpHost === 'www.bostonproper.com' 22411 || bpHost === 'bostonproper.myshopify.com' 22412 || bpHost.includes('127'); 22413 const company = isProd ? dialog.dataset.klaviyoCompany : (dialog.dataset.klaviyoCompanyDev || dialog.dataset.klaviyoCompany); 22414 const listId = isProd ? dialog.dataset.klaviyoList : (dialog.dataset.klaviyoListDev || dialog.dataset.klaviyoList); 22415 22416 let currentVariantId = null; 22417 22418 const openDialog = (variantId) => { 22419 currentVariantId = variantId || window.bpCurrentVariantId || null; 22420 console.log(LOG, 'open, variantId=', currentVariantId); 22421 resetState(); 22422 try { dialog.showModal(); } catch (_) { dialog.setAttribute('open', ''); } 22423 document.body.classList.add('bp-notify-open'); 22424 if (emailInput) setTimeout(() => emailInput.focus(), 60); 22425 }; 22426 const closeDialog = () => { 22427 try { dialog.close(); } catch (_) { dialog.removeAttribute('open'); } 22428 document.body.classList.remove('bp-notify-open'); 22429 }; 22430 const resetState = () => { 22431 if (emailInput) emailInput.value = ''; 22432 if (optinInput) optinInput.checked = false; 22433 if (errorEl) { errorEl.hidden = true; errorEl.textContent = ''; } 22434 if (formState) formState.hidden = false; 22435 if (successState) successState.hidden = true; 22436 if (submitBtn) { submitBtn.disabled = false; submitBtn.textContent = 'Notify Me'; } 22437 }; 22438 22439 const klaviyoHeaders = { 22440 accept: 'application/vnd.api+json', 22441 revision: '2025-07-15', 22442 'content-type': 'application/vnd.api+json', 22443 }; 22444 22445 const submitBackInStock = async (email, variantId) => { 22446 const url = `https://a.klaviyo.com/client/back-in-stock-subscriptions/?company_id=${company}`; 22447 const body = JSON.stringify({ 22448 data: { 22449 type: 'back-in-stock-subscription', 22450 attributes: { 22451 channels: ['EMAIL'], 22452 profile: { data: { type: 'profile', attributes: { email } } }, 22453 }, 22454 relationships: { 22455 variant: { data: { type: 'catalog-variant', id: `$shopify:::$default:::${variantId}` } }, 22456 }, 22457 }, 22458 }); 22459 return fetch(url, { method: 'POST', headers: klaviyoHeaders, body }); 22460 }; 22461 const submitNewsletter = async (email) => { 22462 const url = `https://a.klaviyo.com/client/subscriptions/?company_id=${company}`; 22463 const body = JSON.stringify({ 22464 data: { 22465 type: 'subscription', 22466 attributes: { 22467 profile: { data: { type: 'profile', attributes: { email } } }, 22468 custom_source: 'Back-in-Stock Waitlist Modal', 22469 }, 22470 relationships: { 22471 list: { data: { type: 'list', id: listId } }, 22472 }, 22473 }, 22474 }); 22475 return fetch(url, { method: 'POST', headers: klaviyoHeaders, body }); 22476 }; 22477 22478 // --- delegated open --- 22479 document.addEventListener('click', (e) => { 22480 const t = e.target.closest('[data-bp-notify-open]'); 22481 if (!t) return; 22482 e.preventDefault(); 22483 const vid = t.getAttribute('data-bp-notify-variant-id'); 22484 openDialog(vid); 22485 }); 22486 22487 // --- close --- 22488 dialog.addEventListener('click', (e) => { 22489 if (e.target.closest('[data-bp-notify-close]')) { closeDialog(); return; } 22490 if (e.target === dialog) closeDialog(); 22491 }); 22492 dialog.addEventListener('close', () => document.body.classList.remove('bp-notify-open')); 22493 22494 // --- submit --- 22495 form.addEventListener('submit', async (e) => { 22496 e.preventDefault(); 22497 const email = (emailInput.value || '').trim(); 22498 if (!email) return; 22499 if (!currentVariantId) { 22500 errorEl.textContent = 'We could not detect which variant to subscribe you to. Please re-select your size and try again.'; 22501 errorEl.hidden = false;
22502 return; 22503 } 22504 submitBtn.disabled = true; 22505 submitBtn.textContent = 'Submittingâ¦'; 22506 errorEl.hidden = true; 22507 try { 22508 const res = await submitBackInStock(email, currentVariantId); 22509 console.log(LOG, 'BIS response', res.status); 22510 if (res.status === 202 || res.ok) { 22511 if (optinInput && optinInput.checked) { 22512 try { await submitNewsletter(email); } catch (e2) { console.error(LOG, 'newsletter error', e2); } 22513 } 22514 if (formState) formState.hidden = true; 22515 if (successState) successState.hidden = false; 22516 } else { 22517 errorEl.textContent = 'Something went wrong. Please try again.'; 22518 errorEl.hidden = false; 22519 } 22520 } catch (err) { 22521 console.error(LOG, 'BIS error', err); 22522 errorEl.textContent = 'Network error. Please try again.'; 22523 errorEl.hidden = false; 22524 } finally { 22525 submitBtn.disabled = false; 22526 submitBtn.textContent = "Notify me when it's back"; 22527 } 22528 }); 22529 })(); 22530</script>
22530 22531 22532<style> 22533 /* Figma reference: centered modal (not right-side drawer), cream 22534 background, Signifier serif heading, underline-only input, dark 22535 full-width CTA, small legal copy with inline links. */ 22536 dialog.bp-notify { 22537 margin: auto; 22538 padding: 0; 22539 border: 0; 22540 width: min(560px, calc(100vw - 32px)); 22541 max-width: 560px; 22542 max-height: calc(100dvh - 32px); 22543 background: var(--bp-color-cta-text, #FDFFFA); 22544 color: var(--bp-color-text, #171609); 22545 box-shadow: 0 24px 64px rgba(0, 0, 0, 0.18); 22546 box-sizing: border-box; 22547 opacity: 0; 22548 transform: scale(0.96); 22549 transition: opacity 0.2s ease, transform 0.2s ease; 22550 } 22551 dialog.bp-notify[open] { opacity: 1; transform: scale(1); } 22552 dialog.bp-notify::backdrop { background: rgba(0, 0, 0, 0.4); } 22553 body.bp-notify-open { overflow: hidden; } 22554 22555 .bp-notify__inner { 22556 position: relative; 22557 padding: 56px 56px 40px; 22558 } 22559 @media (max-width: 749px) { 22560 .bp-notify__inner { padding: 48px 24px 32px; } 22561 } 22562 22563 .bp-notify__close { 22564 position: absolute; 22565 top: 16px; 22566 right: 16px; 22567 background: transparent; 22568 border: 0; 22569 padding: 8px; 22570 cursor: pointer; 22571 color: var(--bp-color-text); 22572 line-height: 0; 22573 border-radius: 50%; 22574 transition: background 0.15s ease; 22575 } 22576 .bp-notify__close:hover { background: rgba(23, 22, 9, 0.06); } 22577 22578 .bp-notify__body { 22579 display: flex; 22580 flex-direction: column; 22581 gap: 16px; 22582 font-family: var(--bp-font-body); 22583 font-size: 14px; 22584 line-height: 145%; 22585 text-align: center; 22586 } 22587 /* `.bp-notify__body { display: flex }` has the same specificity as the UA 22588 `[hidden] { display: none }` rule but is authored later, so it won the 22589 cascade and the success state showed ALONGSIDE the form (both visible at 22590 once â QA 7/21). Re-assert display:none for the hidden state so only the 22591 active state (form, then success after submit) shows. */ 22592 .bp-notify__body[hidden] { display: none; } 22593 .bp-notify__body--success { gap: 20px; align-items: center; } 22594 22595 .bp-notify__title { 22596 margin: 0; 22597 font-family: var(--bp-font-display); 22598 font-size: 32px; 22599 font-weight: 300; 22600 line-height: 120%; 22601 letter-spacing: -0.64px; 22602 color: var(--bp-color-text); 22603 } 22604 .bp-notify__copy { 22605 margin: 0 0 8px; 22606 color: var(--bp-color-body-copy, #74736B); 22607 font-size: 14px; 22608 line-height: 145%; 22609 } 22610 22611 /* Underline-only input â matches Figma. */ 22612 .bp-notify__field { display: flex; flex-direction: column; gap: 4px; text-align: left; margin-top: 8px; } 22613 .bp-notify__label { 22614 font-size: 14px; 22615 font-weight: 500; 22616 color: var(--bp-color-text); 22617 letter-spacing: -0.14px; 22618 } 22619 .bp-notify__field input[type="email"] { 22620 width: 100%; 22621 box-sizing: border-box; 22622 border: 0; 22623 border-bottom: 1px solid rgba(23, 22, 9, 0.32); 22624 background: transparent; 22625 padding: 8px 0; 22626 font-family: var(--bp-font-body); 22627 font-size: 14px; 22628 color: var(--bp-color-text); 22629 border-radius: 0; 22630 } 22631 .bp-notify__field input[type="email"]:focus { 22632 outline: none; 22633 border-bottom-color: var(--bp-color-text); 22634 } 22635 .bp-notify__field input[type="email"]:focus-visible { 22636 outline: 2px solid var(--bp-color-text); 22637 outline-offset: 2px; 22638 } 22639 22640 .bp-notify__opt { 22641 display: inline-flex; 22642 align-items: center; 22643 gap: 10px; 22644 cursor: pointer; 22645 text-align: left; 22646 margin-top: 4px; 22647 font-size: 14px; 22648 } 22649 .bp-notify__opt input[type="checkbox"] { 22650 width: 18px; 22651 height: 18px; 22652 accent-color: var(--bp-color-text); 22653 flex: 0 0 auto; 22654 } 22655 22656 .bp-notify__submit { 22657 width: 100%; 22658 background: var(--bp-color-text, #171609); 22659 color: var(--bp-color-cta-text, #FDFFFA); 22660 border: 0; 22661 height: 52px; 22662 padding: 0 24px; 22663 font-family: var(--bp-font-body); 22664 font-size: 17px; 22665 font-weight: 500; 22666 line-height: normal; 22667 letter-spacing: -0.17px; 22668 cursor: pointer; 22669 margin-top: 8px; 22670 transition: opacity 0.2s ease; 22671 } 22672 .bp-notify__submit:hover { opacity: 0.85; } 22673 .bp-notify__submit[disabled] { opacity: 0.55; cursor: wait; } 22674 22675 /* Success-state submit is sized to content (not full-width) â the 22676 "Keep Shopping" CTA in the Figma is a centered fit-content 22677 button, not an edge-to-edge primary. */ 22678 .bp-notify__submit--success { 22679 width: auto; 22680 padding: 0 32px; 22681 margin-top: 4px; 22682 } 22683 22684 .bp-notify__legal { 22685 margin: 4px 0 0; 22686 font-size: 12px; 22687 color: var(--bp-color-body-copy, #74736B); 22688 line-height: 145%; 22689 letter-spacing: -0.12px; 22690 } 22691 .bp-notify__legal a { 22692 color: var(--bp-color-text); 22693 text-decoration: underline; 22694 text-underline-offset: 2px; 22695 } 22696 22697 .bp-notify__error {
22698 margin: 0; 22699 color: var(--bp-color-promo, #B5341B); 22700 font-size: 13px; 22701 } 22702 .bp-notify__success-title { 22703 margin: 0; 22704 font-family: var(--bp-font-display); 22705 font-size: 32px; 22706 font-weight: 300; 22707 letter-spacing: -0.64px; 22708 line-height: 120%; 22709 color: var(--bp-color-text); 22710 } 22711 .bp-notify__success-copy { 22712 margin: 0; 22713 color: var(--bp-color-body-copy, #74736B); 22714 font-size: 14px; 22715 line-height: 145%; 22716 text-align: center; 22717 } 22718</style> 22719 22720 <div 22721 class="bp-sticky-atc" 22722 data-bp-sticky-atc 22723 aria-hidden="true" 22724 inert 22725 > 22726 <div class="bp-sticky-atc__inner"> 22727 <div class="bp-sticky-atc__info"> 22728 <span class="bp-sticky-atc__title">Boston Proper Printed Scarf With Love BP</span> 22729 <span class="bp-sticky-atc__price" data-bp-sticky-price> 22730 $50.00 22731 </span> 22732 </div> 22733 <button 22734 type="button" 22735 class="bp-sticky-atc__btn" 22736 data-bp-sticky-add 22737 > 22738 Add to Bag 22739 </button> 22740 </div> 22741 </div> 22742 22743
22743<script> 22744 (function () { 22745 const bar = document.querySelector('[data-bp-sticky-atc]'); 22746 if (!bar) return; 22747 22748 // Scope lookups to the main buy box so recommendation / bundle ATCs on 22749 // the page don't get picked up. 22750 const mainScope = () => document.querySelector('.product-information') || document; 22751 // Find the page's main ATC button to watch for visibility. 22752 const findMainAtc = () => mainScope().querySelector( 22753 'button[name="add"]:not([data-bp-sticky-add]), add-to-cart-component button' 22754 ); 22755 // The Notify Me trigger (bp-notify-me-button) replaces the ATC when the 22756 // variant is sold out AND restock-eligible. 22757 const findNotify = () => mainScope().querySelector('[data-bp-notify-trigger]'); 22758 const findMainForm = () => document.querySelector('form[action*="/cart/add"]'); 22759 22760 // Painted = not display:none / 0-size (bp-notify-me hides the ATC via 22761 // display:none and shows the Notify trigger, and vice-versa). 22762 const isRendered = (el) => { 22763 if (!el) return false; 22764 const r = el.getBoundingClientRect(); 22765 return (r.width > 0 || r.height > 0) && el.offsetParent !== null; 22766 }; 22767 22768 // The primary CTA currently shown in the buy box. The sticky bar MIRRORS 22769 // it: "Notify me" for a Notify-Me variant (opens the drawer), "Add to Bag" 22770 // for an in-stock one, "Sold Out" for a disabled sold-out button. This is 22771 // why the bar no longer sits pinned with a dead "Sold Out" on Notify-Me 22772 // products â it tracks the actual visible CTA (QA: Alfred). 22773 const findPrimaryCta = () => { 22774 const notify = findNotify(); 22775 if (isRendered(notify)) return { el: notify, mode: 'notify' }; 22776 const atc = findMainAtc(); 22777 if (isRendered(atc)) return { el: atc, mode: atc.disabled ? 'soldout' : 'atc' }; 22778 return { el: null, mode: 'none' }; 22779 }; 22780 let mode = 'atc'; 22781 22782 // Publish the bar's rendered height so the desktop rule below can reserve 22783 // exactly that much space at the foot of the page (see the stylesheet). 22784 const setBarHeight = () => { 22785 document.documentElement.style.setProperty( 22786 '--bp-sticky-atc-h', `${Math.round(bar.offsetHeight)}px` 22787 ); 22788 }; 22789 // a11y (WCAG 4.1.2): while hidden the bar is only translated off-screen, so 22790 // its button stayed keyboard-focusable despite aria-hidden. `inert` removes 22791 // the whole bar from the tab order + a11y tree; showBar() lifts it. 22792 const showBar = () => { 22793 bar.classList.add('is-visible'); 22794 bar.setAttribute('aria-hidden', 'false'); 22795 bar.removeAttribute('inert'); 22796 setBarHeight(); 22797 // Desktop-only footer spacing hook (QA 7/23): keep the footer's 22798 // terms/privacy links from hiding under the fixed bar at page bottom. 22799 document.body.classList.add('bp-sticky-atc-open'); 22800 }; 22801 const hideBar = () => { 22802 bar.classList.remove('is-visible'); 22803 bar.setAttribute('aria-hidden', 'true'); 22804 bar.setAttribute('inert', ''); 22805 document.body.classList.remove('bp-sticky-atc-open'); 22806 }; 22807 22808 // (Re)attach the visibility observer to the CURRENT main ATC. A colour 22809 // swatch swap replaces the product section, so the button this watched is 22810 // destroyed and the observer would keep firing on the detached node â 22811 // leaving the bar stuck visible (QA: "sticky ATC always shows"). Called 22812 // again on bp:pdp:swapped with the freshly-swapped button. 22813 // Mirror the current primary CTA onto the sticky button (label + mode) so 22814 // it reads "Notify me" (opens the drawer) on a Notify-Me variant, "Add to 22815 // Bag" in stock, "Sold Out" when the buy button is a disabled sold-out. 22816 const refreshButton = () => { 22817 const stickyBtn = bar.querySelector('[data-bp-sticky-add]'); 22818 if (!stickyBtn) return; 22819 const cta = findPrimaryCta(); 22820 mode = cta.mode; 22821 if (mode === 'notify') { 22822 const lbl = cta.el.querySelector('[data-bp-notify-open] span, [data-bp-notify-open]'); 22823 stickyBtn.textContent = (lbl && lbl.textContent.trim()) || "Notify me when it's back"; 22824 stickyBtn.disabled = false;
22825 } else if (mode === 'soldout') { 22826 stickyBtn.textContent = 'Sold Out'; 22827 stickyBtn.disabled = true; 22828 } else if (mode === 'atc') { 22829 stickyBtn.textContent = 'Add to Bag'; 22830 stickyBtn.disabled = false; 22831 } 22832 }; 22833 22834 let io = null; 22835 const attachObserver = () => { 22836 if (!('IntersectionObserver' in window)) return; 22837 if (io) { try { io.disconnect(); } catch (_) {} io = null; } 22838 // Observe whichever CTA is currently primary (ATC or Notify Me), so the 22839 // bar hides while it's in view and appears once it scrolls away â 22840 // identical for both (QA: Alfred â the bar no longer pins itself over a 22841 // Notify-Me product). 22842 const cta = findPrimaryCta(); 22843 if (!cta.el) { hideBar(); return; } 22844 io = new IntersectionObserver((entries) => { 22845 entries.forEach((entry) => { 22846 // A display:none / 0-size CTA also reports isIntersecting=false; only 22847 // show the bar when the CTA is genuinely RENDERED and scrolled out of 22848 // view, otherwise keep it hidden. 22849 const r = entry.boundingClientRect; 22850 const rendered = !!r && (r.height > 0 || r.width > 0); 22851 if (entry.isIntersecting || !rendered) hideBar(); 22852 else showBar(); 22853 }); 22854 }, { threshold: 0 }); 22855 io.observe(cta.el); 22856 }; 22857 22858 // Re-read the buy box (which CTA is visible) + re-point the observer. 22859 const sync = () => { refreshButton(); attachObserver(); }; 22860 sync(); 22861 // bp-notify-me / pre-order settle the buy box asynchronously after load, 22862 // so re-sync then (and shortly after) to catch the final CTA. 22863 if (document.readyState === 'loading') { 22864 document.addEventListener('DOMContentLoaded', sync, { once: true }); 22865 } 22866 window.addEventListener('load', () => { sync(); setTimeout(sync, 200); }); 22867 22868 // Colour swap â re-point the observer at the new main ATC and refresh the 22869 // bar's title (the price + button label already track variant:update 22870 // below). Without this the bar stays stuck on the old colour + visible. 22871 const titleEl = bar.querySelector('.bp-sticky-atc__title'); 22872 document.addEventListener('bp:pdp:swapped', () => { 22873 try { 22874 const t = document.querySelector('.view-product-title, .product-title, .product-information h1'); 22875 if (t && titleEl) { 22876 const txt = t.textContent.replace(/\s+/g, ' ').trim(); 22877 if (txt) titleEl.textContent = txt; 22878 } 22879 } catch (_) {} 22880 sync(); 22881 }); 22882 22883 // Click the sticky button â click the main ATC (so Horizon 22884 // handles validation, variant selection, and cart drawer open). 22885 const stickyAdd = bar.querySelector('[data-bp-sticky-add]'); 22886 if (stickyAdd) { 22887 stickyAdd.addEventListener('click', (e) => { 22888 e.preventDefault(); 22889 22890 // Notify Me variant: open the back-in-stock drawer via the buy-box 22891 // trigger, mirroring the main Notify Me button. 22892 if (mode === 'notify') { 22893 const notify = findNotify(); 22894 const open = notify && notify.querySelector('[data-bp-notify-open]'); 22895 if (open) { 22896 notify.scrollIntoView({ behavior: 'smooth', block: 'center' }); 22897 open.click(); 22898 } 22899 return; 22900 } 22901 // Disabled sold-out button: nothing to do. 22902 if (mode === 'soldout') return; 22903 22904 const main = findMainAtc(); 22905 if (!main) return; 22906 22907 // Respect the page's require-size gate (blocks/bp-require-size.liquid). 22908 // That block flags the main button with `data-bp-size-block` when no 22909 // size is chosen (CSS `pointer-events: none`), and `disabled` / 22910 // `aria-disabled` when the chosen size is out of stock. A synthetic 22911 // `.click()` bypasses `pointer-events: none`, so without this guard the 22912 // sticky bar adds the first-available (smallest) size â the bug in QA 22913 // 7/23. Mirror the page Add to Bag: add nothing, and surface the same 22914 // "Please select a {size}" prompt instead. bp-require-size renders that 22915 // prompt off the required-radio `invalid` event that reportValidity() 22916 // fires (it preventDefault()s the native bubble and shows its own). 22917 const blocked = 22918 main.hasAttribute('data-bp-size-block') || 22919 main.getAttribute('aria-disabled') === 'true' || 22920 main.disabled; 22921 22922 main.scrollIntoView({ behavior: 'smooth', block: 'center' }); 22923 22924 if (blocked) { 22925 const form = findMainForm(); 22926 if (form && typeof form.reportValidity === 'function') form.reportValidity(); 22927 return; 22928 } 22929 22930 // Slight delay so scroll settles before the click. 22931 setTimeout(() => main.click(), 120); 22932 }); 22933 } 22934 22935 // Track variant changes to keep the price + button label fresh. 22936 // Horizon dispatches `variant:update` (see assets/events.js 22937 // â ThemeEvents.variantUpdate); also keep legacy event names 22938 // listening for forward-compat. 22939 const priceEl = bar.querySelector('[data-bp-sticky-price]'); 22940 const onVariant = (e) => { 22941 const v = e?.detail?.resource || e?.detail?.variant || e?.detail; 22942 if (v && priceEl && (v.price || v.priceFormatted)) { 22943 priceEl.textContent = v.priceFormatted || `$${(v.price / 100).toFixed(2)}`; 22944 } 22945 // Re-sync AFTER bp-notify-me / pre-order finish morphing the buy box, so 22946 // the bar mirrors the CURRENT visible CTA (ATC vs Notify Me) and re-points 22947 // its observer instead of watching a stale/hidden button (QA: Alfred). 22948 // The label is derived from the DOM in refreshButton, not v.available. 22949 requestAnimationFrame(sync); 22950 setTimeout(sync, 150); 22951 }; 22952 document.addEventListener('variant:update', onVariant); 22953 document.addEventListener('variant:change', onVariant); 22954 document.addEventListener('variantchange', onVariant); 22955 })(); 22956 </script>
22956 22957 22958 <style> 22959 /* Defensive: hide Horizon's stock sticky-add-to-cart bar so it 22960 never paints alongside ours. Primary disable is the hardcoded 22961 `{% if false and section.settings.enable_sticky_add_to_cart %}` 22962 in sections/product-information.liquid (the BP override there 22963 prevents the element from rendering at all). This rule is a 22964 belt-and-braces backup for the case where: 22965 - a future template or quick-view modal re-renders Horizon's 22966 bar without the JSON gate, 22967 - Horizon's own section CSS loads after this snippet and 22968 tries to override `display: block`. 22969 `html body` prefix bumps specificity past any naked 22970 `.sticky-add-to-cart__bar` rule; the multiple "off" properties 22971 (display + visibility + position + opacity + transform) make 22972 sure no single one being overridden brings the bar back. */ 22973 html body sticky-add-to-cart, 22974 html body .sticky-add-to-cart, 22975 html body .sticky-add-to-cart__bar, 22976 html body .sticky-add-to-cart[data-stuck="true"], 22977 html body .sticky-add-to-cart__bar[data-stuck="true"] { 22978 display: none !important; 22979 visibility: hidden !important; 22980 position: absolute !important; 22981 opacity: 0 !important; 22982 transform: translateY(200vh) !important; 22983 pointer-events: none !important; 22984 z-index: -1 !important; 22985 } 22986 22987 .bp-sticky-atc { 22988 position: fixed; 22989 left: 0; right: 0; bottom: 0; 22990 /* Sit above general page chrome but below dialogs (cart drawer, 22991 quick-add modal â those use top-layer / z-index 9999+). The 22992 previous z-index 60 was being overlapped by Klaviyo embeds and 22993 the chat widget on mobile, hiding the bar. */ 22994 z-index: 100; 22995 background: var(--bp-color-cta-text, #FDFFFA); 22996 box-shadow: 0 -2px 16px rgba(0, 0, 0, 0.08); 22997 transform: translateY(100%); 22998 transition: transform 0.24s cubic-bezier(0.4, 0, 0.2, 1); 22999 /* Respect iPhone notch / home-indicator inset so the bar isn't 23000 hidden under the safe area on mobile Safari. */ 23001 padding-bottom: env(safe-area-inset-bottom); 23002 } 23003 .bp-sticky-atc.is-visible { transform: translateY(0); } 23004 .bp-sticky-atc__inner { 23005 max-width: 1280px; 23006 margin: 0 auto; 23007 padding: 10px 16px; 23008 display: flex; 23009 align-items: center; 23010 justify-content: space-between; 23011 gap: 16px; 23012 } 23013 .bp-sticky-atc__info { 23014 display: flex; 23015 flex-direction: column; 23016 min-width: 0; 23017 } 23018 .bp-sticky-atc__title { 23019 font-family: var(--bp-font-body); 23020 font-size: 13px; 23021 font-weight: 700; 23022 line-height: 1.3; 23023 color: var(--bp-color-text, #171609); 23024 overflow: hidden; 23025 text-overflow: ellipsis; 23026 white-space: nowrap; 23027 } 23028 .bp-sticky-atc__price { 23029 font-family: var(--bp-font-body); 23030 font-size: 14px; 23031 font-weight: 500; 23032 color: var(--bp-color-text, #171609); 23033 } 23034 .bp-sticky-atc__btn { 23035 background: var(--bp-color-text, #171609); 23036 color: var(--bp-color-cta-text, #FDFFFA); 23037 border: 0; 23038 padding: 12px 20px; 23039 font-family: var(--bp-font-body); 23040 font-size: 13px; 23041 font-weight: 700; 23042 letter-spacing: 0.5px; 23043 text-transform: uppercase; 23044 cursor: pointer; 23045 flex: 0 0 auto; 23046 } 23047 .bp-sticky-atc__btn[disabled] { opacity: 0.55; cursor: not-allowed; } 23048 23049 /* Desktop only (QA 7/23): when the sticky bar is showing, reserve space at 23050 the very bottom of the page so the footer's Privacy / Shipping / Terms 23051 links clear the fixed bar instead of sitting under it. The live footer is 23052 sections/bp-footer-main.liquid, whose last element is .bp-footer__policy-bar 23053 (holds those links); we pad its bottom. The <bp-footer> root paints the 23054 cream background (and clips with overflow: hidden), so the reserved area 23055 fills cream â no bare strip under the bar. Reserved height tracks the 23056 bar's actual height (set in JS as --bp-sticky-atc-h) plus a little gap. 23057 Mobile is unchanged: the bar there sits below the safe-area inset and the 23058 policy bar already clears it. */ 23059 @media (min-width: 1024px) { 23060 body.bp-sticky-atc-open .bp-footer__policy-bar { 23061 padding-bottom: calc(var(--bp-sticky-atc-h, 64px) + 16px); 23062 } 23063 } 23064 @media (prefers-reduced-motion: reduce) { 23065 .bp-sticky-atc { transition: none; } 23066 } 23067 </style> 23068 <dialog class="bp-zoom" id="bp-zoom-dialog" aria-label="Zoomed image gallery"> 23069 <header class="bp-zoom__head">
23070 <span class="bp-zoom__hint" aria-hidden="true"> 23071 <svg width="18" height="18" viewBox="0 0 24 24" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> 23072 <circle cx="11" cy="11" r="7"/> 23073 <line x1="11" y1="8" x2="11" y2="14"/> 23074 <line x1="8" y1="11" x2="14" y2="11"/> 23075 </svg> 23076 <span class="bp-zoom__hint-text">Pinch to Zoom</span> 23077 </span> 23078 <button class="bp-zoom__close" type="button" aria-label="Close zoom" data-bp-zoom-close> 23079 <svg viewBox="0 0 24 24" width="22" height="22" fill="none" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round" aria-hidden="true"> 23080 <line x1="18" y1="6" x2="6" y2="18"/> 23081 <line x1="6" y1="6" x2="18" y2="18"/> 23082 </svg> 23083 </button> 23084 </header> 23085 23086 <button class="bp-zoom__arrow bp-zoom__arrow--prev" type="button" aria-label="Previous image" data-bp-zoom-prev> 23087 <svg viewBox="0 0 12 22" width="14" height="22" fill="none" aria-hidden="true"> 23088 <path d="M11 21L1 11L11 1" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> 23089 </svg> 23090 </button> 23091 <button class="bp-zoom__arrow bp-zoom__arrow--next" type="button" aria-label="Next image" data-bp-zoom-next> 23092 <svg viewBox="0 0 12 22" width="14" height="22" fill="none" aria-hidden="true"> 23093 <path d="M1 21L11 11L1 1" stroke="currentColor" stroke-width="1.5" stroke-linecap="round" stroke-linejoin="round"/> 23094 </svg> 23095 </button><div class="bp-zoom__track" data-bp-zoom-track></div> 23096 23097 <ol class="bp-zoom__dots" data-bp-zoom-dots></ol> 23098 </dialog> 23099 23100
23100<script> 23101 (function () { 23102 const dialog = document.getElementById('bp-zoom-dialog'); 23103 if (!dialog) return; 23104 23105 const track = dialog.querySelector('[data-bp-zoom-track]'); 23106 const dotsEl = dialog.querySelector('[data-bp-zoom-dots]'); 23107 const prevBtn = dialog.querySelector('[data-bp-zoom-prev]'); 23108 const nextBtn = dialog.querySelector('[data-bp-zoom-next]'); 23109 23110 /* High-res URL builder. Two CDNs in play: Shopify (?width=N) 23111 and Amplience (?w=N). Replace whichever param exists; fall 23112 back to adding the right one based on host. */ 23113 const HIRES = 2000; 23114 const toHires = (src) => { 23115 if (!src) return ''; 23116 try { 23117 const u = new URL(src, window.location.href); 23118 if (u.searchParams.has('width')) u.searchParams.set('width', HIRES); 23119 else if (u.searchParams.has('w')) u.searchParams.set('w', HIRES); 23120 else if (u.host.includes('shopify') || u.host.includes('cdn.shopify')) u.searchParams.set('width', HIRES); 23121 else u.searchParams.set('w', HIRES); 23122 return u.toString(); 23123 } catch (_) { return src; } 23124 }; 23125 23126 /* State per open: which slides exist + per-slide zoom level. */ 23127 let slidesData = []; // [{src, alt}] 23128 let activeIndex = 0; 23129 let zooms = []; // [{scale, x, y}] 23130 let suppressScrollSync = false; 23131 23132 /* ---------- DOM builders ---------- */ 23133 const buildSlide = (data, i) => { 23134 const slide = document.createElement('div'); 23135 slide.className = 'bp-zoom__slide'; 23136 slide.dataset.index = String(i); 23137 const cage = document.createElement('div'); 23138 cage.className = 'bp-zoom__cage'; 23139 const img = document.createElement('img'); 23140 img.className = 'bp-zoom__img'; 23141 img.draggable = false; 23142 img.alt = data.alt || ''; 23143 img.src = data.src; 23144 cage.appendChild(img); 23145 slide.appendChild(cage); 23146 return slide; 23147 }; 23148 const buildDots = (count) => { 23149 dotsEl.innerHTML = ''; 23150 if (count <= 1) return; 23151 for (let i = 0; i < count; i += 1) { 23152 const li = document.createElement('li'); 23153 const btn = document.createElement('button'); 23154 btn.type = 'button'; 23155 btn.className = 'bp-zoom__dot' + (i === 0 ? ' is-active' : ''); 23156 btn.setAttribute('aria-label', `Go to image ${i + 1}`); 23157 btn.addEventListener('click', () => goTo(i, { smooth: true })); 23158 li.appendChild(btn); 23159 dotsEl.appendChild(li); 23160 } 23161 }; 23162 23163 /* ---------- Slide nav (scrollLeft is the source of truth) ---------- */ 23164 const setActive = (i) => { 23165 if (i === activeIndex) return; 23166 if (zooms[activeIndex]) resetSlideZoom(activeIndex); 23167 activeIndex = i; 23168 dotsEl.querySelectorAll('.bp-zoom__dot').forEach((d, idx) => d.classList.toggle('is-active', idx === i)); 23169 updateArrows(); 23170 }; 23171 const goTo = (i, opts) => { 23172 if (i < 0 || i >= slidesData.length) return; 23173 // CRITICAL: release the zoom lock BEFORE scrolling. If the 23174 // current slide is zoomed the dialog carries `.is-zoom-active` 23175 // which sets `overflow-x: hidden` on the track â scroll is 23176 // then a no-op and the arrows appear "broken". Reset zoom on 23177 // the current slide first, which removes the class via 23178 // applyTransform's scale-aware toggle. 23179 if (zooms[activeIndex] && zooms[activeIndex].scale > 1.001) { 23180 resetSlideZoom(activeIndex); 23181 } 23182 const smooth = !!(opts && opts.smooth); 23183 suppressScrollSync = true; 23184 // Use scrollIntoView for smooth â interacts cleanly with 23185 // scroll-snap. Direct scrollLeft assignment for instant â most 23186 // reliable across browsers. Update activeIndex synchronously 23187 // so arrow disabled state + dots flip immediately, then let 23188 // the scroll catch up. 23189 setActive(i); 23190 const slide = slideEl(i); 23191 if (smooth && slide) { 23192 slide.scrollIntoView({ inline: 'start', block: 'nearest', behavior: 'smooth' }); 23193 } else { 23194 track.scrollLeft = i * track.clientWidth; 23195 } 23196 setTimeout(() => { suppressScrollSync = false; }, smooth ? 450 : 50); 23197 }; 23198 const updateArrows = () => { 23199 prevBtn.disabled = activeIndex === 0; 23200 nextBtn.disabled = activeIndex === slidesData.length - 1; 23201 }; 23202 23203 /* Track scroll observer â keeps activeIndex in sync with the 23204 browser-driven swipe/scroll, then updates the dots. */ 23205 let scrollRaf = 0; 23206 track.addEventListener('scroll', () => { 23207 if (suppressScrollSync) return; 23208 if (scrollRaf) return; 23209 scrollRaf = requestAnimationFrame(() => { 23210 scrollRaf = 0; 23211 const w = track.clientWidth; 23212 if (!w) return; 23213 const i = Math.round(track.scrollLeft / w); 23214 if (i !== activeIndex) setActive(i); 23215 }); 23216 }, { passive: true }); 23217 23218 /* ---------- Per-slide zoom + pan (touch + mouse) ---------- 23219 Each slide owns a {scale, x, y} transform applied to its 23220 .bp-zoom__cage. Gestures are tracked on the slide itself; 23221 pinch (2-finger) and pan (1-finger / mouse drag) both 23222 update the same state. */ 23223 const TAP_SCALE = 2.5; 23224 const MIN = 1; 23225 const MAX = 4; 23226 23227 const slideEl = (i) => track.querySelectorAll('.bp-zoom__slide')[i]; 23228 const cageEl = (i) => track.querySelectorAll('.bp-zoom__cage')[i]; 23229 23230 const resetSlideZoom = (i) => { 23231 const s = zooms[i]; 23232 if (!s) return; 23233 s.scale = 1; s.x = 0; s.y = 0; 23234 applyTransform(i); 23235 }; 23236 const applyTransform = (i) => { 23237 const s = zooms[i]; 23238 const cage = cageEl(i); 23239 if (!s || !cage) return; 23240 cage.style.transform = `translate3d(${s.x}px, ${s.y}px, 0) scale(${s.scale})`; 23241 const slide = slideEl(i); 23242 if (slide) slide.classList.toggle('is-zoomed', s.scale > 1.001); 23243 // Lock the carousel scroll while the active slide is zoomed 23244 // so a pan gesture doesn't accidentally snap to the next slide. 23245 if (i === activeIndex) { 23246 dialog.classList.toggle('is-zoom-active', s.scale > 1.001); 23247 } 23248 }; 23249 const clampPan = (i) => { 23250 const s = zooms[i]; 23251 const slide = slideEl(i); 23252 if (!s || !slide) return; 23253 if (s.scale <= 1) { s.x = 0; s.y = 0; return; } 23254 const rect = slide.getBoundingClientRect(); 23255 const halfX = (rect.width * (s.scale - 1)) / 2; 23256 const halfY = (rect.height * (s.scale - 1)) / 2; 23257 s.x = Math.max(-halfX, Math.min(halfX, s.x)); 23258 s.y = Math.max(-halfY, Math.min(halfY, s.y)); 23259 }; 23260 const toggleZoomAt = (i, clientX, clientY) => { 23261 const s = zooms[i]; 23262 const slide = slideEl(i); 23263 if (!s || !slide) return; 23264 if (s.scale > 1) { resetSlideZoom(i); return; } 23265 const rect = slide.getBoundingClientRect(); 23266 const cx = clientX - rect.left - rect.width / 2; 23267 const cy = clientY - rect.top - rect.height / 2; 23268 s.scale = TAP_SCALE; 23269 s.x = -cx * (TAP_SCALE - 1); 23270 s.y = -cy * (TAP_SCALE - 1); 23271 clampPan(i); 23272 applyTransform(i); 23273 }; 23274 23275 /* Touch gestures (pinch + pan). Pointer events handle 23276 mouse + single-touch pan; we add a separate touch handler 23277 on the SLIDE for pinch since the Pointer API doesn't 23278 natively give us simultaneous touches. */ 23279 const distance = (t1, t2) => Math.hypot(t2.clientX - t1.clientX, t2.clientY - t1.clientY); 23280 let pinch = null; // {i, startD, startScale} 23281 track.addEventListener('touchstart', (e) => { 23282 if (e.touches.length !== 2) return; 23283 const i = activeIndex; 23284 pinch = { i, startD: distance(e.touches[0], e.touches[1]), startScale: zooms[i].scale }; 23285 }, { passive: true }); 23286 track.addEventListener('touchmove', (e) => { 23287 if (!pinch || e.touches.length !== 2) return; 23288 const d = distance(e.touches[0], e.touches[1]); 23289 const s = zooms[pinch.i]; 23290 const next = Math.max(MIN, Math.min(MAX, pinch.startScale * (d / pinch.startD))); 23291 s.scale = next; 23292 clampPan(pinch.i); 23293 applyTransform(pinch.i); 23294 }, { passive: true }); 23295 track.addEventListener('touchend', () => { 23296 if (!pinch) return; 23297 const i = pinch.i; 23298 const s = zooms[i]; 23299 if (s.scale < 1.05) resetSlideZoom(i); 23300 pinch = null; 23301 }, { passive: true }); 23302 23303 /* Pointer events handle single-pointer interactions: a clean 23304 tap toggles zoom; a drag pans when already zoomed. Touch 23305 pan is also handled here when only one finger is down (the 23306 pinch handler above takes over when a second finger lands). */ 23307 let pointer = null; // {i, startX, startY, lastX, lastY, baseX, baseY, moved} 23308 const onPointerDown = (e) => { 23309 if (e.pointerType === 'touch' && e.isPrimary === false) return; 23310 const slide = e.target.closest('.bp-zoom__slide'); 23311 if (!slide) return; 23312 const i = Number(slide.dataset.index); 23313 if (Number.isNaN(i)) return; 23314 const s = zooms[i]; 23315 pointer = { 23316 i, 23317 startX: e.clientX, startY: e.clientY, 23318 lastX: e.clientX, lastY: e.clientY, 23319 baseX: s.x, baseY: s.y, 23320 moved: false, 23321 pointerType: e.pointerType 23322 };
23323 // Capture so we keep receiving move/up even if the cursor 23324 // leaves the slide. 23325 try { track.setPointerCapture(e.pointerId); } catch (_) {} 23326 }; 23327 const onPointerMove = (e) => { 23328 if (!pointer) return; 23329 const dx = e.clientX - pointer.startX; 23330 const dy = e.clientY - pointer.startY; 23331 if (!pointer.moved && Math.hypot(dx, dy) > 6) pointer.moved = true; 23332 const s = zooms[pointer.i]; 23333 if (s.scale > 1) { 23334 // Pan the zoomed cage. Use absolute delta from down so the 23335 // panning feels anchored to the initial grab. 23336 s.x = pointer.baseX + dx; 23337 s.y = pointer.baseY + dy; 23338 clampPan(pointer.i); 23339 applyTransform(pointer.i); 23340 // Prevent browser scroll while panning a zoomed image. 23341 e.preventDefault(); 23342 } 23343 pointer.lastX = e.clientX; pointer.lastY = e.clientY; 23344 }; 23345 const onPointerUp = (e) => { 23346 if (!pointer) return; 23347 const dx = e.clientX - pointer.startX; 23348 const dy = e.clientY - pointer.startY; 23349 if (!pointer.moved || Math.hypot(dx, dy) < 6) { 23350 // Clean tap â toggle zoom (use the up coordinates so the 23351 // anchor matches where the finger ended). 23352 toggleZoomAt(pointer.i, e.clientX, e.clientY); 23353 } 23354 try { track.releasePointerCapture(e.pointerId); } catch (_) {} 23355 pointer = null; 23356 }; 23357 track.addEventListener('pointerdown', onPointerDown); 23358 // pointermove must be non-passive so we can preventDefault 23359 // when panning a zoomed image (otherwise the browser would 23360 // scroll the carousel underneath us). 23361 track.addEventListener('pointermove', onPointerMove, { passive: false }); 23362 track.addEventListener('pointerup', onPointerUp); 23363 track.addEventListener('pointercancel', () => { pointer = null; }); 23364 23365 /* Mouse wheel pinch (Ctrl/â modifier â same as native 23366 browser zoom convention). */ 23367 track.addEventListener('wheel', (e) => { 23368 if (!dialog.open) return; 23369 if (!e.ctrlKey && !e.metaKey) return; 23370 e.preventDefault(); 23371 const i = activeIndex; 23372 const s = zooms[i]; 23373 const delta = -e.deltaY * 0.01; 23374 s.scale = Math.max(MIN, Math.min(MAX, s.scale + delta)); 23375 if (s.scale <= 1) { s.x = 0; s.y = 0; } 23376 clampPan(i); 23377 applyTransform(i); 23378 }, { passive: false }); 23379 23380 /* ---------- Open / close ---------- */ 23381 const open = (data, startIndex) => { 23382 slidesData = data; 23383 zooms = slidesData.map(() => ({ scale: 1, x: 0, y: 0 })); 23384 track.innerHTML = ''; 23385 slidesData.forEach((s, i) => track.appendChild(buildSlide(s, i))); 23386 buildDots(slidesData.length); 23387 activeIndex = Math.max(0, Math.min(startIndex || 0, slidesData.length - 1)); 23388 try { dialog.showModal(); } catch (_) { dialog.setAttribute('open', ''); } 23389 document.body.classList.add('bp-zoom-open'); 23390 // Jump to the right slide AFTER show (need a layout pass so 23391 // track.clientWidth is real). Two rAFs is enough to get past 23392 // the show-modal layout. 23393 requestAnimationFrame(() => requestAnimationFrame(() => { 23394 goTo(activeIndex, { smooth: false }); 23395 })); 23396 }; 23397 const close = () => { 23398 try { dialog.close(); } catch (_) { dialog.removeAttribute('open'); } 23399 document.body.classList.remove('bp-zoom-open'); 23400 dialog.classList.remove('is-zoom-active'); 23401 track.innerHTML = ''; 23402 slidesData = []; zooms = []; 23403 }; 23404 23405 /* ---------- Wiring ---------- */ 23406 prevBtn.addEventListener('click', () => goTo(activeIndex - 1, { smooth: true })); 23407 nextBtn.addEventListener('click', () => goTo(activeIndex + 1, { smooth: true })); 23408 dialog.querySelector('[data-bp-zoom-close]').addEventListener('click', close); 23409 dialog.addEventListener('click', (e) => { 23410 // Clicks that hit the dialog backdrop (not a slide / header / 23411 // arrow / dot) close the modal. 23412 if (e.target === dialog) close(); 23413 }); 23414 dialog.addEventListener('close', () => { 23415 document.body.classList.remove('bp-zoom-open'); 23416 dialog.classList.remove('is-zoom-active'); 23417 }); 23418 document.addEventListener('keydown', (e) => { 23419 if (!dialog.open) return; 23420 if (e.key === 'Escape') close(); 23421 else if (e.key === 'ArrowLeft') goTo(activeIndex - 1, { smooth: true }); 23422 else if (e.key === 'ArrowRight') goTo(activeIndex + 1, { smooth: true }); 23423 }); 23424 23425 /* Delegation: any [data-bp-zoomable] image inside a PDP gallery 23426 opens the modal. Slide list = every zoomable image inside the 23427 clicked image's gallery, in DOM order. */ 23428 document.addEventListener('click', (e) => { 23429 const target = e.target.closest('[data-bp-zoomable]'); 23430 if (!target) return; 23431 const gallery = target.closest('bp-pdp-gallery, .bp-pdp-gallery, .product-slider-main-images'); 23432 if (!gallery) return; 23433 const all = Array.from(gallery.querySelectorAll('img[data-bp-zoomable]')); 23434 if (!all.length) return; 23435 const startIndex = all.indexOf(target); 23436 const data = all.map((img) => ({ 23437 src: toHires(img.currentSrc || img.src), 23438 alt: img.alt || '' 23439 })); 23440 open(data, startIndex < 0 ? 0 : startIndex); 23441 }); 23442 23443 /* Re-anchor on resize so the active slide stays correctly 23444 scrolled into view (track.clientWidth changes affect 23445 scrollLeft math). */ 23446 window.addEventListener('resize', () => { 23447 if (!dialog.open) return; 23448 goTo(activeIndex, { smooth: false }); 23449 }); 23450 })(); 23451 </script>
23451 23452 23453 <style> 23454 /* Modal â full-screen white dialog. Vertical layout: header, 23455 track, dots. The track is a native scroll-snap horizontal 23456 container; slides are absolute-flex-sized children. */ 23457 dialog.bp-zoom { 23458 margin: 0; 23459 padding: 0; 23460 border: 0; 23461 max-width: none; 23462 max-height: none; 23463 width: 100vw; 23464 height: 100dvh; 23465 inset: 0; 23466 background: var(--bp-color-bg-page); 23467 color: var(--bp-color-text, #171609); 23468 overflow: hidden; 23469 } 23470 dialog.bp-zoom[open] { 23471 display: flex; 23472 flex-direction: column; 23473 } 23474 dialog.bp-zoom::backdrop { background: rgba(0, 0, 0, 0.5); } 23475 body.bp-zoom-open { overflow: hidden; } 23476 23477 .bp-zoom__head { 23478 flex: 0 0 auto; 23479 display: grid; 23480 grid-template-columns: 1fr auto 1fr; 23481 align-items: center; 23482 padding: 12px 16px; 23483 border-bottom: 1px solid rgba(23, 22, 9, 0.08); 23484 z-index: 2; 23485 } 23486 .bp-zoom__hint { 23487 grid-column: 2; 23488 display: inline-flex; 23489 align-items: center; 23490 gap: 8px; 23491 font-family: var(--bp-font-body); 23492 font-size: 13px; 23493 font-weight: 500; 23494 letter-spacing: 0.5px; 23495 text-transform: uppercase; 23496 color: var(--bp-color-body-copy, #74736B); 23497 } 23498 .bp-zoom__hint svg { display: block; } 23499 @media (hover: hover) and (pointer: fine) { 23500 .bp-zoom__hint-text::before { content: 'Click to Zoom'; } 23501 .bp-zoom__hint-text { font-size: 0; } 23502 .bp-zoom__hint-text::before { font-size: 13px; } 23503 } 23504 .bp-zoom__close { 23505 grid-column: 3; 23506 justify-self: end; 23507 background: transparent; 23508 border: 0; 23509 padding: 6px; 23510 cursor: pointer; 23511 color: inherit; 23512 line-height: 0; 23513 } 23514 .bp-zoom__close:hover { opacity: 0.65; } 23515 23516 /* Native scroll-snap carousel. JS reads/writes track.scrollLeft; 23517 browser does the swipe + inertia. When the active slide is 23518 zoomed, dialog.is-zoom-active disables overflow so panning 23519 doesn't accidentally snap to the next slide. */ 23520 .bp-zoom__track { 23521 flex: 1 1 0; 23522 position: relative; 23523 display: flex; 23524 overflow-x: auto; 23525 overflow-y: hidden; 23526 scroll-snap-type: x mandatory; 23527 scroll-behavior: auto; 23528 scrollbar-width: none; 23529 -ms-overflow-style: none; 23530 -webkit-overflow-scrolling: touch; 23531 touch-action: pan-y pinch-zoom; 23532 } 23533 .bp-zoom__track::-webkit-scrollbar { display: none; } 23534 dialog.bp-zoom.is-zoom-active .bp-zoom__track { 23535 overflow-x: hidden; 23536 touch-action: none; 23537 } 23538 23539 .bp-zoom__slide { 23540 flex: 0 0 100%; 23541 height: 100%; 23542 scroll-snap-align: start; 23543 display: flex; 23544 align-items: center; 23545 justify-content: center; 23546 overflow: hidden; 23547 user-select: none; 23548 position: relative; 23549 } 23550 .bp-zoom__cage { 23551 width: 100%; 23552 height: 100%; 23553 display: flex; 23554 align-items: center; 23555 justify-content: center; 23556 transition: transform 0.2s ease-out; 23557 will-change: transform; 23558 cursor: zoom-in; 23559 } 23560 /* When zoomed: pause the transition so pan feels 1:1, and flip 23561 the cursor to a grab cue. Disabling the transition prevents 23562 lag while dragging â the JS applies transform inline. */ 23563 .bp-zoom__slide.is-zoomed .bp-zoom__cage { 23564 transition: none; 23565 cursor: grab; 23566 } 23567 .bp-zoom__slide.is-zoomed .bp-zoom__cage:active { cursor: grabbing; } 23568 .bp-zoom__img { 23569 max-width: 100%; 23570 max-height: 100%; 23571 width: auto; 23572 height: auto; 23573 object-fit: contain; 23574 pointer-events: none; 23575 } 23576 23577 /* Arrows */ 23578 .bp-zoom__arrow { 23579 position: absolute; 23580 top: 50%; 23581 transform: translateY(-50%); 23582 width: 48px; 23583 height: 48px; 23584 display: inline-flex; 23585 align-items: center; 23586 justify-content: center; 23587 background: var(--bp-color-bg-page); 23588 border: 1px solid rgba(23, 22, 9, 0.12); 23589 cursor: pointer; 23590 z-index: 3; 23591 color: var(--bp-color-text, #171609); 23592 transition: background-color 0.15s ease, opacity 0.15s ease; 23593 } 23594 .bp-zoom__arrow:hover { background: var(--bp-color-bg-page); } 23595 .bp-zoom__arrow:disabled { opacity: 0.3; cursor: default; }
23596 .bp-zoom__arrow--prev { left: 16px; } 23597 .bp-zoom__arrow--next { right: 16px; } 23598 @media (max-width: 749px) { 23599 .bp-zoom__arrow { display: none; } 23600 } 23601 23602 /* Dots */ 23603 .bp-zoom__dots { 23604 flex: 0 0 auto; 23605 list-style: none; 23606 margin: 0; 23607 padding: 12px 16px; 23608 display: flex; 23609 gap: 8px; 23610 justify-content: center; 23611 align-items: center; 23612 border-top: 1px solid rgba(23, 22, 9, 0.08); 23613 } 23614 .bp-zoom__dot { 23615 width: 8px; 23616 height: 8px; 23617 border-radius: 50%; 23618 border: 1px solid var(--bp-color-text, #171609); 23619 background: transparent; 23620 padding: 0; 23621 cursor: pointer; 23622 transition: background-color 0.15s ease; 23623 } 23624 .bp-zoom__dot.is-active { background: var(--bp-color-text, #171609); } 23625 </style>
23626<script> 23627 (function () { 23628 'use strict'; 23629 if (window.__bpCartCtlWired) return; 23630 window.__bpCartCtlWired = true; 23631 23632 const AMPLIENCE_BASE = 23633 (window.shop && window.shop.themeSettings && window.shop.themeSettings.amplience_cdn_base_url) || 23634 "https:\/\/media.bostonproper.com"; 23635 const STYLITICS_USERNAME = 'bostonproper'; 23636 const MAX_CARDS = 8; 23637 const ENDPOINT = 'https://widget-api.stylitics.com/api/outfits'; 23638 const BP_CTL_BAG_SVG = '<svg viewBox="0 0 20 20" fill="currentColor" xmlns="http://www.w3.org/2000/svg"><path d="M4.18929 5.74434H16.5343H16.6292L16.6729 5.74549C16.9007 5.75099 17.1328 5.87803 17.2448 6.08089C17.3145 6.20157 17.3382 6.30372 17.3547 6.43048C17.6354 9.18806 17.9008 11.996 18.3051 14.7356C18.3771 15.2467 18.4466 15.758 18.5137 16.2697C18.5163 16.2917 18.5195 16.336 18.5207 16.3582C18.5378 16.6193 18.4156 16.9263 18.1508 17.0687C17.9651 17.1752 17.7807 17.1763 17.5845 17.1755C16.1978 17.1755 4.69428 17.1755 3.34023 17.1755C3.20422 17.1726 3.01496 17.1862 2.86505 17.1451C2.45036 17.0406 2.25213 16.656 2.31666 16.2445C2.41071 15.5271 2.58579 14.2489 2.68621 13.5185C2.95476 11.5399 3.15849 9.46674 3.35962 7.47459C3.37582 7.3571 3.44296 6.63739 3.45714 6.52887L3.46698 6.43424L3.46814 6.42237C3.46814 6.41948 3.46901 6.4134 3.47045 6.40559C3.5156 6.02417 3.79254 5.74028 4.18929 5.74434ZM4.18929 6.6125C4.2136 6.6125 4.24804 6.59601 4.27872 6.57343C4.30302 6.55491 4.3201 6.53089 4.32704 6.51845C4.33457 6.50601 4.33486 6.50138 4.33399 6.50427C4.33341 6.50803 4.33225 6.51353 4.33138 6.51758C4.33138 6.51758 4.33081 6.51932 4.33052 6.52424L4.32936 6.5361L4.31923 6.63044C4.132 8.4151 3.9054 10.3181 3.74624 12.1016C3.63685 13.4481 3.56103 15.0256 3.46727 16.3753L3.46582 16.3915V16.3961C3.46582 16.3961 3.46611 16.3973 3.4664 16.3915C3.4664 16.3872 3.4664 16.375 3.46351 16.3588C3.45396 16.2908 3.41721 16.2161 3.3602 16.1507C3.29306 16.0798 3.20422 16.0246 3.12319 16.0162C3.11856 16.0162 3.13361 16.0173 3.15647 16.0176C4.67199 16.0182 16.0117 16.0176 17.5848 16.0176H17.6719C17.6823 16.017 17.6916 16.0168 17.6962 16.0165C17.7014 16.0162 17.7034 16.0159 17.6991 16.0162C17.5752 16.0286 17.4268 16.1591 17.3857 16.2795C17.3576 16.3556 17.3617 16.3788 17.362 16.3941C17.362 16.3921 17.362 16.393 17.362 16.3886L17.3605 16.3724C17.254 14.8543 17.1872 13.3292 17.0509 11.8148C16.8906 10.0895 16.6752 8.23163 16.4883 6.50456C16.495 6.52019 16.5161 6.55433 16.5468 6.57575C16.5714 6.59485 16.5989 6.60585 16.6145 6.60961L16.6243 6.61192H16.6284C16.6284 6.61192 16.6298 6.61279 16.6304 6.61279C16.6318 6.61279 16.6336 6.61279 16.633 6.61279C16.6324 6.61279 16.635 6.61279 16.6292 6.61279C16.5783 6.61221 16.4 6.61337 16.3445 6.61279H4.18929V6.6125Z"/><path d="M6.08301 8.78064C6.1788 7.5189 6.20658 6.21405 6.58539 4.98907C7.15432 3.11007 8.4421 2.11631 10.414 2.12673C13.8001 2.13338 14.5031 4.87997 14.6721 7.71395C14.6964 8.07453 14.7158 8.43453 14.745 8.78035L13.5874 8.77485C13.6172 8.46057 13.6575 8.02649 13.6754 7.71858C13.801 5.83351 13.5976 3.34129 11.3988 2.81605C10.7575 2.6676 10.0702 2.6676 9.42892 2.81605C7.61503 3.24811 7.15143 5.0374 7.12683 6.6872C7.11121 7.36929 7.17545 8.09132 7.24056 8.77485L6.08301 8.78035V8.78064Z"/></svg>'; 23639 23640 // ----- Helpers ----- 23641 function inferPrefix(rootEl) { 23642 const root = rootEl.closest('[data-bp-ctl]'); 23643 if (!root) return 'bp-cart-drawer'; 23644 const match = Array.from(root.classList).find((c) => /__ctl$/.test(c)); 23645 return match ? match.replace(/__ctl$/, '') : 'bp-cart-drawer'; 23646 } 23647 23648 function escape(s) { 23649 return String(s || '').replace(/[&<>"']/g, (c) => ({ 23650 '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' 23651 }[c])); 23652 } 23653 23654 // Scroll-progress indicator â same thin-bar-with-thumb pattern as 23655 // bp-product-carousel's BpProductCarousel.updateArrows(). Wired once 23656 // per track (idempotent) and re-measured any time the track's content 23657 // changes, since a fresh render can change scrollWidth. 23658 function wireIndicator(track) { 23659 const root = track.closest('[data-bp-ctl]'); 23660 const indicator = root && root.querySelector('[data-bp-ctl-indicator]'); 23661 if (!indicator) return null; 23662 if (!track.__bpIndicatorUpdate) { 23663 const update = () => { 23664 const { scrollLeft, scrollWidth, clientWidth } = track; 23665 if (scrollWidth <= clientWidth + 1) { 23666 indicator.style.display = 'none'; 23667 return; 23668 } 23669 indicator.style.display = ''; 23670 const thumbW = (clientWidth / scrollWidth) * 100; 23671 const overflow = scrollWidth - clientWidth; 23672 const thumbX = overflow > 0 ? scrollLeft / overflow : 0; 23673 indicator.style.setProperty('--bp-ctl-thumb-w', thumbW + '%'); 23674 indicator.style.setProperty('--bp-ctl-thumb-x', thumbX); 23675 }; 23676 track.__bpIndicatorUpdate = update; 23677 track.addEventListener('scroll', update, { passive: true }); 23678 window.addEventListener('resize', update); 23679 } 23680 return track.__bpIndicatorUpdate; 23681 } 23682 23683 // Stylitics serves prices as currency-formatted strings.
23684 // "$25.00" â "$25" (drop the .00) so it lines up with the rest of 23685 // the BP cart typography. 23686 function formatPrice(s) { 23687 if (!s) return ''; 23688 return String(s).replace('.00', ''); 23689 } 23690 23691 function itemImage(item) { 23692 // Atlas's pickItemImage: Shopify Collective items use their raw 23693 // image_url (no Amplience asset); everyone else uses the 23694 // {amplience}/i/bostonproper/{remote_id}_2 SET shot. 23695 const isCollective = item && item.other_client_item_ids && item.other_client_item_ids.shopify_collective; 23696 const remoteId = item && item.remote_id; 23697 if (isCollective || !remoteId) return (item && item.image_url) || ''; 23698 return `${AMPLIENCE_BASE}/i/bostonproper/${remoteId}_2?fmt=auto&w=484`; 23699 } 23700 23701 function itemUrl(item) { 23702 const raw = (item && item.affiliate_link) || ''; 23703 // Keep navigation on the local storefront â strip the host. 23704 return raw.replace(/^https?:\/\/(?:www\.)?bostonproper\.com/, ''); 23705 } 23706 23707 // Product handle parsed from affiliate_link, e.g. "iris-ring-set". 23708 // Belt-and-suspenders against the remote_id/bp_style in-cart check 23709 // below: those assume Stylitics echoes the queried item_number back 23710 // as remote_id for the anchor, which doesn't hold for every product 23711 // (Stylitics' catalog id doesn't always match the ERP-synced bp_style 23712 // metafield). A live edge case: "Iris Ring Set" recommended itself 23713 // back because its Stylitics remote_id != its bp_style. The handle 23714 // is what the customer actually sees, so it's a more reliable match 23715 // regardless of whatever id scheme Stylitics uses internally. 23716 function itemHandle(item) { 23717 const raw = (item && item.affiliate_link) || ''; 23718 const match = raw.match(/\/products\/([^/?#]+)/); 23719 return match ? match[1] : ''; 23720 } 23721 23722 function isPopular(item) { 23723 // Stylitics doesn't carry product tags; BP convention surfaces 23724 // popularity via the `meta` map on collective items. We can flag 23725 // anchor items here later if Stylitics adds a signal; for now, 23726 // render no badge by default. 23727 return !!(item && item.bp_popular); 23728 } 23729 23730 function buildCard(item, prefix) { 23731 const title = item.name || ''; 23732 const url = itemUrl(item); 23733 const img = itemImage(item); 23734 const price = formatPrice(item.price_localized); 23735 const sale = item.sale_price_localized && item.sale_price_localized !== item.price_localized 23736 ? formatPrice(item.sale_price_localized) 23737 : null; 23738 23739 const badge = isPopular(item) ? `<span class="${prefix}__ctl-badge">Popular</span>` : ''; 23740 const priceHtml = sale 23741 ? `<span class="${prefix}__ctl-sale">${escape(sale)}</span> 23742 <span class="${prefix}__ctl-compare">${escape(price)}</span>` 23743 : escape(price); 23744 23745 return ` 23746 <li class="${prefix}__ctl-card ctl__item" data-bp-ctl-item data-remote-id="${escape(item.remote_id || '')}"> 23747 <a class="${prefix}__ctl-link" href="${escape(url || '#')}"> 23748 ${badge} 23749 <div class="${prefix}__ctl-image ctl-media-container"> 23750 <img src="${escape(img)}" alt="${escape(title)}" loading="lazy" width="240" height="300"> 23751 <button type="button" class="ctl-bag-icon" data-bp-ctl-bag data-url="${url}" data-bp-remote-id="${escape(String(item.remote_id == null ? '' : item.remote_id))}" aria-label="Quick add ${escape(item.name)}">${BP_CTL_BAG_SVG}</button> 23752 </div> 23753 <div class="${prefix}__ctl-body"> 23754 <p class="${prefix}__ctl-name">${escape(title)}</p> 23755 <p class="${prefix}__ctl-price">${priceHtml}</p> 23756 </div> 23757 </a> 23758 </li>`; 23759 } 23760 23761 async function fetchOutfits(style) { 23762 const url = `${ENDPOINT}?username=${encodeURIComponent(STYLITICS_USERNAME)}&item_number=${encodeURIComponent(style)}`; 23763 try { 23764 const res = await fetch(url, { cache: 'force-cache' }); 23765 if (!res.ok) return []; 23766 const data = await res.json(); 23767 return Array.isArray(data) ? data : []; 23768 } catch (err) { 23769 console.warn('[bp-cart-ctl] Stylitics fetch failed', err); 23770 return []; 23771 } 23772 } 23773 23774 // Every bp_style currently in the bag, from the server-rendered cart 23775 // lines. bp-cart-line-item.liquid stamps data-bp-style on each line 23776 // and the drawer re-renders lines from section HTML (not from
23777 // /cart.js, which carries no metafields), so this stays accurate 23778 // after add/remove without an extra round-trip. 23779 function cartStyles() { 23780 const out = new Set(); 23781 document.querySelectorAll('[data-bp-cart-line][data-bp-style]').forEach((line) => { 23782 const s = (line.getAttribute('data-bp-style') || '').trim(); 23783 if (s) out.add(String(s)); 23784 }); 23785 return out; 23786 } 23787 23788 // Every product handle currently in the bag. See itemHandle() above â 23789 // this is the fallback exclusion for when remote_id/bp_style doesn't 23790 // match (Stylitics catalog id drift), since the handle is a direct, 23791 // customer-visible identity check. 23792 function cartHandles() { 23793 const out = new Set(); 23794 document.querySelectorAll('[data-bp-cart-line][data-product-handle]').forEach((line) => { 23795 const h = (line.getAttribute('data-product-handle') || '').trim(); 23796 if (h) out.add(h); 23797 }); 23798 return out; 23799 } 23800 23801 // Collect a flat de-duped item list from up to N outfits, dropping 23802 // anything already in the bag. 23803 // 23804 // This used to drop only the anchor style, which is why adding an 23805 // item could still see it recommended back: the anchor is just the 23806 // most-recently-added line, so as soon as a different line became the 23807 // anchor, the earlier item was fair game again â and any outfit 23808 // legitimately containing it would recommend a product already in the 23809 // bag. Excluding the whole bag covers the anchor too (it's a cart 23810 // line), but the anchor check is kept for the case where the cart 23811 // DOM isn't rendered yet. 23812 function flattenOutfits(outfits, anchorStyle, max) { 23813 const seen = new Set(); 23814 const inCart = cartStyles(); 23815 const inCartHandles = cartHandles(); 23816 const out = []; 23817 for (let i = 0; i < outfits.length && out.length < max; i += 1) { 23818 const items = (outfits[i] && outfits[i].items) || []; 23819 for (let j = 0; j < items.length && out.length < max; j += 1) { 23820 const item = items[j]; 23821 if (!item) continue; 23822 const rid = item.remote_id ? String(item.remote_id) : ''; 23823 if (rid && String(anchorStyle) === rid) continue; 23824 if (rid && inCart.has(rid)) continue; 23825 const handle = itemHandle(item); 23826 if (handle && inCartHandles.has(handle)) continue; 23827 const key = item.remote_id || item.name; 23828 if (!key || seen.has(key)) continue; 23829 seen.add(key); 23830 out.push(item); 23831 } 23832 } 23833 return out; 23834 } 23835 23836 // Read the anchor from the most-recently-added cart line in the DOM. 23837 // Falls back to /cart.js + a product fetch only if no DOM line carries 23838 // a data-bp-style (e.g. all anchors are products without a bp_style 23839 // metafield). 23840 function resolveAnchorStyle() { 23841 const lines = Array.from(document.querySelectorAll('[data-bp-cart-line][data-bp-style]')); 23842 // Pick the LAST one in source order â that's the most-recently-added 23843 // because Liquid renders cart.items in add order (oldest first). 23844 for (let i = lines.length - 1; i >= 0; i -= 1) { 23845 const style = lines[i].getAttribute('data-bp-style'); 23846 if (style && style.trim()) return style.trim(); 23847 } 23848 return null; 23849 } 23850 23851 let lastKey = null; 23852 let lastItems = null; 23853 23854 // Single-flight guard. Several listeners (DOMContentLoaded, cart:update, 23855 // bp:cart-drawer:opened, ...) can fire within milliseconds of each other 23856 // â most visibly on first page load, when a quick drawer-open races 23857 // DOMContentLoaded's own sync(). Without this, both calls would see the 23858 // same "not cached yet" state and each kick off an independent 23859 // fetchOutfits() call; whichever resolves last wins and overwrites the 23860 // other's render, which is why the CTL could flash in then disappear. 23861 // While a run is in flight, later calls just flag `pending` instead of 23862 // racing a second fetch; once the in-flight run finishes, one clean 23863 // follow-up run picks up whatever the final state actually is. 23864 let inFlight = null; 23865 let pending = false;
23866 23867 function sync() { 23868 if (inFlight) { 23869 pending = true; 23870 return inFlight; 23871 } 23872 inFlight = runSync().finally(() => { 23873 inFlight = null; 23874 if (pending) { 23875 pending = false; 23876 sync(); 23877 } 23878 }); 23879 return inFlight; 23880 } 23881 23882 async function runSync() { 23883 const tracks = Array.from(document.querySelectorAll('[data-bp-ctl-track]')); 23884 if (!tracks.length) return; 23885 const sections = tracks.map((t) => t.closest('[data-bp-ctl]')).filter(Boolean); 23886 23887 const anchorStyle = resolveAnchorStyle(); 23888 if (!anchorStyle) { 23889 sections.forEach((sec) => sec.setAttribute('hidden', '')); 23890 lastKey = null; 23891 return; 23892 } 23893 23894 // Cache on the anchor AND the bag contents (styles + handles â a 23895 // product missing bp_style would only show up in the handle set, so 23896 // both are needed to bust the cache correctly). Keying on the anchor 23897 // alone went stale now that the whole bag is excluded: removing a 23898 // non-anchor item leaves the anchor unchanged, so the removed 23899 // product would stay filtered out of its own recommendations. 23900 // fetchOutfits is force-cache'd, so re-running this is cheap. 23901 const cacheKey = anchorStyle 23902 + '|' + Array.from(cartStyles()).sort().join(',') 23903 + '|' + Array.from(cartHandles()).sort().join(','); 23904 let items = lastItems; 23905 if (cacheKey !== lastKey) { 23906 const outfits = await fetchOutfits(anchorStyle); 23907 items = flattenOutfits(outfits, anchorStyle, MAX_CARDS); 23908 lastKey = cacheKey; 23909 lastItems = items; 23910 } 23911 23912 if (!items || !items.length) { 23913 sections.forEach((sec) => sec.setAttribute('hidden', '')); 23914 return; 23915 } 23916 23917 tracks.forEach((track) => { 23918 const prefix = inferPrefix(track); 23919 track.innerHTML = items.map((item) => buildCard(item, prefix)).join(''); 23920 try { track.scrollTo({ left: 0, behavior: 'auto' }); } catch (_) {} 23921 const update = wireIndicator(track); 23922 if (update) requestAnimationFrame(update); 23923 }); 23924 sections.forEach((sec) => sec.removeAttribute('hidden')); 23925 23926 document.dispatchEvent(new CustomEvent('bp:ctl:updated')); 23927 }// `cart:updated` (with the 'd') is BP-internal â fired by the cart 23928 // drawer's bpCartChange() after qty/remove mutations. 23929 document.addEventListener('cart:updated', () => sync()); 23930 // `cart:update` (no 'd') is Horizon's stock event â fired on PDP 23931 // add-to-cart, quick-add, and product-card adds. The drawer reacts 23932 // to it but doesn't translate to `cart:updated`, so the CTL needs 23933 // to listen for both. Without this the CTL stays empty after any 23934 // cross-page add (the most common path into the drawer). 23935 document.addEventListener('cart:update', () => sync()); 23936 // `product:added` is the alternative add-to-cart signal some Horizon
23937 // surfaces dispatch â listen too so we cover every cart-grow path. 23938 document.addEventListener('product:added', () => sync()); 23939 // The cart drawer dispatches this when its dialog becomes visible 23940 // (see snippets/bp-cart-drawer.liquid â open()). Re-sync here so 23941 // the CTL populates the first time the user opens the drawer, 23942 // even when the cart was already populated server-side and no 23943 // cart mutation event has fired since the snippet's initial run. 23944 document.addEventListener('bp:cart-drawer:opened', () => sync()); 23945 // Fired by the drawer once the re-fetched cart lines are actually in 23946 // the DOM. The cart-* events above fire as soon as the cart API 23947 // returns, which is BEFORE the lines carrying data-bp-style are 23948 // replaced â so a sync on those alone reads the pre-mutation bag and 23949 // recommends back the item that was just added. This is the signal 23950 // the in-bag exclusion depends on. 23951 document.addEventListener('bp:cart-lines:rendered', () => sync()); 23952 23953 // Wire bag buttons â delegated so it survives track.innerHTML re-renders. 23954 // preventDefault + stopPropagation blocks the parent <a> from navigating. 23955 document.addEventListener('click', (e) => { 23956 const bag = e.target.closest && e.target.closest('[data-bp-ctl-bag]'); 23957 if (!bag) return; 23958 e.preventDefault(); 23959 e.stopPropagation(); 23960 const url = bag.getAttribute('data-url'); 23961 const remoteId = bag.getAttribute('data-bp-remote-id') || ''; 23962 openQuickAdd(url, remoteId); 23963 }); 23964 23965 function openQuickAdd(productUrl, remoteId) { 23966 if (!productUrl) return; 23967 // Set the Stylitics widget-add context so bp-how-to-wear-it can 23968 // attribute the follow-up /cart/add as a 'widget' engagement. 23969 const handle = (String(productUrl).match(/\/products\/([^/?#]+)/) || [])[1] || ''; 23970 if (handle) { 23971 window.__bpStyliticsWidgetAdd = { 23972 style: String(remoteId || handle), 23973 handle, 23974 until: Date.now() + 90000, 23975 }; 23976 } 23977 let host = window.__bpCartCtlQaHost; 23978 if (!host) { 23979 host = document.createElement('div'); 23980 host.style.cssText = 'position:absolute;left:-9999px;top:0;width:1px;height:1px;overflow:hidden;'; 23981 document.body.appendChild(host); 23982 window.__bpCartCtlQaHost = host; 23983 } 23984 host.innerHTML = 23985 '<product-card>' + 23986 '<a class="product-card__link" ref="productCardLink" href="' + productUrl + '"></a>' + 23987 '<quick-add-component ref="quickAdd">' + 23988 '<button type="button" class="button quick-add__button quick-add__button--choose add-to-cart-button" ' + 23989 'on:click="quick-add-component/handleClick"></button>' + 23990 '</quick-add-component>' + 23991 '</product-card>'; 23992 const choose = host.querySelector('.quick-add__button--choose'); 23993 if (typeof window.bpTrackCartQuickview === 'function') window.bpTrackCartQuickview(productUrl); 23994 requestAnimationFrame(() => { 23995 try { 23996 if (typeof window.bpOpenProductDrawer === 'function' && choose) window.bpOpenProductDrawer(choose); 23997 else if (choose) choose.click(); 23998 else window.location.href = productUrl; 23999 } catch (err) { 24000 console.warn('[bp-cart-ctl] quick-add open failed, navigating', err); 24001 window.location.href = productUrl; 24002 } 24003 }); 24004 } 24005 })(); 24006</script>
24006 24007 24008<style data-shopify> 24009 .ctl-media-container { 24010 position: relative; 24011 } 24012 24013 .ctl-bag-icon { 24014 position: absolute; 24015 bottom: 12px; 24016 right: 12px; 24017 z-index: 2; 24018 width: 36px; 24019 height: 36px; 24020 padding: 0; 24021 border: 1px solid rgba(23, 22, 9, 0.15); 24022 border-radius: 0; 24023 background: var(--bp-color-bg-page); 24024 color: var(--bp-color-text, #171609); 24025 display: inline-flex; 24026 align-items: center; 24027 justify-content: center; 24028 cursor: pointer; 24029 opacity: 0; 24030 transform: translateY(4px); 24031 transition: opacity 0.2s ease, transform 0.2s ease, background-color 0.2s ease; 24032 } 24033 24034 .ctl-bag-icon:hover { 24035 background: var(--bp-color-text, #171609); 24036 color: #fff; 24037 } 24038 .ctl-bag-icon svg { 24039 width: 20px; 24040 height: 20px; 24041 display: block; 24042 } 24043 .ctl__item:hover .ctl-bag-icon, 24044 .ctl-bag-icon:focus-visible { 24045 opacity: 1; 24046 transform: translateY(0); 24047 } 24048</style> 24049
24050<script> 24051(function () { 24052 if (window.bpBundleUpsell) return; 24053 24054 var QA_HOST = null; // hidden quick-add trigger host 24055 var REMOVED_KEY = 'removedUpsells'; 24056 var SURFACE_SELECTOR = 'bp-cart-drawer, bp-cart-page'; 24057 24058 // Candidate upsell products emitted on a cart line by bp-cart-line-item.liquid. 24059 function readLineCandidates(line) { 24060 var el = line.querySelector('[data-bp-line-upsell]'); 24061 if (!el) return []; 24062 try { return JSON.parse((el.textContent || '').trim() || '[]'); } 24063 catch (e) { return []; } 24064 } 24065 24066 function getRemoved() { 24067 try { return JSON.parse(sessionStorage.getItem(REMOVED_KEY) || '[]'); } 24068 catch (e) { return []; } 24069 } 24070 function addRemoved(id) { 24071 var removed = getRemoved(); 24072 if (id && removed.indexOf(String(id)) === -1) { 24073 removed.push(String(id)); 24074 try { sessionStorage.setItem(REMOVED_KEY, JSON.stringify(removed)); } catch (e) { /* noop */ } 24075 } 24076 } 24077 function escapeHtml(str) { 24078 return String(str == null ? '' : str).replace(/[&<>"']/g, function (c) { 24079 return { '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]; 24080 }); 24081 } 24082 24083 function buildCard(u) { 24084 var card = document.createElement('li'); 24085 card.className = 'bp-cart-upsell'; 24086 card.setAttribute('data-bp-cart-upsell', ''); 24087 card.setAttribute('data-upsell-id', u.id); 24088 card.setAttribute('data-upsell-url', u.url); 24089 24090 var dv = parseFloat(u.discount_value); 24091 var discountLabel = ''; 24092 if (u.discount_type && dv) { 24093 discountLabel = u.discount_type === 'percentage' 24094 ? 'Save ' + Math.round(dv) + '%' 24095 : 'Save $' + Math.round(dv); 24096 } 24097 24098 var media = u.image 24099 ? '<a class="bp-cart-upsell__media" href="' + u.url + '">' + 24100 '<img src="' + u.image + '" alt="" width="120" height="150" loading="lazy">' + 24101 '</a>' 24102 : ''; 24103 24104 card.innerHTML = 24105 media + 24106 '<div class="bp-cart-upsell__body">' + 24107 '<p class="bp-cart-upsell__title">' + escapeHtml(u.title) + '</p>' + 24108 '<p class="bp-cart-upsell__prompt">Complete the look' + (discountLabel ? ' and' : '') + '</p>' + 24109 (discountLabel ? '<p class="bp-cart-upsell__discount">' + discountLabel + '</p>' : '') + 24110 '<button type="button" class="bp-cart-upsell__add" data-bp-upsell-add>Add to Bag</button>' + 24111 '</div>' + 24112 '<button type="button" class="bp-cart-upsell__close" data-bp-upsell-remove aria-label="Dismiss">' + 24113 '<svg width="14" height="14" viewBox="0 0 14 14" fill="none" aria-hidden="true">' + 24114 '<path d="M12.66 1 1 13" stroke="currentColor" stroke-miterlimit="10"/>' + 24115 '<path d="M1 1l11.66 12" stroke="currentColor" stroke-miterlimit="10"/>' + 24116 '</svg>' + 24117 '</button>'; 24118 return card; 24119 } 24120 24121 // Inject ONE "Complete the look" card into `surfaceEl` (a <bp-cart-drawer> 24122 // or <bp-cart-page>): the first cart line that has an eligible candidate â 24123 // available, not already in the cart, not dismissed this session. DOM-driven 24124 // (reads the server-emitted per-line blobs), so it works on cold open/load 24125 // and after a line re-render alike. Clears prior cards first. 24126 function render(surfaceEl) { 24127 if (!surfaceEl) return; 24128 surfaceEl.querySelectorAll('[data-bp-cart-upsell]').forEach(function (el) { el.remove(); }); 24129 24130 var bundlesOff = window.shop && window.shop.themeSettings && 24131 window.shop.themeSettings.bundlesEnabled === false; 24132 if (bundlesOff) return; 24133 24134 var lines = Array.prototype.slice.call(surfaceEl.querySelectorAll('[data-bp-cart-line]')); 24135 if (!lines.length) return; 24136 var inCart = lines 24137 .map(function (l) { return (l.dataset.productHandle || '').toLowerCase(); }) 24138 .filter(Boolean); 24139 var removed = getRemoved(); 24140 24141 for (var i = 0; i < lines.length; i++) { 24142 var candidates = readLineCandidates(lines[i]); 24143 if (!candidates.length) continue; 24144 var u = candidates.find(function (p) { 24145 var h = (p.handle || '').toLowerCase(); 24146 return h && p.available && 24147 inCart.indexOf(h) === -1 && removed.indexOf(String(p.id)) === -1; 24148 }); 24149 if (u) { 24150 u.url = u.url || ('/products/' + u.handle); 24151 lines[i].parentNode.insertBefore(buildCard(u), lines[i]); 24152 break; 24153 } 24154 } 24155 } 24156 24157 // Render into every cart surface currently in the DOM. 24158 function renderAll() {
24159 document.querySelectorAll(SURFACE_SELECTOR).forEach(function (el) { render(el); }); 24160 } 24161 24162 // Open the upsell product in the BP quick-add drawer (same dynamic hidden 24163 // trigger the HTWI section uses â Horizon reads the product URL from the 24164 // product-card's ref="productCardLink"). Preserves LIPs via the fetched form. 24165 function openQuickAdd(productUrl) { 24166 if (!productUrl) return; 24167 if (!QA_HOST) { 24168 QA_HOST = document.createElement('div'); 24169 QA_HOST.setAttribute('data-bp-upsell-qa-host', ''); 24170 QA_HOST.style.cssText = 'position:absolute;left:-9999px;top:0;width:1px;height:1px;overflow:hidden;'; 24171 document.body.appendChild(QA_HOST); 24172 } 24173 QA_HOST.innerHTML = 24174 '<product-card>' + 24175 '<a class="product-card__link" ref="productCardLink" href="' + productUrl + '"></a>' + 24176 '<quick-add-component ref="quickAdd">' + 24177 '<button type="button" class="button quick-add__button quick-add__button--choose add-to-cart-button" ' + 24178 'on:click="quick-add-component/handleClick"></button>' + 24179 '</quick-add-component>' + 24180 '</product-card>'; 24181 var choose = QA_HOST.querySelector('.quick-add__button--choose'); 24182 // Cart-flyout / cart-page Quickview view_item â this bespoke trigger isn't a 24183 // .bp-card, so bp-track-cards' fireQuickview can't see it; bpTrackCartQuickview 24184 // resolves the Page Type (Cart Flyout Quickview vs Cart Quickview). 24185 if (typeof window.bpTrackCartQuickview === 'function') window.bpTrackCartQuickview(productUrl); 24186 requestAnimationFrame(function () { 24187 try { 24188 if (typeof window.bpOpenProductDrawer === 'function' && choose) window.bpOpenProductDrawer(choose); 24189 else if (choose) choose.click(); 24190 else window.location.href = productUrl; 24191 } catch (e) { window.location.href = productUrl; } 24192 }); 24193 } 24194 24195 // One delegated listener handles Add-to-Bag + dismiss for cards on any surface. 24196 document.addEventListener('click', function (e) { 24197 var addBtn = e.target.closest && e.target.closest('[data-bp-upsell-add]'); 24198 if (addBtn) { 24199 e.preventDefault(); 24200 var addCard = addBtn.closest('[data-bp-cart-upsell]'); 24201 if (addCard) openQuickAdd(addCard.getAttribute('data-upsell-url')); 24202 return; 24203 } 24204 var rmBtn = e.target.closest && e.target.closest('[data-bp-upsell-remove]'); 24205 if (rmBtn) { 24206 e.preventDefault(); 24207 var rmCard = rmBtn.closest('[data-bp-cart-upsell]'); 24208 if (!rmCard) return; 24209 addRemoved(rmCard.getAttribute('data-upsell-id')); 24210 var surface = rmCard.closest(SURFACE_SELECTOR); 24211 rmCard.remove(); 24212 // Surface the next eligible offer on the same surface. 24213 if (surface) render(surface); 24214 } 24215 }); 24216 24217 // Self-wiring: render on ready + whenever the cart changes / a drawer opens / 24218 // the line list is re-rendered. All events are dispatched by the cart surfaces. 24219 ['cart:updated', 'bp:cart-drawer:opened', 'bp:cart-lines:rendered'].forEach(function (ev) { 24220 document.addEventListener(ev, function () { renderAll(); }); 24221 }); 24222 if (document.readyState === 'loading') { 24223 document.addEventListener('DOMContentLoaded', function () { renderAll(); }); 24224 } else { 24225 renderAll(); 24226 } 24227 24228 window.bpBundleUpsell = { 24229 render: render, 24230 renderAll: renderAll, 24231 openQuickAdd: openQuickAdd, 24232 getRemoved: getRemoved, 24233 addRemoved: addRemoved 24234 }; 24235})(); 24236</script>
24236 24237 24238 24239 24240<div class="bp-toast" data-bp-toast role="status" aria-live="polite" hidden> 24241 <span class="bp-toast__msg" data-bp-toast-msg></span> 24242 <button type="button" class="bp-toast__action" data-bp-toast-action hidden></button> 24243 <button type="button" class="bp-toast__close" data-bp-toast-close aria-label="Dismiss"> 24244 <svg width="14" height="14" viewBox="0 0 20 20" fill="none" aria-hidden="true"> 24245 <path d="M5 5l10 10M15 5L5 15" stroke="currentColor" stroke-width="1.6" stroke-linecap="round"/> 24246 </svg> 24247 </button> 24248</div> 24249 24250<style data-shopify> 24251 .bp-toast { 24252 position: fixed; 24253 left: 50%; 24254 bottom: 24px; 24255 transform: translateX(-50%) translateY(8px); 24256 z-index: 9998; 24257 display: flex; 24258 align-items: center; 24259 gap: 16px; 24260 max-width: calc(100vw - 32px); 24261 padding: 14px 16px; 24262 background: var(--bp-color-text, #171609); 24263 color: var(--bp-color-cta-text, #FDFFFA); 24264 font-family: var(--bp-font-body); 24265 font-size: 14px; 24266 font-weight: 500; 24267 line-height: 145%; 24268 letter-spacing: -0.14px; 24269 box-shadow: 0 6px 24px rgb(0 0 0 / 0.18); 24270 opacity: 0; 24271 pointer-events: none; 24272 transition: opacity 0.2s ease, transform 0.2s ease; 24273 } 24274 .bp-toast[hidden] { display: none; } 24275 .bp-toast.is-visible { 24276 opacity: 1; 24277 transform: translateX(-50%) translateY(0); 24278 pointer-events: auto; 24279 } 24280 24281 .bp-toast__msg { flex: 1 1 auto; } 24282 24283 .bp-toast__action { 24284 flex: 0 0 auto; 24285 background: transparent; 24286 border: 0; 24287 padding: 0; 24288 cursor: pointer; 24289 color: inherit; 24290 font: inherit; 24291 text-decoration: underline; 24292 text-underline-offset: 3px; 24293 } 24294 .bp-toast__action[hidden] { display: none; } 24295 .bp-toast__action:hover { opacity: 0.7; } 24296 24297 .bp-toast__close { 24298 flex: 0 0 auto; 24299 background: transparent; 24300 border: 0; 24301 padding: 0; 24302 width: 20px; 24303 height: 20px; 24304 cursor: pointer; 24305 color: inherit; 24306 display: inline-flex; 24307 align-items: center; 24308 justify-content: center; 24309 } 24310 .bp-toast__close:hover { opacity: 0.7; } 24311 24312 @media (prefers-reduced-motion: reduce) { 24313 .bp-toast { 24314 transition: none; 24315 transform: translateX(-50%); 24316 } 24317 .bp-toast.is-visible { transform: translateX(-50%); } 24318 } 24319</style> 24320
24321<script> 24322 (function () { 24323 if (window.bpToast) return; // singleton â safe if rendered twice 24324 24325 const el = document.querySelector('[data-bp-toast]'); 24326 if (!el) return; 24327 const msgEl = el.querySelector('[data-bp-toast-msg]'); 24328 const actionEl = el.querySelector('[data-bp-toast-action]'); 24329 const closeEl = el.querySelector('[data-bp-toast-close]'); 24330 let timer = null; 24331 24332 function hide() { 24333 clearTimeout(timer); 24334 el.classList.remove('is-visible'); 24335 // Wait out the fade before un-rendering so it doesn't vanish 24336 // abruptly. 200ms matches the transition above. 24337 timer = setTimeout(() => { el.hidden = true; }, 200); 24338 } 24339 24340 function show(message, opts) { 24341 const o = opts || {}; 24342 if (!message) return; 24343 clearTimeout(timer); 24344 24345 msgEl.textContent = message; 24346 24347 // Reset the action each time â a previous toast's label/attrs 24348 // must not leak into this one. 24349 actionEl.hidden = true; 24350 actionEl.textContent = ''; 24351 [...actionEl.attributes].forEach((a) => { 24352 if (a.name.indexOf('data-') === 0 && a.name !== 'data-bp-toast-action') { 24353 actionEl.removeAttribute(a.name); 24354 } 24355 }); 24356 24357 if (o.action) { 24358 actionEl.textContent = o.action; 24359 Object.keys(o.actionAttrs || {}).forEach((k) => actionEl.setAttribute(k, o.actionAttrs[k])); 24360 actionEl.hidden = false; 24361 } 24362 24363 el.hidden = false; 24364 // Force a reflow so the transition runs on a freshly-unhidden 24365 // element instead of being skipped. 24366 void el.offsetWidth; 24367 el.classList.add('is-visible'); 24368 24369 const duration = o.duration || 4000; 24370 timer = setTimeout(hide, duration); 24371 } 24372 24373 closeEl.addEventListener('click', hide); 24374 // The action is a one-shot; dismiss after it fires. The click still 24375 // bubbles first, so delegated handlers (e.g. [data-bp-saved-open]) 24376 // run normally. 24377 actionEl.addEventListener('click', () => hide()); 24378 24379 window.bpToast = show; 24380 window.bpToast.hide = hide; 24381 24382 // ---- Save-for-later confirmations ------------------------------- 24383 // `wishlist:item-saved` / `:item-removed` are dispatched by every 24384 // save surface (PDP gallery heart, cart line, saved drawer), so 24385 // wiring here covers them all without touching each one. 24386 // 24387 // "View" carries data-bp-saved-open â bp-saved-for-later-drawer 24388 // opens on a delegated document click for ANY element with that 24389 // attribute, so no extra wiring is needed. 24390 document.addEventListener('wishlist:item-saved', () => { 24391 // Show the toast in-frame too (like the remove toast). Inside the 24392 // Publitas quickview iframe the toast is the VISIBLE surface (the parent 24393 // toast sat BEHIND the Publitas modal â the "background" bug), and its 24394 // "View" click is intercepted by bp-quickview-cart-bridge and relayed to 24395 // the parent, which opens its saved drawer in the top layer above the 24396 // modal (QA 7/27, Dragana â previously the in-frame toast was suppressed 24397 // and the parent's was hidden behind the modal + its View unclickable). 24398 show('Saved for later', { 24399 action: 'View', 24400 actionAttrs: { 'data-bp-saved-open': '' } 24401 }); 24402 }); 24403 24404 document.addEventListener('wishlist:item-removed', () => { 24405 show('Removed from saved items'); 24406 }); 24407 })(); 24408</script>
24408 24409 24410 <style> .n113 {color: grey;} </style> 24411<div id="shopify-block-AeERjbDhiOTdYM000e__16157100011046449464" class="shopify-block shopify-app-block"> 24412 24413 24414 24415 24416 24417 24418 24419 24420 <link rel="stylesheet" href="https://cdn.shopify.com/extensions/01a0c561-16b0-72bc-8987-550a5e565f0a/narvar-extensions-244/assets/shipping-protection-cart-widget.css"> 24421
24421<script src="https://cdn.shopify.com/extensions/01a0c561-16b0-72bc-8987-550a5e565f0a/narvar-extensions-244/assets/shipping-protection-cart-widget.js" defer></script>
24421 24422 24423 24424 24425 24426 <style> 24427 div[data-product-handle="narvardeliveryprotection"] { 24428 display: none !important; 24429} 24430 24431button#CartDrawer-Checkout-DP-With { 24432 letter-spacing: .025em; 24433 --tw-text-opacity: 1; 24434 color: rgb(255 255 255 / var(--tw-text-opacity)); 24435 --tw-bg-opacity: 1; 24436 background-color: rgb(0 0 0 / var(--tw-bg-opacity)); 24437 font-weight: 600; 24438 height: 50px; 24439 font-size:100%; 24440 margin: 0; 24441} 24442button#CartDrawer-Checkout-DP-With:hover { 24443 --tw-bg-opacity: 1; 24444 background-color: rgb(102 102 102 / var(--tw-bg-opacity)); 24445} 24446button#CartDrawer-Checkout-DP-Without { 24447 --tw-text-opacity: 1; 24448 --tw-bg-opacity: 1; 24449 --tw-border-opacity: 1; 24450 letter-spacing: .025em; 24451 color: rgb(0 0 0 / var(--tw-text-opacity)); 24452 font-weight: 600; 24453 height: 50px; 24454 font-size:100%; 24455 border: 1px solid rgb(0 0 0/var(--tw-border-opacity)); 24456 text-transform: capitalize; 24457} 24458 </style> 24459 24460 24461 24462
24463<script> 24464 window.NarvarDPData = { 24465 variant_id: 46514716934310, 24466 metafield: JSON.stringify({"isRetailerActive":true,"US":{"version":9,"protectionType":"insurance","currency":"USD","minimumPremiumValue":3,"taxAdjustment":15,"l10ns":{"en":{"title":"Shipping Protection","description":"Protect your purchase from loss, damage, and theft with InsureShield.","tnc":"","dpLogoUrl":"https://assets.narvar.com/delivery-protection/ups-insureshield.png","privacyPolicyUrl":"","tosUrl":"","footerText":"By selecting coverage, you acknowledge you have read and agree to the","learnMoreText":"Program Terms and Conditions.","learnMoreUrl":"https://www.insureshield.com/content/terms-and-conditions/Narvar_TermsAndConditions.pdf","zeroPremiumText":"Your purchase includes complimentary coverage paid by Boston Proper for lost, damaged, and stolen packages with InsureShield.","merchantName":"Boston Proper"}},"eligibilityRule":{"shippingCountries":["US"],"maxOrderValue":2000},"premiumRule":{"condition":{"excludedCategories":[],"excludedShippingCountries":[]},"premium":{"type":"percentage","value":3}},"zeroPremiumRule":{"condition":{"customerTags":[""]}},"serviceFee":0,"insuranceType":"upsc"}}), 24467 cart_attributes: "", 24468 settings: { 24469 widget_style: "opt-out-button", 24470 btn_with_class_names: "bp-cart-drawer__btn bp-cart-drawer__btn--primary", 24471 btn_without_class_names: "bp-cart-drawer__btn bp-cart-drawer__btn--secondary", 24472 btn_add_to_cart_class_names: "button, add-to-cart-button", 24473 subtotal_selector: "totals__subtotal-value", 24474 cart_count_bubble_selector: "bp-cart-drawer__bubble", 24475 color_accent: "#2563eb", 24476 always_show_widget: true, 24477 show_price_next_to_checkout_btn: true, 24478 show_domain: "www.bostonproper.com", 24479 shop_name: "Boston Proper", 24480 qty_update_selector: "bp-cart-line__qty-input" 24481 } 24482 }; 24483</script>
24483 24484 24485 24486</div><div id="shopify-block-AZkhkQVl0RVZWQVpyZ__13480610564648845780" class="shopify-block shopify-app-block">
24486<script 24487 defer 24488 type="text/javascript" 24489 src="https://szero.narvar.com/packs/js/shop_now.js"></script>
vendor: 1 bytes, line 24489
24489
24490<script> 24491 (function(){ 24492 const scriptEl = document.querySelector('script[src="https://szero.narvar.com/packs/js/shop_now.js"]'); 24493 if(scriptEl){ 24494 scriptEl.addEventListener('load', function(){ 24495 const config = {bannerContainer: "body",checkoutButton: "input[name=\"checkout\"],button[name=\"checkout\"],a[href=\"checkout\"],a[href=\"\/checkout\"]",checkoutForm: "form[action=\"cart\"],form[action=\"\/cart\"],form[action=\"checkout\"],form[action=\"\/checkout\"]",alternativePaymentButton: ".shopify-payment-button,shopify-accelerated-checkout,.cart__dynamic-checkout-buttons.additional-checkout-buttons,shopify-accelerated-checkout-cart,shop-pay-wallet-button,shopify-paypal-button,shopify-google-pay-button,shopify-payment-terms,.rebuy-cart__shop-pay-button",bindCheckoutListener: true, 24496 rebuySmartCart: false, 24497 ampCartDrawer: false, 24498 }; 24499 24500 nv_shopnow.init(config); 24501 24502 const preview = window.Shopify.designMode; 24503 if(preview){ 24504 nv_shopnow.previewBanner(); 24505 } 24506 }); 24507 } 24508 })(); 24509</script>
24509 24510 24511 24512</div><div id="shopify-block-AbGs2R0xnSVR3dGZhd__9271244056961380716" class="shopify-block shopify-app-block">
24512<script src='https://cdn.shopify.com/extensions/01a0f186-e064-7986-a442-4245fc3eb6fb/discount-kit-634/assets/dk_cart_observer.js'></script>
vendor: 1 bytes, line 24512
24512
24513<script type='module' src='https://cdn.shopify.com/extensions/01a0f186-e064-7986-a442-4245fc3eb6fb/discount-kit-634/assets/dk_cart.js'></script>
vendor: 1 bytes, line 24513
24513
24514<script type='module' src='https://cdn.shopify.com/extensions/01a0f186-e064-7986-a442-4245fc3eb6fb/discount-kit-634/assets/dk_gift-selector.js'></script>
24514 24515 24516<!-- BEGIN app snippet: core_script --> 24517 24518 24519 24520 24521 24522 24523 24524
24525<script> 24526 // Initialize DiscountKit namespace 24527 window.discount_kit = window.discount_kit || {}; 24528 24529 // Configuration and data 24530 window.discount_kit.config = { 24531 auto_gift: true, 24532 gift_selector_enabled: false, 24533 gift_quickview: false, 24534 gift_quickview_auto_add: false, 24535 gift_quickview_change: false, 24536 gift_quickview_change_label: "Change gifts", 24537 gift_quickview_change_style: "theme", 24538 message: "You've unlocked a free gift! Make your selections now.", 24539 add_to_cart: "Add to cart", 24540 show_on_cart_page: false, 24541 stop_showing_after: 0, 24542 prevent_readd_removed_gifts: true, 24543 refresh_on_cart_change: false, 24544 custom_cart_update_event: "discount_kit:cart_changed", 24545 cart: {"note":null,"attributes":{},"original_total_price":0,"total_price":0,"total_discount":0,"total_weight":0.0,"item_count":0,"items":[],"requires_shipping":false,"currency":"USD","items_subtotal_price":0,"cart_level_discount_applications":[],"checkout_charge_amount":0}, 24546 collections: [], 24547 compare_at_prices: [], 24548 conversion_rate: 1.0, 24549 discounts: [{"id": "2395617853606", 24550 "discountType": "GWP", 24551 "discountTitle": "OCTOBERSCARF", 24552 "startsAt": null, 24553 "endsAt": null, 24554 "discountNode": { "config": { "value": {"allowB2b":false,"appliesOncePerCustomer":false,"cartAttribute":null,"currencyCode":null,"customerOperator":"oneOf","customerTags":null,"discountTitle":"OCTOBERSCARF","discountType":"GWP","uuid":"960e25b1-9805-475d-bab9-7aa982339b73","locationIds":null,"locationOperator":"oneOf","marketHandles":null,"marketOperator":"oneOf","matchRule":{"all":true,"type":"product","include":[],"selection":[],"exclude":[],"excludeSale":false,"purchaseType":"both"},"onlyB2b":false,"requireGiftProperty":true,"test":false,"tiers":[{"message":null,"messageTranslations":null,"buys":{"type":"minimumSubtotal","value":40000},"gets":{"type":"percentage","value":100,"targetQuantity":1,"matchRule":{"all":false,"type":"productSelection","include":[],"selection":[{"id":"
2455415286962618534","variants":null}],"exclude":[],"excludeSale":false}}}],"usageLimit":null} } }},{"id": "2396067692710", 24555 "discountType": "CUSTOM", 24556 "discountTitle": "Winter Sweater Promo", 24557 "startsAt": null, 24558 "endsAt": null, 24559 "discountNode": { "config": { "value": {"allowB2b":false,"allowB2B":false,"onlyB2b":false,"appliesOncePerCustomer":false,"attributeKey":"_discount_kit","bucketRules":[{"name":"ALL_RESERVED"},{"name":"Ribbed Sweaters: winter","collectionFilter":[693294661798],"cfp":[0]}],"cartAttribute":null,"currencyCode":null,"customerOperator":"oneOf","customerTags":null,"discountApplicationStrategy":"MAXIMUM","discountRules":[{"message":"Save $25 on 2","messageTranslations":null,"recurring":false,"requireAllTargets":false,"discountValue":{"fixedAmount":{"amount":"25.00","appliesToEachItem":false}},"maxRecurrences":null,"currency":null,"fixedPrice":null,"targetParts":[{"bucket":"Ribbed Sweaters: winter","quantity":null,"targetSelectionStrategy":"cheapest_first"}
24559],"useCompareAtPrice":false,"useMetafieldPrice":false,"conditionParts":[{"bucket":"Ribbed Sweaters: winter","quantity":2,"total":null,"uniqueProducts":null,"uniqueVariants":null,"operator":"equal","order":0}]},{"message":"Save $45 on 3","messageTranslations":null,"recurring":false,"requireAllTargets":false,"discountValue":{"fixedAmount":{"amount":"45.00","appliesToEachItem":false}},"maxRecurrences":null,"currency":null,"fixedPrice":null,"targetParts":[{"bucket":"Ribbed Sweaters: winter","quantity":null,"targetSelectionStrategy":"cheapest_first"}],"useCompareAtPrice":false,"useMetafieldPrice":false,"conditionParts":[{"bucket":"Ribbed Sweaters: winter","quantity":3,"total":null,"uniqueProducts":null,"uniqueVariants":null,"operator":"equal","order":0}]}],"discountTitle":"Winter Sweater Promo","discountType":"CUSTOM","fillingStrategy":"all","locationIds":null,"locationOperator":"oneOf","marketHandles":null,"marketOperator":"oneOf","metafieldKey":"","metafieldNamespace":"","useMetafieldDiscounts":false,"orderDiscountApplicationStrategy":"MAXIMUM","orderDiscountRules":[],"shippingDiscountApplicationStrategy":"ALL","shippingDiscountRules":[],"test":false,"usageLimit":null,"uuid":"81d72569-a154-408d-b1e6-bd1f285fcbcf","consume":false} } }}] 24560 }; 24561 24562 // Gift products for GWP 24563 window.discount_kit.gift_products = []; 24564 24565 24566 24567 window.discount_kit.gift_products.push({ 24568 product: {"id":15286962618534,"title":"Boston Proper Printed Scarf With Love BP","handle":"boston-proper-printed-scarf-with-love-bp","description":null,"published_at":"2026-09-30T03:06:19-04:00","created_at":"2026-04-16T02:30:04-04:00","vendor":"Boston Proper","type":"Promotions","tags":["2CT80","2CT8071","799","Active","Charmeuse","Fashion","Floral","Full Price","INV5_2CT80","Miscellaneous","N\/A","Non-Apparel","nvgroup_2CT80","Pink","Promotions","Published: 20261001","rtly-gc:offer-free-shipping","Scarves","Winter 26","Work","Woven","YGroup_2CT80"],"price":5000,"price_min":5000,"price_max":5000,"available":true,"price_varies":false,"compare_at_price":5000,"compare_at_price_min":5000,"compare_at_price_max":5000,"compare_at_price_varies":false,"variants":[{"id":56389031657638,"title":"With Love BP \/ One Size","option1":"With Love BP","option2":"One Size","option3":null,"sku":"2CT8071000","requires_shipping":true,"taxable":true,"featured_image":{"id":80304586948774,"product_id":15286962618534,"position":1,"created_at":"2026-10-03T02:33:16-04:00","updated_at":"2026-10-03T02:33:18-04:00","alt":null,"width":1639,"height":2048,"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2CT8071_SET_e6f3e3ce-5a5f-451b-bc3b-ea1f8f71d236.jpg?v=1791009198","variant_ids":[56389031657638]},"available":true,"name":"Boston Proper Printed Scarf With Love BP - With Love BP \/ One Size","public_title":"With Love BP \/ One Size","options":["With Love BP","One Size"],"price":5000,"weight":0,"compare_at_price":5000,"inventory_management":"shopify","barcode":null,"featured_media":{"alt":null,"id":70645919449254,"position":1,"preview_image":{"aspect_ratio":0.8,"height":2048,"width":1639,"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2CT8071_SET_e6f3e3ce-5a5f-451b-bc3b-ea1f8f71d236.jpg?v=1791009198"}},"requires_selling_plan":false,"selling_plan_allocations":[],"quantity_rule":{"min":1,"max":null,"increment":1}}],"images":["\/\/www.bostonproper.com\/cdn\/shop\/files\/2CT8071_SET_e6f3e3ce-5a5f-451b-bc3b-ea1f8f71d236.jpg?v=1791009198","\/\/www.bostonproper.com\/cdn\/shop\/files\/2_af52291f-802d-4184-8815-c64ea15071dd.jpg?v=1791009198"],"featured_image":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2CT8071_SET_e6f3e3ce-5a5f-451b-bc3b-ea1f8f71d236.jpg?v=1791009198","options":["Color","Size"],"media":[{"alt":null,"id":70645919449254,"position":1,"preview_image":{"aspect_ratio":0.8,"height":2048,"width":1639,"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2CT8071_SET_e6f3e3ce-5a5f-451b-bc3b-ea1f8f71d236.jpg?v=1791009198"},"aspect_ratio":0.8,"height":2048,"media_type":"image","src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2CT8071_SET_e6f3e3ce-5a5f-451b-bc3b-ea1f8f71d236.jpg?v=1791009198","width":1639},{"alt":null,"id":70645919482022,"position":2,"preview_image":{"aspect_ratio":0.8,"height":2048,"width":1639,"src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2_af52291f-802d-4184-8815-c64ea15071dd.jpg?v=1791009198"},"aspect_ratio":0.8,"height":2048,"media_type":"image","src":"\/\/www.bostonproper.com\/cdn\/shop\/files\/2_af52291f-802d-4184-8815-c64ea15071dd.jpg?v=1791009198","width":1639}],"requires_selling_plan":false,"selling_plan_groups":[],"content":null}, 24569 options_with_values: [{"name":"Color","position":1,"values":["With Love BP"]},{"name":"Size","position":2,"values":["One Size"]}] 24570 }); 24571 24572 24573</script>
24573 24574
24575<script 24576 type='module' 24577 src='https://cdn.shopify.com/extensions/01a0f186-e064-7986-a442-4245fc3eb6fb/discount-kit-634/assets/dk_core.js' 24578></script>
24578 24579<!-- END app snippet --> 24580 24581 24582 24583 24584</div>
24584<script src="https://cdn.shopify.com/storefront/standard-actions.js" type="module" data-source-attribution="shopify.standard_actions"></script>
24584 24585</body> 24586</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.