UNPKG

@liquidcommerceteam/elements-sdk

Version:

LiquidCommerce Elements SDK

1 lines 222 kB
!function(n,t){"object"==typeof exports&&"undefined"!=typeof module?t(exports):"function"==typeof define&&define.amd?define(["exports"],t):t((n="undefined"!=typeof globalThis?globalThis:n||self).LiquidCommerceElements={})}(this,(function(n){"use strict";var t,e,i,s,r,o,a,c;!function(n){n.LOCAL="local",n.DEVELOPMENT="development",n.STAGING="staging",n.PRODUCTION="production"}(t||(t={})),function(n){n.PRODUCT_LOADED="productLoaded",n.PRODUCT_QUANTITY_INCREASE="productQuantityIncrease",n.PRODUCT_QUANTITY_DECREASE="productQuantityDecrease",n.PRODUCT_ADD_TO_CART="productAddToCart",n.CART_INITIALIZED="cartInitialized",n.CART_CLOSED="cartClosed",n.CART_OPENED="cartOpened",n.CART_UPDATED="cartUpdated",n.ADDRESS_UPDATED="addressUpdated",n.CHECKOUT_INITIALIZED="checkoutInitialized",n.CHECKOUT_OPENED="checkoutOpened",n.CHECKOUT_CLOSED="checkoutClosed",n.CHECKOUT_UPDATED="checkoutUpdated"}(e||(e={})),function(n){n.SHIPPING_FIRST_NAME_INPUT="shippingFirstNameInput",n.SHIPPING_LAST_NAME_INPUT="shippingLastNameInput",n.SHIPPING_PHONE_INPUT="shippingPhoneInput",n.SHIPPING_EMAIL_INPUT="shippingEmailInput"}(i||(i={})),function(n){n.ADDRESS="address",n.DRAWER="drawer",n.MODAL="modal",n.LOADING="loading",n.COLLAPSIBLE="collapsible",n.INPUT="input",n.PRODUCT="product",n.PRODUCT_IMAGE_CAROUSEL="product-image-carousel",n.PRODUCT_OPTIONS="product-options",n.PRODUCT_INTERACTIONS="product-interactions",n.PRODUCT_RETAILERS="product-retailers",n.PRODUCT_PRICE="product-price",n.PRODUCT_ADD_TO_CART_SECTION="product-add-to-cart-section",n.PRODUCT_ENGRAVING="product-engraving",n.CART="cart",n.CART_RETAILER="cart-retailer",n.CART_ITEM="cart-item",n.CART_ITEM_ENGRAVING="cart-item-engraving",n.CART_FOOTER="cart-footer",n.CART_FOOTER_PRICE="cart-footer-price",n.CART_ITEM_QUANTITY_PRICE="cart-item-quantity-price",n.CART_RETAILER_PROGRESS_BAR="cart-retailer-progress-bar",n.CART_RETAILER_SUBTOTAL="cart-retailer-subtotal",n.CHECKOUT="checkout",n.CHECKOUT_SHIPPING_INFORMATION="checkout-shipping-information",n.CHECKOUT_PAYMENT_FORM="checkout-payment-form",n.CHECKOUT_BUYERS_INFO_FORM="checkout-buyers-info-form"}(s||(s={})),function(n){n.ON_DEMAND="onDemand",n.SHIPPING="shipping"}(r||(r={})),function(n){n.OOS="OutOfStock",n.ITEMS_NOT_ADDED="ItemsNotAdded",n.ITEMS_REQUESTED_NOT_ADDED="ItemsRequestedNotAdded",n.ITEM_NOT_ENGRAVED="ItemEngravingError",n.ADDRESS_CHANGE="AddressChange",n.LOCATION_AVAILABILITY="LocationAvailability",n.PARTNER_PRODUCT_CONFIGS="PartnerProductConfigs",n.REMOVED_EXISTING_ITEMS="RemovedExistingCartItems",n.RETAILER_MIN="RetailerMinNotMet",n.NO_ITEMS_IN_CART="NoItemsInCart",n.INVALID_ID="InvalidId",n.NO_ID="NoId",n.CART_CHECKOUT_PROCESSED="CartCheckoutProcessed",n.NEW_CART="NewCart",n.DEFAULT="CartError",n.ITEM_QTY_CHANGE="ItemQuantityChange",n.ITEM_ID_NOT_FOUND="ItemIdNotFound",n.ITEMS_REMOVED="ItemsRemoved",n.COUPON_PROCESSING_ERROR="CouponProcessingError",n.COUPON_NOT_FOUND="CouponNotFound",n.COUPON_EXPIRED="CouponExpired",n.NO_APPLICABLE_DISCOUNT="NoApplicableDiscount",n.COUPON_NOT_STARTED="CouponNotStarted",n.MINIMUM_ORDER_VALUE_NOT_MET="MinimumOrderValueNotMet",n.MINIMUM_ORDER_UNITS_NOT_MET="MinimumOrderUnitsNotMet",n.MINIMUM_DISTINCT_ITEMS_NOT_MET="MinimumDistinctItemsNotMet",n.QUOTA_EXCEEDED="QuotaExceeded",n.USER_LIMIT_EXCEEDED="UserLimitExceeded",n.NOT_FIRST_PURCHASE="NotFirstPurchase",n.INVALID_COUPON="InvalidCoupon",n.INVALID_MEMBERSHIP="InvalidMembership",n.INVALID_DOMAIN="InvalidDomain",n.INVALID_REQUIREMENTS="InvalidRequirements",n.INVALID_ORGANIZATION="InvalidOrganization",n.PRODUCT_NOT_ELIGIBLE="ProductNotEligible",n.NOT_ENOUGH_PREVIOUS_ORDERS="NotEnoughPreviousOrders",n.PRESALE_ITEMS_NOT_ALLOWED="PresaleItemsNotAllowed",n.PRESALE_LIMIT_EXCEEDED="PresaleLimitExceeded",n.PRESALE_NOT_STARTED="PresaleNotStarted",n.PRESALE_EXPIRED="PresaleExpired",n.PRESALE_MIXED_CART="PresaleMixedCart"}(o||(o={})),function(n){n.ERROR_PROCESSING_GIFT_CARDS="ErrorProcessingGiftCards",n.INVALID_GIFT_CARD_CODE="InvalidGiftCardCodes",n.INVALID_GIFT_CARD_PARTNER="InvalidGiftCardPartner",n.INACTIVE_GIFT_CARD="InactiveGiftCard",n.GIFT_CARD_ALREADY_IN_USE="GiftCardAlreadyInUse",n.GIFT_CARD_EXPIRED="GiftCardExpired",n.GIFT_CARD_BALANCE_DEPLETED="GiftCardBalanceDepleted"}(a||(a={})),function(n){n.SHIPPING="shipping",n.BILLING="billing"}(c||(c={}));class l{static getInstance(n,t){this.instances.has(n)||this.instances.set(n,t());const e=this.instances.get(n);if(!e)throw new Error(`ElementsSdk: Instance for class ${n} could not be created.`);return e}}l.instances=new Map;class d{constructor(){this.client=null}static getInstance(){return l.getInstance("ApiClientService",(()=>new d))}async setClient(n){this.client=n}async getGlobalConfigs(){try{if(!this.client)throw new Error("API client is not initialized");const n=await this.client.get("/elements/config");if(200!==n.statusCode)throw console.error(n),new Error(`Failed to fetch global configs: ${n.message}`);return n.data}catch(n){throw console.error("Error fetching configs:",n),n}}async getProductData(n,t){try{if(!this.client)throw new Error("API client is not initialized");if(!n)throw new Error("Identifier is required to fetch product data");const e=await this.client.post("/cloud/catalog/availability",{upcs:[n],grouping:[n],loc:t,shouldShowOffHours:!0});if(200!==e.statusCode||!e.products||!e.products[0])throw console.error(e),new Error(`Failed to fetch product data: ${e.message}`);return{product:e.products[0],retailers:e.retailers}}catch(n){throw console.error("Error fetching product data:",n),n}}async getCartData(n){try{if(!this.client)throw new Error("API client is not initialized");const t=await this.client.get("/cloud/cart"+(n?`?id=${n}`:""));if(200!==t.statusCode)throw console.error(t),new Error(`Failed to fetch product data: ${t.message}`);return t.cart}catch(n){throw console.error("Error fetching product data:",n),n}}async updateCart(n,t){try{if(!this.client)throw new Error("API client is not initialized");const e=await this.client.put("/cloud/cart",{...n,loc:t});if(200!==e.statusCode)throw console.error(e),new Error(`Failed to fetch product data: ${e.message}`);return e.cart}catch(n){throw console.error("Error fetching product data:",n),n}}async getAddressSuggestions(n){try{if(!this.client)throw new Error("API client is not initialized");const t=await this.client.get(`/cloud/address/autocomplete?input=${n}`);if(200!==t.statusCode)throw console.error(t),new Error(`Failed to fetch address suggestions: ${t.message}`);return t.data}catch(n){throw console.error("Error fetching address suggestions:",n),n}}async getAddressDetails(n){try{if(!this.client)throw new Error("API client is not initialized");const t=await this.client.get(`/cloud/address/details/${n}`);if(200!==t.statusCode)throw console.error(t),new Error(`Failed to fetch address details: ${t.message}`);return t.data}catch(n){throw console.error("Error fetching address details:",n),n}}async prepareCheckout(n){try{if(!this.client)throw new Error("API client is not initialized");const t=await this.client.post("/cloud/checkout/prepare",n);if(200!==t.statusCode)throw console.error(t),new Error(`Failed to fetch prepare checkout data: ${t.message}`);return t.checkout}catch(n){throw console.error("Error fetching prepare checkout data:",n),n}}async getUserSession(n){try{if(!this.client)throw new Error("API client is not initialized");const t=await this.client.post("/cloud/user/session",n);if(201!==t.statusCode)throw console.error(t),new Error(`Failed to fetch user session: ${t.message}`);return t.data}catch(n){throw console.error("Error fetching user session:",n),n}}async confirmPaymentSession(n,t){try{if(!this.client)throw new Error("API client is not initialized");const e=await this.client.post("/cloud/payment/confirmSession",{sessionSecret:n,paymentMethodId:t});if(201!==e.statusCode)throw console.error(e),new Error(`Failed to confirm session: ${e.message}`);return e.data}catch(n){throw console.error("Error confirming session:",n),n}}async checkoutComplete(n){try{if(!this.client)throw new Error("API client is not initialized");const t=await this.client.post("/cloud/checkout/complete",n);if(201!==t.statusCode)throw console.error(t),new Error(`Failed to complete checkout: ${t.message}`);return t.order.legacyOrderNumber}catch(n){throw console.error("Error completing checkout:",n),n}}}class h{constructor(){this.baseStyleSheet=new CSSStyleSheet,this.styleSheets=new Map,this.productGroup=[s.PRODUCT,s.PRODUCT_OPTIONS,s.PRODUCT_RETAILERS,s.PRODUCT_ADD_TO_CART_SECTION,s.PRODUCT_INTERACTIONS,s.PRODUCT_IMAGE_CAROUSEL],this.addressGroup=[s.ADDRESS],this.checkoutGroup=[s.CHECKOUT,s.CHECKOUT_SHIPPING_INFORMATION],this.cartGroup=[s.CART,s.CART_FOOTER,s.CART_FOOTER_PRICE,s.CART_ITEM,s.CART_ITEM_ENGRAVING,s.CART_RETAILER,s.CART_RETAILER_PROGRESS_BAR],this.uiGroup=[s.COLLAPSIBLE,s.DRAWER,s.INPUT,s.LOADING,s.MODAL]}static getInstance(){return l.getInstance("ThemeProviderService",(()=>new h))}initialize(n){this.data=n,this.generateBaseStyles(),this.generateComponentStyles()}updateAllConfigs(n){this.data=this.deepMerge(this.data,n),this.updateSpecificCSSVariablesFromConfig(this.baseStyleSheet,n,"global",":root")}updateProductConfig(n){const t=this.getProductConfig(),e=this.deepMerge(t,n);this.updateComponentInData("pdp",e);const i=this.styleSheets.get(s.PRODUCT);i&&this.updateSpecificCSSVariablesFromConfig(i,n,"product",":host")}updateAddressConfig(n){const t=this.getAddressConfig(),e=this.deepMerge(t,n);this.updateComponentInData("location",e);const i=this.styleSheets.get(s.ADDRESS);i&&this.updateSpecificCSSVariablesFromConfig(i,n,"address",":host")}updateCartConfig(n){const t=this.getCartConfig(),e=this.deepMerge(t,n);this.updateComponentInData("cart",e);const i=this.styleSheets.get(s.CART);i&&this.updateSpecificCSSVariablesFromConfig(i,n,"cart",":host")}updateCheckoutConfig(n){const t=this.getCheckoutConfig(),e=this.deepMerge(t,n);this.updateComponentInData("checkout",e);const i=this.styleSheets.get(s.CHECKOUT);i&&this.updateSpecificCSSVariablesFromConfig(i,n,"checkout",":host")}getConfigs(){return this.data}getProductConfig(){return this.data.components.find((n=>"pdp"===n.type))}getCartConfig(){return this.data.components.find((n=>"cart"===n.type))}getCheckoutConfig(){return this.data.components.find((n=>"checkout"===n.type))}getAddressConfig(){return this.data.components.find((n=>"location"===n.type))}getComponentConfig(n){return this.productGroup.includes(n)?this.getProductConfig():this.addressGroup.includes(n)?this.getAddressConfig():this.checkoutGroup.includes(n)?this.getCheckoutConfig():this.cartGroup.includes(n)?this.getCartConfig():void 0}updateSpecificCSSVariablesFromConfig(n,t,e,i){if(t&&0!==Object.keys(t).length)try{const s={};if(this.extractCSSVariables(t,e,s),0===Object.keys(s).length)return void console.log(`No CSS variables to update for ${e}`);let r=null;for(const t of Array.from(n.cssRules))if(t instanceof CSSStyleRule&&t.selectorText===i){r=t;break}if(!r){const t=n.insertRule(`${i} {}`);r=n.cssRules[t]}for(const[n,t]of Object.entries(s))r.style.setProperty(n,String(t))}catch(n){console.warn(`Failed to update CSS variables for ${e}:`,n)}}createCSSVariables(n,t,e=""){const i={};return this.extractCSSVariables(n,t,i,e),Object.entries(i).map((([n,t])=>` ${n}: ${t};`)).join("\n")}extractCSSVariables(n,t,e,i=""){if(n&&"object"==typeof n){console.groupCollapsed("🚫 Rejected CSS Variables for:",i||t);for(const[s,r]of Object.entries(n)){const n=i?`${i}.${s}`:s;r&&"object"==typeof r&&!Array.isArray(r)?this.extractCSSVariables(r,t,e,n):this.isCSSValue(r)?e[this.pathToCSSVariable(n,t)]=r:console.log(`${t}.${n} = ${r} (${typeof r})`)}console.groupEnd()}}pathToCSSVariable(n,t){return`--${t}-${n.replace(/\./g,"-").replace(/[A-Z]/g,(n=>`-${n.toLowerCase()}`))}`}isCSSValue(n){return"string"==typeof n?/^(#[0-9a-f]{3,8}|rgb|rgba|hsl|hsla|\d+px|\d+%|\d+rem|\d+em|auto|none|inherit|initial|unset|rounded|squared)/.test(n.toLowerCase())||/^[a-z-]+$/.test(n):"number"==typeof n}generateComponentStylesWithVariables(n,t,e=[]){const i=n?this.createCSSVariables(n,t):"",s=e.length>0?e.join("\n\n"):"";return i?`:host {\n${i}\n}\n\n${s}`:s}generateBaseStyles(){const n=`\n :root {\n ${this.createCSSVariables(this.data,"global")}\n }\n\n \n .w-full { width: 100%; }\n .max-w-full { max-width: 100%; }\n .flex { display: flex; }\n .flex-col { flex-direction: column; }\n .flex-row { flex-direction: row; }\n .flex-wrap { flex-wrap: wrap; }\n .flex-nowrap { flex-wrap: nowrap; }\n .gap-2 { gap: 10px; }\n .items-center { align-items: center; }\n .items-start { align-items: flex-start; }\n .items-end { align-items: flex-end; }\n .justify-between { justify-content: space-between; }\n .justify-center { justify-content: center; }\n .justify-start { justify-content: flex-start; }\n .justify-end { justify-content: flex-end; }\n .gap-xs { gap: 4px; }\n .gap-sm { gap: 8px; }\n .gap-md { gap: 12px; }\n .gap-lg { gap: 20px; }\n .gap-xl { gap: 40px; }\n .m-0 { margin: 0; }\n .p-xs { padding: 4px; }\n .p-sm { padding: 8px; }\n .p-md { padding: 12px; }\n .rounded-sm { border-radius: 4px}\n .rounded { border-radius: 8px; }\n .rounded-lg { border-radius: 12px; }\n .rounded-xl { border-radius: 20px; }\n .cursor-pointer { cursor: pointer; }\n .transition { transition: all 0.2s ease-in-out; }\n .hidden { display: none; }\n .overflow-hidden { overflow: hidden; }\n .border { border: 1px solid; }\n .shadow-sm { box-shadow: 0 1px 2px 0 rgba(0, 0, 0, 0.05); }\n .shadow { box-shadow: 0 4px 6px -1px rgba(0, 0, 0, 0.1); }\n\n div.divider {\n border: none;\n border-top: 1px solid #c1c1c1;\n margin: 10px 0;\n width: 100%;\n }\n\n .btn {\n padding: 5px;\n border: none;\n border-radius: 4px;\n cursor: pointer;\n font-weight: 500;\n font-size: 16px;\n }\n\n .btn-primary {\n width: 100%;\n border-radius: 8px;\n min-width: 180px;\n display: flex;\n justify-content: center;\n align-items: center;\n gap: 8px;\n padding: 8px 0;\n }\n\n html, body, div, span, applet, object, iframe,\n h1, h2, h3, h4, h5, h6, p, blockquote, pre,\n a, abbr, acronym, address, big, cite, code,\n del, dfn, em, img, ins, kbd, q, s, samp,\n small, strike, strong, sub, sup, tt, var,\n b, u, i, center,\n dl, dt, dd, ol, ul, li,\n fieldset, form, label, legend,\n table, caption, tbody, tfoot, thead, tr, th, td,\n article, aside, canvas, details, embed,\n figure, figcaption, footer, header, hgroup,\n menu, nav, output, ruby, section, summary,\n time, mark, audio, video {\n margin: 0;\n padding: 0;\n border: 0;\n font-size: 100%;\n font: inherit;\n vertical-align: baseline;\n }\n\n *, :after, :before {\n box-sizing: border-box;\n }\n\n article, aside, details, figcaption, figure,\n footer, header, hgroup, menu, nav, section {\n display: block;\n }\n body {\n line-height: 1;\n }\n ol, ul {\n list-style: none;\n }\n blockquote, q {\n quotes: none;\n }\n blockquote:before, blockquote:after,\n q:before, q:after {\n content: '';\n content: none;\n }\n table {\n border-collapse: collapse;\n border-spacing: 0;\n }\n\n .custom-input-container {\n margin-bottom: 1rem;\n }\n\n .custom-input-container input {\n width: 100%;\n padding: 0.75rem;\n border: 2px solid #e5e7eb;\n border-radius: 0.375rem;\n font-size: 1rem;\n transition: border-color 0.2s ease-in-out;\n }\n\n .custom-input-container input:focus {\n outline: none;\n border-color: #3b82f6;\n box-shadow: 0 0 0 3px rgba(59, 130, 246, 0.1);\n }\n\n .custom-input-container input.input-error {\n border-color: #ef4444;\n }\n\n .custom-input-container .error-container {\n margin-top: 0.25rem;\n }\n\n .custom-input-container .error-message {\n color: #ef4444;\n font-size: 0.875rem;\n margin-bottom: 0.25rem;\n }\n\n\n ${["\n .drawer-container {\n position: relative;\n }\n\n .drawer-backdrop {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100dvh;\n background-color: rgba(0, 0, 0, 0.4);\n z-index: 999;\n opacity: 0;\n visibility: hidden;\n transition: opacity 0.3s ease, visibility 0.3s ease;\n }\n\n .drawer-backdrop.visible {\n opacity: 1;\n visibility: visible;\n }\n\n .drawer {\n height: 100dvh;\n width: 0; /* Start closed */\n position: fixed;\n z-index: 1000;\n top: 0;\n right: 0; /* Position from right instead of left */\n background-color: #ffffff;\n overflow-x: hidden;\n transition: width 0.3s ease;\n box-shadow: -2px 0 8px rgba(0, 0, 0, 0.1);\n display: flex;\n flex-direction: column;\n }\n\n /* When drawer is open - Desktop */\n .drawer-container.open .drawer {\n width: 450px; /* Width when open */\n }\n\n /* Header section */\n .drawer .header {\n padding: 16px;\n border-bottom: 1px solid #eaeaea;\n display: flex;\n justify-content: space-between;\n align-items: center;\n flex-shrink: 0; /* Prevent header from shrinking */\n }\n\n .drawer .header h2 {\n margin: 0;\n font-size: 18px;\n font-weight: 600;\n color: #333333;\n }\n\n .drawer .close-button {\n background: transparent;\n border: none;\n font-size: 24px;\n line-height: 1;\n color: #666666;\n cursor: pointer;\n padding: 0;\n display: flex;\n align-items: center;\n justify-content: center;\n height: 32px;\n width: 32px;\n border-radius: 4px;\n transition: background-color 0.2s ease;\n }\n\n .drawer .close-button:hover {\n background-color: #f5f5f5;\n color: #333333;\n }\n\n /* Body section */\n .drawer .body {\n flex: 1;\n overflow: hidden;\n }\n\n /* Footer section */\n .drawer .footer {\n padding: 12px 16px;\n border-top: 1px solid #eaeaea;\n display: flex;\n align-items: center;\n justify-content: center;\n font-size: 12px;\n color: #666666;\n flex-shrink: 0; /* Prevent footer from shrinking */\n }\n\n .drawer .footer p {\n margin: 0;\n }\n\n .drawer .footer span {\n display: flex;\n align-items: center;\n }\n\n /* Tablet adjustments */\n @media (max-width: 768px) {\n .drawer-container.open .drawer {\n width: 380px; /* Slightly smaller on tablets */\n }\n }\n\n /* Mobile adjustments */\n @media (max-width: 576px) {\n .drawer-container.open .drawer {\n width: 100% !important; /* Full width on mobile - using !important to ensure it overrides */\n }\n\n /* Adjust padding on mobile for better spacing */\n .drawer .header {\n padding: 12px 16px;\n }\n\n .drawer .footer {\n padding: 10px 16px;\n }\n }\n\n /* Small mobile devices */\n @media (max-width: 480px) {\n .drawer .header h2 {\n font-size: 16px; /* Slightly smaller title on very small screens */\n }\n\n .drawer .close-button {\n height: 28px;\n width: 28px;\n font-size: 20px;\n }\n }\n\n /* Landscape mobile orientation */\n @media (max-height: 500px) and (orientation: landscape) {\n .drawer .header {\n padding: 8px 16px; /* Reduce vertical padding in landscape */\n }\n\n .drawer .footer {\n padding: 6px 16px;\n }\n }\n\n\n /* Smooth transitions for different screen sizes */\n @media (prefers-reduced-motion: reduce) {\n .drawer,\n .drawer-backdrop {\n transition: none;\n }\n }\n ",'\n .modal-container {\n position: fixed;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n z-index: 1000;\n display: flex;\n align-items: center;\n justify-content: center;\n opacity: 0;\n visibility: hidden;\n transition: opacity 0.3s ease, visibility 0.3s ease;\n }\n\n .modal-container.open {\n opacity: 1;\n visibility: visible;\n }\n\n /* Container relative positioning for contained modals */\n .modal-container.container-relative {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n z-index: 1000; /* Relative to the container, not the entire page */\n }\n\n .modal-backdrop {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n background-color: rgba(0, 0, 0, 0.5);\n opacity: 0;\n transition: opacity 0.3s ease;\n }\n\n .modal-backdrop.visible {\n opacity: 1;\n }\n\n /* When contained, backdrop should only cover the container */\n .modal-container.container-relative .modal-backdrop {\n position: absolute;\n top: 0;\n left: 0;\n width: 100%;\n height: 100%;\n }\n\n .modal-content {\n position: relative;\n background: white;\n border-radius: 8px;\n box-shadow: 0 10px 25px rgba(0, 0, 0, 0.2);\n width: auto;\n max-width: 90vw;\n max-height: 90vh;\n overflow: auto;\n transform: scale(0.9) translateY(-20px);\n transition: transform 0.3s ease;\n z-index: 1001;\n }\n\n .modal-container.open .modal-content {\n transform: scale(1) translateY(0);\n }\n\n .modal-body {\n padding: 0;\n }\n\n /* Position variants */\n .modal-container.position-top {\n align-items: flex-start;\n padding-top: 5vh;\n }\n\n .modal-container.position-bottom {\n align-items: flex-end;\n padding-bottom: 5vh;\n }\n\n .modal-container.position-left {\n justify-content: flex-start;\n padding-left: 5vw;\n }\n\n .modal-container.position-right {\n justify-content: flex-end;\n padding-right: 5vw;\n }\n\n /* Engraving Modal Specific Styles */\n .engraving-modal-content {\n padding: 24px;\n width: 100%;\n max-width: 500px;\n min-width: 320px;\n }\n\n .engraving-modal-header {\n margin-bottom: 24px;\n text-align: center;\n }\n\n .engraving-modal-header h3 {\n margin: 0 0 8px 0;\n font-size: 24px;\n font-weight: 600;\n color: #1a1a1a;\n }\n\n .engraving-modal-header p {\n margin: 4px 0;\n color: #666;\n font-size: 14px;\n }\n\n .engraving-fee {\n font-weight: 600;\n color: #2563eb;\n }\n\n .engraving-form {\n margin-bottom: 24px;\n }\n\n .engraving-input-group {\n margin-bottom: 16px;\n }\n\n .engraving-input-group label {\n display: block;\n margin-bottom: 4px;\n font-weight: 500;\n color: #374151;\n }\n\n .engraving-input-group input {\n width: 100%;\n padding: 8px 12px;\n border: 1px solid #d1d5db;\n border-radius: 4px;\n font-size: 14px;\n transition: border-color 0.2s ease;\n box-sizing: border-box;\n }\n\n .engraving-input-group input:focus {\n outline: none;\n border-color: #2563eb;\n box-shadow: 0 0 0 3px rgba(37, 99, 235, 0.1);\n }\n\n .char-count {\n display: block;\n margin-top: 4px;\n font-size: 12px;\n color: #6b7280;\n text-align: right;\n }\n\n .engraving-preview-section {\n margin-top: 20px;\n padding: 16px;\n background-color: #f9fafb;\n border-radius: 6px;\n }\n\n .engraving-preview-section h4 {\n margin: 0 0 12px 0;\n font-size: 16px;\n font-weight: 500;\n color: #374151;\n }\n\n .bottle-preview {\n display: flex;\n justify-content: center;\n align-items: center;\n min-height: 80px;\n background-color: white;\n border: 2px dashed #d1d5db;\n border-radius: 4px;\n }\n\n .engraving-preview-text {\n text-align: center;\n font-family: serif;\n font-style: italic;\n }\n\n .preview-line-1,\n .preview-line-2 {\n font-size: 14px;\n color: #374151;\n min-height: 20px;\n line-height: 1.4;\n }\n\n .preview-line-1 {\n font-weight: 600;\n }\n\n .engraving-modal-actions {\n display: flex;\n gap: 12px;\n justify-content: flex-end;\n flex-wrap: wrap;\n }\n\n .engraving-modal-actions button {\n padding: 8px 16px;\n border: none;\n border-radius: 4px;\n font-size: 14px;\n font-weight: 500;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n\n .btn-primary {\n background-color: #2563eb;\n color: white;\n }\n\n .btn-primary:hover {\n background-color: #1d4ed8;\n }\n\n .btn-secondary {\n background-color: #f3f4f6;\n color: #374151;\n border: 1px solid #d1d5db;\n }\n\n .btn-secondary:hover {\n background-color: #e5e7eb;\n }\n\n .btn-danger {\n background-color: #dc2626;\n color: white;\n }\n\n .btn-danger:hover {\n background-color: #b91c1c;\n }\n\n /* Engraving Container in Cart */\n .engraving-container {\n display: flex;\n align-items: center;\n justify-content: space-between;\n padding: 12px 16px;\n margin-top: 8px;\n background-color: #f8fafc;\n border: 1px solid #e2e8f0;\n border-radius: 6px;\n cursor: pointer;\n transition: all 0.2s ease;\n }\n\n .engraving-container:hover {\n background-color: #f1f5f9;\n border-color: #cbd5e1;\n }\n\n .engraving-title {\n display: flex;\n align-items: center;\n gap: 8px;\n flex: 1;\n }\n\n .engraving-title svg {\n flex-shrink: 0;\n color: #64748b;\n }\n\n .engraving-title-text {\n margin: 0;\n font-size: 14px;\n color: #475569;\n font-weight: 500;\n }\n\n .engraving-preview {\n margin: 4px 0 0 0;\n font-size: 12px;\n color: #64748b;\n font-style: italic;\n }\n\n .engraving-icon {\n color: #94a3b8;\n }\n\n .engraving-icon svg {\n width: 16px;\n height: 16px;\n }\n\n /* Responsive Design */\n @media (max-width: 640px) {\n .engraving-modal-content {\n min-width: auto;\n max-width: 95vw;\n padding: 16px;\n }\n\n .modal-content {\n max-width: 95vw;\n margin: 10px;\n }\n\n .engraving-modal-actions {\n flex-direction: column;\n }\n\n .engraving-modal-actions button {\n width: 100%;\n }\n }\n\n /* High z-index for cart modals */\n .modal-container[style*="z-index: 50000"] {\n z-index: 50000 !important;\n }\n\n .modal-container[style*="z-index: 50000"] .modal-content {\n z-index: 50001 !important;\n }\n\n /* Smooth transitions */\n @media (prefers-reduced-motion: reduce) {\n .modal-container,\n .modal-backdrop,\n .modal-content {\n transition: none;\n }\n }\n ',"\n .loading-skeleton-container {\n width: 100%;\n height: 500px;\n overflow: hidden;\n background-color: #f0f0f0;\n position: relative;\n display: flex;\n align-items: center;\n justify-content: center;\n }\n\n .loading-skeleton-container::before {\n content: '';\n position: absolute;\n top: 0;\n left: -150%;\n width: 150%;\n height: 100%;\n background: linear-gradient(\n to right,\n transparent 0%,\n transparent 25%,\n rgba(255, 255, 255, 0.9) 50%,\n transparent 75%,\n transparent 100%\n );\n animation: wave 2.5s infinite cubic-bezier(0.4, 0, 0.2, 1);\n }\n\n /* Add a second wave with different timing for more dynamic effect */\n .loading-skeleton-container::after {\n content: '';\n position: absolute;\n top: 0;\n left: -150%;\n width: 150%;\n height: 100%;\n background: linear-gradient(\n to right,\n transparent 0%,\n transparent 25%,\n rgba(255, 255, 255, 0.7) 50%,\n transparent 75%,\n transparent 100%\n );\n animation: wave 2.5s infinite 1.25s cubic-bezier(0.4, 0, 0.2, 1);\n }\n\n .loading-skeleton-container .skeleton-content {\n display: flex;\n flex-direction: column;\n align-items: center;\n justify-content: center;\n z-index: 2;\n }\n\n .loading-skeleton-container .icon-container {\n margin-bottom: 20px;\n display: flex;\n justify-content: center;\n align-items: center;\n min-height: 50px;\n }\n\n .loading-skeleton-container .message-container {\n font-size: 20px;\n color: #333;\n text-align: center;\n }\n\n @keyframes wave {\n 0% {\n left: -150%;\n }\n 100% {\n left: 100%;\n }\n }\n\n .loading-skeleton-container.show {\n display: flex;\n }\n\n .loading-skeleton-container:not(.show) {\n display: none;\n }\n","\n .collapsible-container {\n width: 100%;\n display: flex;\n flex-direction: column;\n }\n\n .collapsible-container .header {\n width: 100%;\n display: flex;\n flex-direction: row;\n flex-wrap: nowrap;\n justify-content: space-between;\n cursor: pointer;\n color: #212529;\n font-family: Arial, sans-serif;\n }\n\n .collapsible-container .header .title {\n font-size: 16px;\n font-weight: 500;\n color: #212529;\n line-height: 24px;\n }\n\n .collapsible-container .header .icon {\n font-size: 20px;\n }\n\n .collapsible-icon.expanded {\n transform: rotate(180deg);\n }\n\n .collapsible-container .content {\n max-height: 1000px;\n overflow: hidden;\n color: #282828;\n font-size: 14px;\n line-height: 20px;\n font-type: Arial;\n }\n\n .collapsible-container.collapsed .content {\n max-height: 0;\n }\n"].join("\n\n")}\n `.trim();this.baseStyleSheet.replaceSync(n)}generateComponentStyles(){const n=this.getProductConfig();if(n){const t=this.generateComponentStylesWithVariables(n,"product",["\n .container {\n width: 100%;\n height: auto;\n display: flex;\n flex-direction: column;\n }\n\n .container .image-carousel {\n width: 100%;\n }\n\n .container .main {\n width: 100%;\n }\n\n .product-address-container {\n width: 100%;\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n justify-content: space-between;\n font-family: inherit;\n align-items: center;\n gap: 8px;\n padding: 15px 0;\n }\n\n .product-address-container .edit-address {\n cursor: pointer;\n color: #1A56DB;\n background: none;\n border: none;\n }\n\n .product-add-to-cart {\n display: flex;\n flex-direction: row;\n flex-wrap: nowrap;\n gap: 20px;\n margin: 24px 0;\n }\n\n .product-add-to-cart .quantity {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: 10px;\n color: #1A56DB;\n border-radius: 4px;\n background-color: #F3F4F6;\n }\n\n .product-add-to-cart .quantity button {\n background: none;\n color: inherit;\n border: none;\n cursor: pointer;\n }\n\n .product-add-to-cart .add-to-cart-button {\n background: red;\n color: white;\n border: none;\n cursor: pointer;\n width: 100%;\n border-radius: 8px;\n padding: 8px 0;\n text-align: center;\n }\n\n @media (min-width: 769px) {\n .container {\n flex-direction: row;\n gap: 40px;\n }\n\n .container .image-carousel {\n width: 45%;\n }\n\n .container .main {\n width: 55%;\n }\n }\n\n /** V2 Styles \n * These styles are for the new product page design.\n */\n\n .product-main-container {\n width: 55%;\n display: flex;\n flex-direction: column;\n gap: 32px;\n align-items: flex-start;\n align-self: stretch;\n }\n\n .product-interactions {\n display: flex;\n flex-direction: column;\n gap: 32px;\n align-items: flex-start;\n align-self: stretch;\n }\n\n .product-options {\n width: 100%;\n }\n\n .size-container {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: var(--spacing-2, 8px);\n align-self: stretch;\n }\n\n .size-container .size-label {\n align-self: stretch;\n color: var(--base-foreground, #18181B);\n /* text small/leading-none/medium */\n font-family: var(--typography-font-family-font-sans, Poppins);\n font-size: var(--typography-base-sizes-small-font-size, 14px);\n font-style: normal;\n font-weight: var(--font-weight-medium, 500);\n line-height: 100%; /* 14px */\n }\n\n .size-container .size-buttons-container {\n display: flex;\n align-items: flex-start;\n align-content: flex-start;\n gap: 8px var(--spacing-2, 8px);\n align-self: stretch;\n flex-wrap: wrap;\n }\n\n .size-container .size-buttons-container .size-button {\n display: flex;\n padding: var(--spacing-2, 8px) var(--spacing-4, 16px);\n flex-direction: column;\n justify-content: center;\n align-items: center;\n cursor: pointer;\n\n /* This is the normal not selected button */\n border-radius: var(--border-radius-default, 6px);\n border: 1px solid var(--base-input, #E4E4E7);\n color: var(--base-foreground, #18181B);\n /* text small/leading-normal/medium */\n font-family: var(--typography-font-family-font-sans, Poppins);\n font-size: var(--typography-base-sizes-small-font-size, 14px);\n font-style: normal;\n font-weight: var(--font-weight-medium, 500);\n line-height: var(--typography-base-sizes-small-line-height, 20px);\n }\n\n .size-container .size-buttons-container .size-button.selected {\n background: var(--base-primary, #1A56DB);\n color: var(--base-foreground, #070707);\n\n border-radius: var(--border-radius-default, 6px);\n background: var(--base-primary, #1D4ED8);\n /* shadow/sm */\n box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.05);\n\n color: var(--base-primary-foreground, #F1F5F9);\n /* text small/leading-normal/medium */\n font-family: var(--typography-font-family-font-sans, Poppins);\n font-size: var(--typography-base-sizes-small-font-size, 14px);\n font-style: normal;\n font-weight: var(--font-weight-medium, 500);\n line-height: var(--typography-base-sizes-small-line-height, 20px);\n }\n\n .product-engraving-container {\n display: flex;\n padding: var(--spacing-4, 16px);\n flex-direction: column;\n justify-content: center;\n align-items: center;\n gap: var(--spacing-2, 8px);\n align-self: stretch;\n \n\n border-radius: var(--border-radius-default, 6px);\n border: 1px solid var(--base-input, #E4E4E7);\n }\n\n .product-engraving-wrapper {\n width: 100%;\n }\n\n .product-engraving-container .engraving-header {\n display: flex;\n justify-content: space-between;\n align-items: center;\n align-self: stretch;\n }\n\n .product-engraving-container .engraving-header h3 {\n color: var(--base-foreground, #18181B);\n /* text small/leading-none/medium */\n font-family: var(--typography-font-family-font-sans, Poppins);\n font-size: var(--typography-base-sizes-small-font-size, 14px);\n font-style: normal;\n font-weight: var(--font-weight-medium, 700);\n line-height: 100%; /* 14px */\n }\n\n .product-engraving-container .engraving-header button {\n display: flex;\n justify-content: center;\n align-items: center;\n align-self: stretch;\n background: none;\n border: none;\n cursor: pointer;\n\n color: var(--base-primary, #1D4ED8);\n /* text small/leading-none/medium */\n font-family: var(--typography-font-family-font-sans, Poppins);\n font-size: var(--typography-base-sizes-small-font-size, 14px);\n font-style: normal;\n font-weight: var(--font-weight-medium, 500);\n line-height: 100%; /* 14px */\n }\n\n .product-engraving-container .engraving-lines {\n display: flex;\n height: 60px;\n flex-direction: column;\n align-items: flex-start;\n gap: var(--spacing-0, 0px);\n align-self: stretch;\n }\n\n .product-engraving-container .engraving-lines .engraving-line {\n color: var(--base-foreground, #18181B);\n /* text small/leading-normal/italic */\n font-family: var(--typography-font-family-font-sans, Poppins);\n font-size: var(--typography-base-sizes-small-font-size, 14px);\n font-style: italic;\n font-weight: 400;\n line-height: var(--typography-base-sizes-small-line-height, 20px);\n text-transform: uppercase;\n }\n\n .product-main-container .button-engraving-label {\n display: flex;\n height: var(--height-h-9, 36px);\n padding: var(--spacing-2, 8px) var(--spacing-4, 16px);\n justify-content: center;\n align-items: center;\n gap: var(--spacing-2, 8px);\n cursor: pointer;\n \n\n border-radius: var(--border-radius-md, 6px);\n border: 1px solid var(--base-input, #E4E4E7);\n background: var(--base-background, #FAFAFA);\n /* shadow/sm */\n box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.05);\n\n color: var(--base-foreground, #18181B);\n /* text small/leading-normal/medium */\n font-family: var(--typography-font-family-font-sans, Poppins);\n font-size: var(--typography-base-sizes-small-font-size, 14px);\n font-style: normal;\n font-weight: var(--font-weight-medium, 500);\n line-height: var(--typography-base-sizes-small-line-height, 20px);\n }\n\n\n /** Product Retailers and Tabs */\n \n\n .retailers-container {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: 8px;\n align-self: stretch;\n }\n\n .retailers-container .fulfillment-tabs-container {\n display: flex;\n height: 36px;\n align-items: center;\n align-self: stretch;\n }\n\n .retailers-container .fulfillment-tabs-container .fulfillment-tab {\n display: flex;\n padding: var(--spacing-0, 0px) 0px;\n justify-content: center;\n align-items: center;\n gap: 8px;\n cursor: pointer;\n }\n\n \n .retailers-container .fulfillment-tabs-container .fulfillment-tab.selected {\n border-bottom: 2px solid var(--base-primary, #1D4ED8);\n }\n\n .retailers-container .fulfillment-tabs-container .fulfillment-tab .fulfillment-tab-wrapper {\n display: flex;\n padding: var(--spacing-2, 8px) var(--spacing-4, 16px);\n justify-content: center;\n align-items: center;\n gap: 8px;\n border-radius: var(--border-radius-md, 6px);\n\n /* Typhography Styles */\n\n color: var(--base-foreground, #707077);\n\n /* text small/leading-normal/regular */\n font-family: var(--typography-font-family-font-sans, Poppins);\n font-size: var(--typography-base-sizes-small-font-size, 14px);\n font-style: normal;\n font-weight: var(--font-weight-normal, 400);\n line-height: var(--typography-base-sizes-small-line-height, 20px); /* 142.857% */\n }\n\n .retailers-container .fulfillment-tabs-container .fulfillment-tab .fulfillment-tab-wrapper.selected {\n color: var(--base-foreground, #18181B);\n /* text small/leading-normal/regular */\n font-family: var(--typography-font-family-font-sans, Poppins);\n font-size: var(--typography-base-sizes-small-font-size, 14px);\n font-style: normal;\n font-weight: var(--font-weight-normal, 400);\n line-height: var(--typography-base-sizes-small-line-height, 20px);\n }\n\n /** Retailers List */\n\n\n .retailer-list-container {\n display: flex;\n flex-direction: row;\n align-items: center;\n gap: var(--spacing-2, 8px);\n align-self: stretch;\n overflow-x: auto;\n }\n\n .retailer-card {\n display: flex;\n width: 220px;\n height: 150px;\n padding: 16px;\n flex-direction: column;\n align-items: flex-start;\n justify-content: space-between;\n gap: 8px;\n flex-shrink: 0;\n transition: all 0.2s ease;\n cursor: pointer;\n \n border-radius: var(--border-radius-md, 6px);\n border: 1px solid var(--base-input, #E4E4E7);\n background: var(--base-background, #FAFAFA);\n /* shadow/sm */\n box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.05);\n\n color: var(--base-foreground, #18181B);\n }\n\n .retailer-card.selected {\n border-radius: var(--border-radius-default, 6px);\n background: var(--base-primary, #1D4ED8);\n\n color: var(--base-primary-foreground, #F1F5F9);\n \n\n /* shadow/sm */\n box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.05);\n }\n\n .retailer-card .retailer-name {\n align-self: stretch;\n\n /* text small/leading-normal/medium */\n font-family: var(--typography-font-family-font-sans, Poppins);\n font-size: var(--typography-base-sizes-small-font-size, 14px);\n font-style: normal;\n font-weight: var(--font-weight-medium, 500);\n line-height: var(--typography-base-sizes-small-line-height, 20px); /* 142.857% */\n }\n\n .retailer-card .retailer-info {\n display: flex;\n flex-direction: column;\n align-items: flex-start;\n gap: var(--spacing-1, 4px);\n align-self: stretch;\n }\n\n .retailer-card .price-label {\n display: flex;\n align-items: center;\n gap: var(--spacing-2, 8px);\n align-self: stretch;\n\n /* text extra large/leading-normal/semibold */\n font-family: var(--typography-font-family-font-sans, Poppins);\n font-size: var(--typography-base-sizes-xlarge-font-size, 20px);\n font-style: normal;\n font-weight: var(--font-weight-semibold, 600);\n line-height: var(--typography-base-sizes-xlarge-line-height, 28px); /* 140% */\n }\n\n .retailer-card .expectation {\n align-self: stretch;\n font-family: var(--typography-font-family-font-sans, Poppins);\n font-size: var(--typography-base-sizes-small-font-size, 14px);\n font-style: normal;\n font-weight: var(--font-weight-normal, 400);\n line-height: var(--typography-base-sizes-small-line-height, 20px); /* 142.857% */\n }\n\n /** Product Add to Cart Container */\n\n .add-to-cart-container {\n display: flex;\n align-items: flex-start;\n align-content: flex-start;\n gap: 16px;\n align-self: stretch;\n flex-wrap: wrap;\n }\n\n .add-to-cart-container .quantity-container {\n display: flex;\n align-items: center;\n gap: -1px;\n }\n\n .add-to-cart-container .quantity-container .quantity-decrease {\n display: flex;\n width: var(--width-w-9, 36px);\n height: var(--height-h-9, 36px);\n padding: var(--spacing-2, 8px) var(--spacing-4, 16px);\n justify-content: center;\n align-items: center;\n gap: var(--spacing-2, 8px);\n\n border-radius: var(--border-radius-md, 6px) 0px 0px var(--border-radius-md, 6px);\n border: 1px solid var(--base-input, #E4E4E7);\n background: var(--base-background, #FAFAFA);\n /* shadow/sm */\n box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.05);\n\n cursor: pointer;\n }\n\n .add-to-cart-container .quantity-container .quantity-increase {\n display: flex;\n width: var(--width-w-9, 36px);\n height: var(--height-h-9, 36px);\n padding: var(--spacing-2, 8px) var(--spacing-4, 16px);\n justify-content: center;\n align-items: center;\n gap: var(--spacing-2, 8px);\n cursor: pointer;\n\n border-radius: 0px var(--border-radius-md, 6px) var(--border-radius-md, 6px) 0px;\n border: 1px solid var(--base-input, #E4E4E7);\n background: var(--base-background, #FAFAFA);\n /* shadow/sm */\n box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.05);\n }\n\n .add-to-cart-container .quantity-container .product-count {\n display: flex;\n height: var(--height-h-9, 36px);\n padding: var(--spacing-2, 8px) var(--spacing-4, 16px);\n justify-content: center;\n align-items: center;\n gap: var(--spacing-2, 8px);\n\n background: var(--base-background, #FAFAFA);\n /* shadow/sm */\n box-shadow: 0px 1px 2px 0px rgba(0, 0, 0, 0.05);\n\n border-top: 1px solid var(--base-input, #E4E4E7);\n border-bottom: 1px solid var(--base-input, #E4E4E7);\n\n color: var(--base-foreground, #18181B);\n /* text small/leading-normal/medium */\n font-family: var(--typography-font-family-font-sans, Poppins);\n font-size: var(--typography-base-sizes-small-font-size, 14px);\n font-style: normal;\n font-weight: var(--font-weight-medium, 500);\n line-height: var(--typography-base-sizes-small-line-height, 20px); /* 142.857% */\n }\n\n .add-to-cart-container .add-to-cart-button {\n display: flex;\n height: var(--height-h-9, 36px);\n min-width: 182px;\n padding: var(--spacing-2, 8px) var(--spacing-4, 16px);\n justify-content: center;\n align-items: center;\n gap: var(--spacing-2, 8px);\n flex: 1 0 0;\n\n border-radius: var(--border-radius-md, 6px);\n background: var(--base-primary, #1D4ED8);\n\n border: none;\n outline: none;\n\n cursor: pointer;\n\n color: var(--base-primary-foreground, #F1F5F9);\n /* text small/leading-normal/medium */\n font-family: var(--typography-font-family-font-sans, Poppins);\n font-size: var(--typography-base-sizes-small-font-size, 14px);\n font-style: normal;\n font-weight: var(--font-weight-medium, 500);\n line-height: var(--typography-base-sizes-small-line-height, 20px);\n }\n\n\n /** These will change \n * \n * They are used to demo the css variables passing to client.\n */\n\n .product-title {\n display: flex;\n flex-direction: row;\n flex-wrap: wrap;\n justify-content: space-between;\n align-items: baseline;\n background-color: var(--product-title-container-bg-color);\n }\n\n .product-title h1 {\n font-size: 24px;\n font-weight: 500;\n flex: 1 1 auto;\n min-width: 65%;\n font-family: Arial;\n color: var(--product-title-primary-color);\n }\n\n .product-price {\n display: inline-block;\n width: fit-content;\n }\n\n .product-price span {\n font-size: 26px;\n font-weight: 600;\n line-height: 40px;\n font-family: Arial;\n color: var(--product-title-secondary-color);\n }\n","\n .product-image-container {\n display: flex;\n flex-direction: column;\n gap: 12px;\n max-width: 100%;\n max-height: 620px;\n }\n\n .main-image-wrapper {\n position: relative;\n display: flex;\n justify-content: center;\n align-items: center;\n background: #f8f9fa;\n border-radius: 8px;\n overflow: hidden;\n aspect-ratio: 1;\n }\n\n .main-image {\n width: 100%;\n height: 100%;\n object-fit: contain;\n }\n\n .carousel-dots {\n position: absolute;\n bottom: 16px;\n left: 50%;\n transform: translateX(-50%);\n display: flex;\n gap: 10px;\n background: rgba(0, 0, 0, 0.5);\n padding: 10px 14px;\n border-radius: 20px;\n }\n\n .carousel-dot {\n width: 10px;\n height: 10px;\n border-radius: 50%;\n border: none;\n background: rgba(255, 255, 255, 0.5);\n cursor: pointer;\n transition: background-color 0.2s ease;\n }\n\n .carousel-dot:hover {\n background: rgba(255, 255, 255, 0.7);\n }\n\n .carousel-dot.active {\n background: white;\n }\n\n .product-image-gallery {\n display: none;\n }\n\n .gallery-image {\n min-width: 60px;\n height: 60px;\n object-fit: cover;\n border: 2px solid transparent;\n border-radius: 6px;\n cursor: pointer;\n background: #f8f9fa;\n opacity: 0.5;\n transition: opacity 0.2s ease;\n }\n\n .gallery-image.selected {\n opacity: 1;\n }\n\n .no-images {\n display: flex;\n align-items: center;\n justify-content: center;\n height: 300px;\n background: #f8f9fa;\n color: #6c757d;\n border-radius: 8px;\n font-size: 16px;\n }\n\n /* Small mobile screens */\n @container (min-width: 480px) {\n .carousel-dot {\n width: 8px;\n height: 8px;\n }\n\n .carousel-dots {\n gap: 8px;\n padding: 8px 12px;\n }\n }\n\n /* Tablet and desktop */\n @media (min-width: 769px) {\n .product-image-container {\n gap: 16px;\n }\n\n .carousel-dots {\n display: none;\n }\n\n .product-image-gallery {\n display: flex;\n gap: 8px;\n overflow-x: auto;\n padding: 4px;\n scrollbar-width: thin;\n }\n\n .product-image-gallery::-webkit-scrollbar {\n height: 6px;\n }\n\n .product-image-gallery::-webkit-scrollbar-track {\n background: #f1f1f1;\n border-radius: 3px;\n }\n\n .product-image-gallery::-webkit-scrollbar-thumb {\n background: #c1c1c1;\n border-radius: 3px;\n }\n\n .product-image-gallery::-webkit-scrollbar-thumb:hover {\n background: #a8a8a8;\n }\n\n .gallery-image {\n min-width: 80px;\n height: 80px;\n }\n }\n\n @media (prefers-reduced-motion: reduce)