From 3b22c859927fdf517ab1398b2f8617a62d4ef817 Mon Sep 17 00:00:00 2001 From: Devin Michael Date: Mon, 21 Sep 2026 15:21:03 +0700 Subject: [PATCH 1/6] Add the Image with Text homepage section A 50/50 image and copy split (partials/section_image_text.html) with image left/right, square/portrait/landscape/native image ratio, eyebrow, heading, rich-text body, a CTA with primary/secondary/outline styles, and background and text colours. On mobile it stacks image first. It is included from templates/index.html between Featured Categories and On Sale, behind the show_image_text toggle, which is off by default so existing stores render unchanged. With the toggle on and nothing configured it renders the same setup placeholder pattern as the sibling sections, with a localized pointer to the settings location added to every locale file. partials/cta_button.html gains a `secondary` style so the section can reuse the existing .btn-secondary class through the shared helper. image_text_cta_text follows the localization contract: no schema default, omitted from settings data, the template applies the localized fallback. assets/main.css is rebuilt for the new ratio and ordering utilities (50298 -> 50536 bytes); css/input.css is unchanged. Co-Authored-By: Claude Fable 5.1 --- assets/main.css | 2 +- configs/settings_data.json | 12 +++ configs/settings_optional.txt | 1 + configs/settings_schema.json | 132 +++++++++++++++++++++++++++ locales/de.json | 3 +- locales/en.default.json | 3 +- locales/es.json | 3 +- locales/fi.json | 3 +- locales/fr.json | 3 +- locales/it.json | 3 +- locales/nl.json | 3 +- locales/no.json | 3 +- locales/pt.json | 3 +- locales/sv.json | 3 +- locales/th.json | 3 +- partials/cta_button.html | 4 +- partials/section_image_text.html | 63 +++++++++++++ templates/index.html | 1 + tests/test_localization_contracts.py | 1 + 19 files changed, 235 insertions(+), 14 deletions(-) create mode 100644 partials/section_image_text.html diff --git a/assets/main.css b/assets/main.css index ccee945..0a1257c 100644 --- a/assets/main.css +++ b/assets/main.css @@ -1,2 +1,2 @@ /*! tailwindcss v4.2.2 | MIT License | https://tailwindcss.com */ -*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-top:initial;right:initial;bottom:initial;left:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-duration:initial;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1}:root,:host{--font-sans:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-red-50:#fef2f2;--color-red-200:#ffc9c9;--color-red-400:#ff6467;--color-red-500:#fb2c36;--color-red-600:#e7000b;--color-red-800:#9f0712;--color-amber-50:#fffbeb;--color-amber-200:#fee685;--color-amber-400:#ffb900;--color-amber-800:#973c00;--color-green-50:#f0fdf4;--color-green-200:#b9f8cf;--color-green-600:#00a63e;--color-green-800:#016630;--color-blue-50:#eff6ff;--color-blue-200:#bedbff;--color-blue-800:#193cb8;--color-slate-50:#f8fafc;--color-slate-100:#f1f5f9;--color-slate-200:#e2e8f0;--color-slate-300:#cbd5e1;--color-slate-400:#94a3b8;--color-slate-500:#64748b;--color-slate-600:#475569;--color-slate-700:#334155;--color-slate-800:#1e293b;--color-slate-900:#0f172a;--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-md:28rem;--container-xl:36rem;--container-2xl:42rem;--container-3xl:48rem;--container-4xl:56rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--text-3xl:1.875rem;--text-3xl--line-height:calc(2.25 / 1.875);--text-4xl:2.25rem;--text-4xl--line-height:calc(2.5 / 2.25);--text-6xl:3.75rem;--text-6xl--line-height:1;--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--tracking-wider:.05em;--leading-tight:1.25;--leading-relaxed:1.625;--radius-md:.375rem;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--radius-card:var(--card-radius,0);--spacing-section-y:var(--section-padding-y,3rem);--spacing-section-y-md:calc(var(--section-padding-y,3rem) * 4 / 3);--spacing-band-y:calc(var(--section-padding-y,3rem) * 4 / 3);--spacing-band-y-md:calc(var(--section-padding-y,3rem) * 5 / 3);--spacing-content-md:calc(var(--content-gap,1.5rem) * 4 / 3);--spacing-content-lg:calc(var(--content-gap,1.5rem) * 2);--text-h3:calc(1.25rem * var(--heading-scale,1));--text-h3--line-height:calc(1.75 / 1.25);--text-h2:calc(1.5rem * var(--heading-scale,1));--text-h2--line-height:calc(2 / 1.5);--text-h1:calc(1.875rem * var(--heading-scale,1));--text-h1--line-height:calc(2.25 / 1.875);--text-h1-md:calc(2.25rem * var(--heading-scale,1));--text-h1-md--line-height:calc(2.5 / 2.25);--text-display:calc(1.875rem * var(--heading-scale,1));--text-display--line-height:calc(2.25 / 1.875);--text-display-md:calc(3rem * var(--heading-scale,1));--text-display-md--line-height:1}*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}select[multiple],[size] optgroup{font-weight:bolder}select[multiple],[size] optgroup option{padding-left:20px}::file-selector-button{margin-right:4px}::placeholder{opacity:1}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-top:0;padding-bottom:0}::-webkit-datetime-edit-year-field{padding-top:0;padding-bottom:0}::-webkit-datetime-edit-month-field{padding-top:0;padding-bottom:0}::-webkit-datetime-edit-day-field{padding-top:0;padding-bottom:0}::-webkit-datetime-edit-hour-field{padding-top:0;padding-bottom:0}::-webkit-datetime-edit-minute-field{padding-top:0;padding-bottom:0}::-webkit-datetime-edit-second-field{padding-top:0;padding-bottom:0}::-webkit-datetime-edit-millisecond-field{padding-top:0;padding-bottom:0}::-webkit-datetime-edit-meridiem-field{padding-top:0;padding-bottom:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input[type=button],[type=reset],[type=submit]{appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:not([hidden=until-found]){display:none!important}:root{--primary-color:#1e293b;--accent-color:#475569}body{font-family:var(--font-sans);font-size:var(--body-size,1rem);color:#1e293b;background-color:var(--body-bg-color,#fff);line-height:1.6}h1,h2,h3,h4,h5,h6{color:#1e293b;font-weight:600;line-height:1.2}a{color:var(--primary-color);text-decoration:none}a:hover{opacity:.8}img{max-width:100%;height:auto}.btn{border-radius:var(--control-radius,.25rem);cursor:pointer;justify-content:center;align-items:center;padding:.625rem 1.5rem;font-size:.875rem;font-weight:500;line-height:1.25rem;transition:opacity .15s ease-out;display:inline-flex}.btn:hover{opacity:.9}.btn:disabled{opacity:.5;cursor:not-allowed}.btn-primary{background-color:var(--primary-color)!important;color:#fff!important;border-color:var(--primary-color)!important}.btn-accent{background-color:var(--accent-color)!important;color:#fff!important;border-color:var(--accent-color)!important}.btn-outline{color:var(--btn-color,var(--primary-color))!important;border:1px solid var(--btn-color,var(--primary-color))!important;background-color:#0000!important}.btn-secondary{color:var(--primary-color);border:1px solid var(--border-color,#e2e8f0);background-color:#0000}.btn-secondary:hover{background-color:#f8fafc}.container{width:100%;max-width:var(--container-max,1280px);margin-left:auto;margin-right:auto;padding-left:1rem;padding-right:1rem}@media (min-width:768px){.container{padding-left:1.5rem;padding-right:1.5rem}}.product-card{border-radius:var(--card-radius,0);flex-direction:column;display:flex;overflow:hidden}.product-card-bordered{border:1px solid var(--border-color,#e2e8f0)}.product-card-bordered .product-title{padding-left:1rem;padding-right:1rem}.product-card-bordered .product-price{padding-bottom:1rem;padding-left:1rem;padding-right:1rem}.product-card img{aspect-ratio:1;object-fit:cover;background-color:#f8fafc}.product-card .product-title{margin-top:.75rem;font-size:.875rem;font-weight:500}.product-card .product-price{color:#475569;margin-top:.25rem;font-size:.875rem}[data-spark-membership-price][data-membership-active=true] [data-spark-public-price]{color:#94a3b8;font-weight:400;text-decoration-line:line-through}.spark-member-price{color:#1e293b;flex-wrap:wrap;align-items:baseline;gap:.375rem;margin-top:.25rem;display:flex}.spark-member-price-label{color:var(--primary-color);text-transform:uppercase;font-size:.75rem;font-weight:600}.spark-member-price-value{font-weight:700}.spark-member-price-detail{color:#64748b;font-size:.75rem;font-weight:400}.product-card .spark-member-price,[data-spark-membership-layout=sticky] .spark-member-price{gap:.25rem;margin-top:.125rem}.product-card .spark-member-price-label,[data-spark-membership-layout=sticky] .spark-member-price-label{font-size:.6875rem}[data-spark-membership-layout=sticky][data-membership-active=true]{line-height:1.25;display:block}.product-grid{gap:var(--content-gap,1.5rem);grid-template-columns:repeat(1,1fr);display:grid}@media (min-width:768px){.product-grid{grid-template-columns:repeat(2,1fr)}}@media (min-width:1024px){.product-grid{grid-template-columns:repeat(var(--product-grid-columns,3), 1fr)}}.category-grid{gap:calc(var(--content-gap,1.5rem) * 2 / 3);grid-template-columns:repeat(1,1fr);display:grid}@media (min-width:768px){.category-grid{grid-template-columns:repeat(var(--category-grid-columns,2), 1fr)}}.skeleton-card{flex-direction:column;display:flex}.skeleton-card .skeleton-image{aspect-ratio:1;background-color:#f1f5f9;position:relative;overflow:hidden}.skeleton-card .skeleton-image:after{content:"";background:linear-gradient(90deg,#f1f5f9,#e2e8f0,#f1f5f9);animation:1.5s ease-in-out infinite shimmer;position:absolute;top:0;right:0;bottom:0;left:0}.skeleton-card .skeleton-text{background-color:#f1f5f9;height:12px;margin-top:.75rem;position:relative;overflow:hidden}.skeleton-card .skeleton-text:after{content:"";background:linear-gradient(90deg,#f1f5f9,#e2e8f0,#f1f5f9);animation:1.5s ease-in-out infinite shimmer;position:absolute;top:0;right:0;bottom:0;left:0}.skeleton-card .skeleton-text-short{background-color:#f1f5f9;width:40%;height:12px;margin-top:.25rem;position:relative;overflow:hidden}.skeleton-card .skeleton-text-short:after{content:"";background:linear-gradient(90deg,#f1f5f9,#e2e8f0,#f1f5f9);animation:1.5s ease-in-out infinite shimmer;position:absolute;top:0;right:0;bottom:0;left:0}@keyframes shimmer{0%{transform:translate(-100%)}to{transform:translate(100%)}}.cart-badge{background-color:var(--primary-color);color:#fff;text-align:center;pointer-events:none;border-radius:50%;width:16px;height:16px;font-size:10px;font-weight:600;line-height:16px;position:absolute;top:-4px;right:-4px}[data-light-primary] .cart-badge{color:#1e293b}.cart-badge[data-animate]{animation:.3s ease-out badge-pulse}@keyframes badge-pulse{0%{transform:scale(1)}50%{transform:scale(1.3)}to{transform:scale(1)}}.spark-gallery{flex-direction:column;gap:.75rem;display:flex}@media (min-width:768px){.spark-gallery-left{flex-direction:row-reverse;gap:.75rem}.spark-gallery-left .spark-gallery-main{flex:1;min-width:0}.spark-gallery-left .spark-gallery-thumbs{flex-direction:column;justify-content:flex-start;width:72px;max-height:520px;overflow:hidden auto}.spark-gallery-left .spark-gallery-thumb{flex-shrink:0;width:72px;height:72px}}.spark-gallery-main{aspect-ratio:1;background-color:#f8fafc;max-height:560px;position:relative;overflow:hidden}.spark-gallery-hero{object-fit:contain;width:100%;height:100%;transition:opacity .2s ease-out}.spark-gallery-hero.spark-gallery-fading{opacity:0}.spark-gallery-thumbs{scrollbar-width:thin;scrollbar-color:#cbd5e1 transparent;justify-content:center;gap:.5rem;padding:.25rem 0;display:flex;overflow:auto hidden}.spark-gallery-thumb{cursor:pointer;opacity:.6;background:#f8fafc;border:2px solid #0000;flex-shrink:0;width:72px;height:72px;padding:0;transition:border-color .2s ease-out,opacity .2s ease-out;overflow:hidden}.spark-gallery-thumb:hover{opacity:1;border-color:#cbd5e1}.spark-gallery-thumb-active{opacity:1!important;border-color:var(--primary-color)!important}.spark-gallery-prev,.spark-gallery-next{cursor:pointer;color:#475569;opacity:0;z-index:2;background:#ffffffe6;border:none;border-radius:50%;justify-content:center;align-items:center;width:36px;height:36px;transition:opacity .2s ease-out,background-color .15s ease-out;display:flex;position:absolute;top:50%;transform:translateY(-50%);box-shadow:0 1px 3px #0000001a}.spark-gallery-prev{left:.75rem}.spark-gallery-next{right:.75rem}.spark-gallery-prev:hover,.spark-gallery-next:hover{color:#1e293b;background:#fff}.spark-gallery:hover .spark-gallery-prev,.spark-gallery:hover .spark-gallery-next,.spark-gallery:focus-within .spark-gallery-prev,.spark-gallery:focus-within .spark-gallery-next{opacity:1}@media (hover:none){.spark-gallery-prev,.spark-gallery-next{opacity:.7}}.sticky-atc{z-index:50;background:#fff;border-top:1px solid #e2e8f0;transition:transform .25s ease-out;position:fixed;bottom:0;left:0;right:0;transform:translateY(100%);box-shadow:0 -2px 8px #0000000f}.sticky-atc-visible{transform:translateY(0)}.sticky-atc-inner{justify-content:space-between;align-items:center;gap:1rem;max-width:1280px;margin:0 auto;padding:.75rem 1rem;display:flex}.sticky-atc-info{flex-direction:column;min-width:0;display:flex}.sticky-atc-title{color:#1e293b;white-space:nowrap;text-overflow:ellipsis;font-size:.875rem;font-weight:500;overflow:hidden}.sticky-atc-price{color:#475569;font-size:.875rem}.sticky-atc-btn{white-space:nowrap;flex-shrink:0;padding:.625rem 1.5rem}@media (min-width:1024px){.sticky-atc{display:none}}.search-overlay{z-index:60;opacity:0;visibility:hidden;justify-content:center;align-items:flex-start;transition:opacity .2s ease-out,visibility .2s ease-out;display:flex;position:fixed;top:0;right:0;bottom:0;left:0}.search-overlay-visible{opacity:1;visibility:visible}.search-overlay-backdrop{background:#0006;position:absolute;top:0;right:0;bottom:0;left:0}.search-overlay-content{z-index:1;width:100%;max-width:640px;margin-top:5rem;padding:0 1rem;transition:transform .2s ease-out;position:relative;transform:translateY(-12px)}.search-overlay-visible .search-overlay-content{transform:translateY(0)}.search-overlay-form{background:#fff;border:1px solid #e2e8f0;align-items:center;display:flex;box-shadow:0 8px 30px #0000001f}.search-overlay-input{color:#1e293b;background:0 0;border:none;outline:none;flex:1;padding:1rem 1.25rem;font-size:1rem}.search-overlay-input::placeholder{color:#94a3b8}.search-overlay-submit{color:#475569;cursor:pointer;background:0 0;border:none;padding:.75rem}.search-overlay-submit:hover{color:#1e293b}.search-overlay-close{color:#94a3b8;cursor:pointer;background:0 0;border:none;padding:.75rem}.search-overlay-close:hover{color:#475569}.sr-only,.skip-to-content{clip:rect(0, 0, 0, 0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.skip-to-content:focus{z-index:100;clip:auto;width:auto;height:auto;font-family:var(--font-sans);background-color:#1e293b;border:1px solid #1e293b;border-radius:2px;margin:0;padding:.625rem 1rem;font-size:.875rem;font-weight:500;line-height:1.25rem;top:1rem;left:1rem;overflow:visible;color:#fff!important}@media (min-width:1024px){.catalogue-layout{grid-template-columns:15rem minmax(0,1fr);align-items:start;gap:2rem;display:grid}}.catalogue-rail{display:none}@media (min-width:1024px){.catalogue-rail{display:block;position:sticky;top:5.5rem}}.catalogue-filter-inline{margin-bottom:1.5rem;display:flex}@media (min-width:1024px){.catalogue-filter-inline{display:none}}.catalogue-filter-button{color:#1e293b;cursor:pointer;background:#fff;border:1px solid #e2e8f0;border-radius:.25rem;justify-content:center;align-items:center;gap:.5rem;width:100%;min-height:44px;padding:.625rem 1rem;font-family:inherit;font-size:.875rem;font-weight:500;line-height:1.25rem;display:inline-flex}.catalogue-bar{z-index:50;visibility:hidden;pointer-events:none;background:#fff;border-top:1px solid #e2e8f0;transition:transform .25s ease-in,visibility 0s linear .25s;position:fixed;bottom:0;left:0;right:0;transform:translateY(100%)}.catalogue-bar-visible{visibility:visible;pointer-events:auto;transition:transform .25s ease-out;transform:translateY(0)}.catalogue-bar-inner{align-items:stretch;gap:.75rem;max-width:1280px;margin:0 auto;padding:.75rem 1rem;display:flex}.catalogue-bar-control{flex:1 1 0;min-width:0}.catalogue-bar-count{background:var(--primary-color);color:#fff;border-radius:9999px;justify-content:center;align-items:center;min-width:1.25rem;height:1.25rem;padding:0 .25rem;font-size:10px;font-weight:600;line-height:1;display:inline-flex}@media (min-width:1024px){.catalogue-bar{display:none}}.filter-drawer{z-index:60;visibility:hidden;pointer-events:none;position:fixed;top:0;right:0;bottom:0;left:0}.filter-drawer-visible{visibility:visible;pointer-events:auto}.filter-drawer-backdrop{opacity:0;background:#0000004d;transition:opacity .2s ease-out;position:absolute;top:0;right:0;bottom:0;left:0}.filter-drawer-visible .filter-drawer-backdrop{opacity:1}.filter-drawer-panel{background:#fff;border-top:1px solid #e2e8f0;flex-direction:column;max-height:85vh;transition:transform .25s ease-out;display:flex;position:absolute;bottom:0;left:0;right:0;transform:translateY(100%)}.filter-drawer-visible .filter-drawer-panel{transform:translateY(0)}.filter-drawer-header{border-bottom:1px solid #e2e8f0;justify-content:space-between;align-items:center;gap:1rem;padding:.75rem 1rem;display:flex}.filter-drawer-title{color:#1e293b;font-size:.875rem;font-weight:600}.filter-drawer-close{color:#475569;cursor:pointer;background:0 0;border:none;justify-content:center;align-items:center;width:44px;height:44px;margin-right:-.75rem;display:inline-flex}.filter-drawer-body{padding:1rem;overflow-y:auto}@media (min-width:1024px){.filter-drawer{display:none}}:focus-visible{outline:2px solid var(--primary-color);outline-offset:2px}@media print{header,footer,#announcement-bar,.sidecart,.breadcrumb,nav[aria-label=breadcrumb]{display:none!important}.container{max-width:100%;padding:0}body{color:#000;background:#fff;font-size:12pt}a{color:#000;text-decoration:underline}img{max-width:100%}.product-grid{grid-template-columns:repeat(2,1fr)}}.sidecart-open{overflow:hidden}.sidecart-open .sidecart{overflow-x:hidden}.sidecart{outline:0;font-family:inherit;position:absolute;top:0;overflow:hidden}.sidecart .sidecart-dialog{width:100vw;transition:all .6s cubic-bezier(.75,0,.55,1);right:-100vw}.sidecart.show .sidecart-dialog{transition:all .6s cubic-bezier(.33,0,0,1);right:0}.sidecart .cart-btn{color:#1e293b;cursor:pointer;text-align:center;white-space:nowrap;-webkit-user-select:none;user-select:none;background-color:#e2e8f0;border:1px solid #0000;justify-content:center;padding:.8rem 1rem;transition:background-color .15s ease-out}.sidecart .cart-btn:hover{background-color:#cbd5e1;border:1px solid #0000}.sidecart .cart-btn:focus:not(:active){box-shadow:0 0 0 2px var(--primary-color)}.sidecart .text-right{text-align:right}.sidecart .text-center{text-align:center}.sidecart .error-text{color:#ef4444;font-size:small}.sidecart a{color:#1e293b}.sidecart a:hover{color:#475569}.sidecart button{-webkit-appearance:none;cursor:pointer;background-color:#0000;border:0;padding:0;font-family:inherit}.sidecart button.close{color:#475569;float:right;font-weight:400;line-height:.5;transition:color .15s ease-out}.sidecart button.close:hover{color:#1e293b}.sidecart-dialog{pointer-events:none;z-index:99999;height:100%;margin:auto;position:fixed}.sidecart-content{pointer-events:auto;background-color:#fff;background-clip:padding-box;border:none;border-radius:0;outline:0;flex-direction:column;height:100%;display:flex;position:relative;overflow-y:auto;box-shadow:0 0 1px 1px #8890c333}.sidecart-header{background-color:#fff;border-bottom:none;justify-content:center;align-items:center;height:60px;padding:1rem;display:flex;position:relative}.sidecart-header button.close{color:#475569;font-size:2rem;position:absolute;right:1rem}.sidecart-header button.close:hover{color:#1e293b}.sidecart-title{text-align:center;margin-bottom:0;font-weight:700}#basket-content-div{overflow-y:auto}.sidecart-body{position:inherit;flex:auto;padding:.5rem .5rem 6rem;overflow-y:auto}.sidecart-footer{background-color:#fff;border-top:1px solid #e2e8f0;width:auto;padding:1rem;display:flex;bottom:0}.sidecart-footer .cart-btn{text-align:center;cursor:pointer;border:1px solid #0000;border-radius:.3rem;width:100%;height:auto;padding:1.25rem;font-size:24px;font-weight:700;text-decoration:none;display:inline-block;background-color:var(--primary-color)!important;color:#fff!important}.sidecart-footer .cart-btn:hover{border:1px solid #0000;background-color:var(--primary-color-darker)!important;color:#fff!important}.sidecart .form-control{color:#1e293b;appearance:none;background-color:#fff;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23475569' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");background-position:right .5rem center;background-repeat:no-repeat;background-size:14px 10px;border:1px solid #e2e8f0;border-radius:.25rem;width:100%;padding:.25rem 2rem .25rem .5rem;font-size:.875rem;line-height:1.5;display:block}.cart-items{margin:0 0 20px}.cart-item{box-shadow:none;border-bottom:1px solid #e2e8f0;flex-flow:row;justify-content:flex-start;margin:0 0 .75rem;padding:.75rem;display:flex}.cart-item:first-child{border-top:1px solid #e2e8f0}.cart-item img{width:100%}.cart-item .item-img{width:110px;margin-right:5%;padding:5px}.cart-item .item-details{width:100%;padding-top:.5rem}.cart-item .item-title{margin-bottom:.5rem;font-family:inherit;font-size:1rem;font-weight:500;line-height:1.2;display:block}.cart-item .item-description{flex-flow:wrap;align-items:center;width:100%;margin:0 0 .5em;display:flex}.cart-item .item-description .basket-action{flex-direction:column;flex:40%;height:fit-content;margin-right:20px;display:flex}.cart-item .item-description .basket-action:last-child{margin-right:0}.cart-item .item-description label{margin-bottom:0;display:inline-block}.cart-item .item-description button.close{font-size:smaller}.cart-item .item-price{width:100%}.cart-item .close{font-size:1.5rem}table.sidecart-table{background-color:#0000;width:100%}table.sidecart-table td{vertical-align:top;border-top:1px solid #e2e8f0;padding:.75rem}table.sidecart-table tr:first-child td{border-top:none}table.sidecart-table.table-sm td{padding:.15rem;font-size:small}.cart-totals{font-size:initial}.cart-coupon-wrapper{padding:.75rem}.cart-coupon-wrapper h5{font-family:inherit;font-size:1em}.cart-coupon-wrapper table{width:100%}.cart-coupon-wrapper td:first-child{width:75%}.cart-coupon-wrapper .form-input-group .btn{padding:.75em;font-size:1.125em}.cart-coupon{margin:5% 0}.coupon-remove{font-size:1.5em}.coupon-form{margin:20px 0}.coupon-form .form-input-group{flex-wrap:wrap;width:auto;display:flex}.coupon-form .form-input-group input{background-color:#fff;border:1px solid #e2e8f0;flex:75%;padding:.8rem 1rem}.coupon-form .form-input-group .cart-btn{text-align:center;white-space:nowrap;flex:1;margin-top:10px}.coupon-code{font-size:initial;text-align:center;white-space:nowrap;vertical-align:baseline;color:#fff;background-color:#22c55e;padding:.1em .5em;font-weight:700;line-height:1}.loader{margin:25% 0}.loader .loading{text-align:center;margin:30px;padding:25px}.loader .loading:before{content:"";border-style:solid;border-width:4px;border-color:var(--primary-color) #eee #eee;border-radius:100%;width:50px;height:50px;margin:-25px auto auto -25px;animation:.7s linear infinite rotation;position:absolute;left:50%}@keyframes rotation{0%{transform:rotate(0)}to{transform:rotate(359deg)}}.cart-empty{margin:25% 0}@media (min-width:768px) and (max-width:991.98px){.sidecart .sidecart-dialog{width:60vw}.sidecart-body{padding:1rem 1rem 6rem}.coupon-form .form-input-group input{flex:50%}.coupon-form .form-input-group .cart-btn{margin-top:0}}@media (min-width:992px){.sidecart .sidecart-dialog{width:50vw}.sidecart-body{padding:1rem 2rem 6rem}.coupon-form .form-input-group input{flex:50%}.coupon-form .form-input-group .cart-btn{margin-top:0}}@media (min-width:1200px){.sidecart .sidecart-dialog{width:45vw}}@media (min-width:1400px){.sidecart .sidecart-dialog{width:40vw}}@media (min-width:1600px){.sidecart .sidecart-dialog{width:35vw}}@media (max-width:576px){.cart-coupon-wrapper .form-input-group .btn{padding:.9em}}.cart-popover-wrapper{display:inline-block;position:relative}.cart-popover-wrapper .popover-content{opacity:0;visibility:hidden;background-color:#fff;width:auto;padding:.5rem;position:absolute;top:50px;left:-130px;transform:translateY(10px);box-shadow:0 2px 5px #00000042}.cart-popover-wrapper .popover-content p{margin-bottom:.5rem}.cart-popover-wrapper .popover-content table.popover-table{margin-top:.75em;margin-bottom:.75em;font-size:.875rem}.cart-popover-wrapper .popover-content table.popover-table tr:first-child td{border-top:none}.cart-popover-wrapper .popover-content table.popover-table tr:first-child td,.cart-popover-wrapper .popover-content table.popover-table tr:last-child td,.cart-popover-wrapper .popover-content table.popover-table tr:nth-last-child(2){font-weight:600}.cart-popover-wrapper .popover-content table.popover-table td{border-top:1px solid #dee2e6;padding:.375rem}.cart-popover-wrapper:hover .popover-content{z-index:10;opacity:1;visibility:visible;width:max-content;transition:all .5s cubic-bezier(.75,-.02,.2,.97);transform:translateY(-20px)}.cart-popover-wrapper .help-icon{cursor:pointer;vertical-align:middle;background-image:url("data:image/svg+xml,%3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' width='18px' height='18px' fill='%23606975' viewBox='0 0 24 24' style='enable-background:new 0 0 24 24;' xml:space='preserve'%3E%3Cg id='' style='opacity:0.75;'%3E%3Cg id='help'%3E%3Cpath id='circle' style='fill-rule:evenodd;clip-rule:evenodd;' d='M12.001,2.085c-5.478,0-9.916,4.438-9.916,9.916 c0,5.476,4.438,9.914,9.916,9.914c5.476,0,9.914-4.438,9.914-9.914C21.915,6.523,17.477,2.085,12.001,2.085z M12.002,20.085 c-4.465,0-8.084-3.619-8.084-8.083c0-4.465,3.619-8.084,8.084-8.084c4.464,0,8.083,3.619,8.083,8.084 C20.085,16.466,16.466,20.085,12.002,20.085z' /%3E%3Cg id='question_mark'%3E%3Cpath id='top' style='fill-rule:evenodd;clip-rule:evenodd;' d='M11.766,6.688c-2.5,0-3.219,2.188-3.219,2.188l1.411,0.854 c0,0,0.298-0.791,0.901-1.229c0.516-0.375,1.625-0.625,2.219,0.125c0.701,0.885-0.17,1.587-1.078,2.719 C11.047,12.531,11,15,11,15h1.969c0,0,0.135-2.318,1.041-3.381c0.603-0.707,1.443-1.338,1.443-2.494S14.266,6.688,11.766,6.688z' /%3E%3Crect id='bottom' x='11' y='16' style='fill-rule:evenodd;clip-rule:evenodd;' width='2' height='2' /%3E%3C/g%3E%3C/g%3E%3C/g%3E%3C/svg%3E");background-repeat:no-repeat;width:20px;height:20px;display:inline-block}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.inset-0{top:calc(var(--spacing) * 0);right:calc(var(--spacing) * 0);bottom:calc(var(--spacing) * 0);left:calc(var(--spacing) * 0)}.end{right:var(--spacing)}.top-0{top:calc(var(--spacing) * 0)}.top-full{top:100%}.bottom-0{bottom:calc(var(--spacing) * 0)}.bottom-2{bottom:calc(var(--spacing) * 2)}.left-0{left:calc(var(--spacing) * 0)}.left-1\/2{left:50%}.z-50{z-index:50}.order-1{order:1}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.mx-4{margin-left:calc(var(--spacing) * 4);margin-right:calc(var(--spacing) * 4)}.mx-auto{margin-left:auto;margin-right:auto}.my-8{margin-top:calc(var(--spacing) * 8);margin-bottom:calc(var(--spacing) * 8)}.mt-1{margin-top:calc(var(--spacing) * 1)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-6{margin-top:calc(var(--spacing) * 6)}.mt-8{margin-top:calc(var(--spacing) * 8)}.mt-10{margin-top:calc(var(--spacing) * 10)}.mt-12{margin-top:calc(var(--spacing) * 12)}.mt-16{margin-top:calc(var(--spacing) * 16)}.mt-auto{margin-top:auto}.mb-1{margin-bottom:calc(var(--spacing) * 1)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.mb-8{margin-bottom:calc(var(--spacing) * 8)}.mb-10{margin-bottom:calc(var(--spacing) * 10)}.-ml-2{margin-left:calc(var(--spacing) * -2)}.ml-1{margin-left:calc(var(--spacing) * 1)}.ml-2{margin-left:calc(var(--spacing) * 2)}.ml-10{margin-left:calc(var(--spacing) * 10)}.ml-auto{margin-left:auto}.block{display:block}.contents{display:contents}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.aspect-square{aspect-ratio:1}.h-1\.5{height:calc(var(--spacing) * 1.5)}.h-3{height:calc(var(--spacing) * 3)}.h-4{height:calc(var(--spacing) * 4)}.h-5{height:calc(var(--spacing) * 5)}.h-6{height:calc(var(--spacing) * 6)}.h-8{height:calc(var(--spacing) * 8)}.h-10{height:calc(var(--spacing) * 10)}.h-16{height:calc(var(--spacing) * 16)}.h-20{height:calc(var(--spacing) * 20)}.h-28{height:calc(var(--spacing) * 28)}.h-48{height:calc(var(--spacing) * 48)}.h-\[50vh\]{height:50vh}.h-\[65vh\]{height:65vh}.h-full{height:100%}.h-screen{height:100vh}.max-h-16{max-height:calc(var(--spacing) * 16)}.max-h-20{max-height:calc(var(--spacing) * 20)}.max-h-80{max-height:calc(var(--spacing) * 80)}.min-h-\[44px\]{min-height:44px}.min-h-\[70vh\]{min-height:70vh}.w-1\.5{width:calc(var(--spacing) * 1.5)}.w-1\/3{width:33.3333%}.w-2\/3{width:66.6667%}.w-3\/4{width:75%}.w-4\/6{width:66.6667%}.w-5\/6{width:83.3333%}.w-10{width:calc(var(--spacing) * 10)}.w-20{width:calc(var(--spacing) * 20)}.w-28{width:calc(var(--spacing) * 28)}.w-32{width:calc(var(--spacing) * 32)}.w-48{width:calc(var(--spacing) * 48)}.w-52{width:calc(var(--spacing) * 52)}.w-56{width:calc(var(--spacing) * 56)}.w-72{width:calc(var(--spacing) * 72)}.w-auto{width:auto}.w-full{width:100%}.max-w-2xl{max-width:var(--container-2xl)}.max-w-3xl{max-width:var(--container-3xl)}.max-w-4xl{max-width:var(--container-4xl)}.max-w-md{max-width:var(--container-md)}.max-w-none{max-width:none}.max-w-xl{max-width:var(--container-xl)}.min-w-0{min-width:calc(var(--spacing) * 0)}.flex-1{flex:1}.flex-shrink{flex-shrink:1}.flex-shrink-0,.shrink-0{flex-shrink:0}.-translate-x-1\/2{--tw-translate-x:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.cursor-pointer{cursor:pointer}.resize{resize:both}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-baseline{align-items:baseline}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-start{justify-content:flex-start}.gap-1{gap:calc(var(--spacing) * 1)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-6{gap:calc(var(--spacing) * 6)}.gap-8{gap:calc(var(--spacing) * 8)}.gap-content-md{gap:var(--spacing-content-md)}.space-y-1>:not(:last-child){--tw-space-y-reverse:0;margin-top:calc(calc(var(--spacing) * 1) * var(--tw-space-y-reverse));margin-bottom:calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-y-reverse)))}.space-y-2>:not(:last-child){--tw-space-y-reverse:0;margin-top:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-bottom:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}.space-y-3>:not(:last-child){--tw-space-y-reverse:0;margin-top:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-bottom:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}.space-y-4>:not(:last-child){--tw-space-y-reverse:0;margin-top:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-bottom:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}.space-y-6>:not(:last-child){--tw-space-y-reverse:0;margin-top:calc(calc(var(--spacing) * 6) * var(--tw-space-y-reverse));margin-bottom:calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-y-reverse)))}.divide-y>:not(:last-child){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}.divide-slate-100>:not(:last-child){border-color:var(--color-slate-100)}.divide-slate-200>:not(:last-child){border-color:var(--color-slate-200)}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-card{border-radius:var(--radius-card)}.rounded-full{border-radius:9999px}.rounded-md{border-radius:var(--radius-md)}.border{border-style:var(--tw-border-style);border-width:1px}.border-0{border-style:var(--tw-border-style);border-width:0}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-none{--tw-border-style:none;border-style:none}.border-amber-200{border-color:var(--color-amber-200)}.border-blue-200{border-color:var(--color-blue-200)}.border-green-200{border-color:var(--color-green-200)}.border-red-200{border-color:var(--color-red-200)}.border-slate-100{border-color:var(--color-slate-100)}.border-slate-200{border-color:var(--color-slate-200)}.border-slate-800{border-color:var(--color-slate-800)}.bg-\[var\(--primary-color\)\]{background-color:var(--primary-color)}.bg-amber-50{background-color:var(--color-amber-50)}.bg-black\/30{background-color:#0000004d}.bg-black\/30{background-color:rgba(0,0,0,0.3)}.bg-blue-50{background-color:var(--color-blue-50)}.bg-green-50{background-color:var(--color-green-50)}.bg-red-50{background-color:var(--color-red-50)}.bg-slate-50{background-color:var(--color-slate-50)}.bg-slate-100{background-color:var(--color-slate-100)}.bg-slate-200{background-color:var(--color-slate-200)}.bg-slate-300{background-color:var(--color-slate-300)}.bg-slate-800{background-color:var(--color-slate-800)}.bg-transparent{background-color:#0000}.bg-white{background-color:var(--color-white)}.object-cover{object-fit:cover}.p-2{padding:calc(var(--spacing) * 2)}.p-4{padding:calc(var(--spacing) * 4)}.p-6{padding:calc(var(--spacing) * 6)}.p-8{padding:calc(var(--spacing) * 8)}.px-2{padding-left:calc(var(--spacing) * 2);padding-right:calc(var(--spacing) * 2)}.px-3{padding-left:calc(var(--spacing) * 3);padding-right:calc(var(--spacing) * 3)}.px-4{padding-left:calc(var(--spacing) * 4);padding-right:calc(var(--spacing) * 4)}.px-5{padding-left:calc(var(--spacing) * 5);padding-right:calc(var(--spacing) * 5)}.px-6{padding-left:calc(var(--spacing) * 6);padding-right:calc(var(--spacing) * 6)}.px-8{padding-left:calc(var(--spacing) * 8);padding-right:calc(var(--spacing) * 8)}.py-0\.5{padding-top:calc(var(--spacing) * .5);padding-bottom:calc(var(--spacing) * .5)}.py-1{padding-top:calc(var(--spacing) * 1);padding-bottom:calc(var(--spacing) * 1)}.py-2{padding-top:calc(var(--spacing) * 2);padding-bottom:calc(var(--spacing) * 2)}.py-2\.5{padding-top:calc(var(--spacing) * 2.5);padding-bottom:calc(var(--spacing) * 2.5)}.py-3{padding-top:calc(var(--spacing) * 3);padding-bottom:calc(var(--spacing) * 3)}.py-4{padding-top:calc(var(--spacing) * 4);padding-bottom:calc(var(--spacing) * 4)}.py-6{padding-top:calc(var(--spacing) * 6);padding-bottom:calc(var(--spacing) * 6)}.py-8{padding-top:calc(var(--spacing) * 8);padding-bottom:calc(var(--spacing) * 8)}.py-12{padding-top:calc(var(--spacing) * 12);padding-bottom:calc(var(--spacing) * 12)}.py-16{padding-top:calc(var(--spacing) * 16);padding-bottom:calc(var(--spacing) * 16)}.py-20{padding-top:calc(var(--spacing) * 20);padding-bottom:calc(var(--spacing) * 20)}.py-band-y{padding-top:var(--spacing-band-y);padding-bottom:var(--spacing-band-y)}.py-section-y{padding-top:var(--spacing-section-y);padding-bottom:var(--spacing-section-y)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-3{padding-top:calc(var(--spacing) * 3)}.pt-6{padding-top:calc(var(--spacing) * 6)}.pl-3{padding-left:calc(var(--spacing) * 3)}.pl-4{padding-left:calc(var(--spacing) * 4)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-6xl{font-size:var(--text-6xl);line-height:var(--tw-leading,var(--text-6xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-display{font-size:var(--text-display);line-height:var(--tw-leading,var(--text-display--line-height))}.text-h1{font-size:var(--text-h1);line-height:var(--tw-leading,var(--text-h1--line-height))}.text-h2{font-size:var(--text-h2);line-height:var(--tw-leading,var(--text-h2--line-height))}.text-h3{font-size:var(--text-h3);line-height:var(--tw-leading,var(--text-h3--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.leading-5{--tw-leading:calc(var(--spacing) * 5);line-height:calc(var(--spacing) * 5)}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.leading-tight{--tw-leading:var(--leading-tight);line-height:var(--leading-tight)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.text-amber-400{color:var(--color-amber-400)}.text-amber-800{color:var(--color-amber-800)}.text-blue-800{color:var(--color-blue-800)}.text-current{color:currentColor}.text-green-600{color:var(--color-green-600)}.text-green-800{color:var(--color-green-800)}.text-red-400{color:var(--color-red-400)}.text-red-500{color:var(--color-red-500)}.text-red-800{color:var(--color-red-800)}.text-slate-200{color:var(--color-slate-200)}.text-slate-300{color:var(--color-slate-300)}.text-slate-400{color:var(--color-slate-400)}.text-slate-500{color:var(--color-slate-500)}.text-slate-600{color:var(--color-slate-600)}.text-slate-700{color:var(--color-slate-700)}.text-slate-800{color:var(--color-slate-800)}.text-slate-900{color:var(--color-slate-900)}.text-white{color:var(--color-white)}.text-white\/70{color:#ffffffb3}.text-white\/70{color:rgba(255,255,255,0.7)}.uppercase{text-transform:uppercase}.not-italic{font-style:normal}.line-through{text-decoration-line:line-through}.underline{text-decoration-line:underline}.accent-\[var\(--primary-color\)\]{accent-color:var(--primary-color)}.opacity-90{opacity:.9}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-2xl{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-300{--tw-duration:.3s;transition-duration:.3s}.outline-none{--tw-outline-style:none;outline-style:none}@media (hover:hover){.group-hover\:scale-105.group:hover *{--tw-scale-x:105%;--tw-scale-y:105%;--tw-scale-z:105%;scale:var(--tw-scale-x) var(--tw-scale-y)}.group-hover\:text-slate-600.group:hover *{color:var(--color-slate-600)}}.focus-within\:border-slate-400:focus-within{border-color:var(--color-slate-400)}@media (hover:hover){.hover\:border-slate-400:hover{border-color:var(--color-slate-400)}.hover\:bg-slate-50:hover{background-color:var(--color-slate-50)}.hover\:bg-slate-100:hover{background-color:var(--color-slate-100)}.hover\:bg-slate-200:hover{background-color:var(--color-slate-200)}.hover\:text-red-500:hover{color:var(--color-red-500)}.hover\:text-red-600:hover{color:var(--color-red-600)}.hover\:text-slate-600:hover{color:var(--color-slate-600)}.hover\:text-slate-700:hover{color:var(--color-slate-700)}.hover\:text-slate-800:hover{color:var(--color-slate-800)}.hover\:text-slate-900:hover{color:var(--color-slate-900)}.hover\:text-white:hover{color:var(--color-white)}.hover\:opacity-70:hover{opacity:.7}.hover\:opacity-80:hover{opacity:.8}}@media (min-width:40rem){.sm\:mb-0{margin-bottom:calc(var(--spacing) * 0)}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:flex-row{flex-direction:row}.sm\:items-center{align-items:center}.sm\:justify-between{justify-content:space-between}}@media (min-width:48rem){.md\:absolute{position:absolute}.md\:sticky{position:sticky}.md\:top-24{top:calc(var(--spacing) * 24)}.md\:left-1\/2{left:50%}.md\:order-1{order:1}.md\:order-2{order:2}.md\:order-3{order:3}.md\:order-none{order:0}.md\:col-span-1{grid-column:span 1/span 1}.md\:col-span-2{grid-column:span 2/span 2}.md\:col-span-3{grid-column:span 3/span 3}.md\:mx-8{margin-left:calc(var(--spacing) * 8);margin-right:calc(var(--spacing) * 8)}.md\:ml-auto{margin-left:auto}.md\:block{display:block}.md\:flex{display:flex}.md\:hidden{display:none}.md\:h-64{height:calc(var(--spacing) * 64)}.md\:h-\[70vh\]{height:70vh}.md\:h-\[80vh\]{height:80vh}.md\:-translate-x-1\/2{--tw-translate-x:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.md\:grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.md\:flex-row{flex-direction:row}.md\:items-center{align-items:center}.md\:justify-between{justify-content:space-between}.md\:gap-12{gap:calc(var(--spacing) * 12)}.md\:gap-content-lg{gap:var(--spacing-content-lg)}.md\:p-12{padding:calc(var(--spacing) * 12)}.md\:py-12{padding-top:calc(var(--spacing) * 12);padding-bottom:calc(var(--spacing) * 12)}.md\:py-16{padding-top:calc(var(--spacing) * 16);padding-bottom:calc(var(--spacing) * 16)}.md\:py-24{padding-top:calc(var(--spacing) * 24);padding-bottom:calc(var(--spacing) * 24)}.md\:py-32{padding-top:calc(var(--spacing) * 32);padding-bottom:calc(var(--spacing) * 32)}.md\:py-band-y-md{padding-top:var(--spacing-band-y-md);padding-bottom:var(--spacing-band-y-md)}.md\:py-section-y-md{padding-top:var(--spacing-section-y-md);padding-bottom:var(--spacing-section-y-md)}.md\:text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.md\:text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.md\:text-display-md{font-size:var(--text-display-md);line-height:var(--tw-leading,var(--text-display-md--line-height))}.md\:text-h1-md{font-size:var(--text-h1-md);line-height:var(--tw-leading,var(--text-h1-md--line-height))}.md\:text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}}.nav-item{position:relative}.nav-item:hover>.nav-submenu,.nav-item:focus-within>.nav-submenu{opacity:1;pointer-events:auto;transform:scale(1)}.nav-submenu{opacity:0;transform-origin:0 0;pointer-events:none;z-index:80;transition:all .2s ease-out;transform:scale(.98)}.spark-preview-indicator{z-index:100;color:#fff;background:#0f172a;border:1px solid #fff3;border-radius:.75rem;align-items:center;gap:.75rem;max-width:calc(100vw - 2rem);padding:.75rem 1rem;font-size:.875rem;line-height:1.25rem;display:flex;position:fixed;bottom:1rem;right:1rem;box-shadow:0 12px 32px #0f172a47}.spark-preview-indicator__message{font-weight:600}.spark-preview-indicator__exit{color:#0f172a;background:#fff;border-radius:.375rem;padding:.25rem .5rem;font-weight:700;text-decoration:none}.spark-preview-indicator__exit:focus-visible{outline-offset:3px;outline:3px solid #fbbf24}@media (hover:hover){.spark-preview-indicator__exit:hover{color:#0f172a;opacity:1;background:#e2e8f0}}@media not all and (min-width:30rem){.spark-preview-indicator{justify-content:space-between;max-width:none;bottom:.75rem;left:.75rem;right:.75rem}}.variant-option-list{flex-wrap:wrap;gap:.5rem;display:flex}.variant-option{display:inline-flex;position:relative}.variant-option input{opacity:0;cursor:pointer;width:100%;height:100%;margin:0;position:absolute;top:0;right:0;bottom:0;left:0}.variant-option-label{color:#334155;white-space:nowrap;cursor:pointer;background-color:#fff;border:1px solid #e2e8f0;justify-content:center;align-items:center;min-height:44px;padding:0 1.25rem;font-size:.875rem;line-height:1.25rem;transition:border-color .15s ease-out,background-color .15s ease-out,color .15s ease-out;display:inline-flex}.variant-option:hover .variant-option-label{border-color:#94a3b8}.variant-option input:focus-visible+.variant-option-label{outline:2px solid var(--primary-color);outline-offset:2px}.variant-option input:checked+.variant-option-label{background-color:var(--primary-color);border-color:var(--primary-color);color:#fff}[data-light-primary] .variant-option input:checked+.variant-option-label{color:#1e293b}.variant-option[data-variant-unavailable=true] input:not(:checked)+.variant-option-label{opacity:.55;color:#64748b}.variant-option[data-variant-unavailable=true] .variant-option-label:after{content:"";pointer-events:none;background-image:linear-gradient(to top right,#fff0 47.5%,#94a3b8 47.5% 52.5%,#fff0 52.5%);position:absolute;top:0;right:0;bottom:0;left:0}.variant-option input:checked+.variant-option-label:after{background-image:linear-gradient(to top right,#fff0 47.5%,#fff 47.5% 52.5%,#fff0 52.5%)}[data-light-primary] .variant-option input:checked+.variant-option-label:after{background-image:linear-gradient(to top right,#fff0 47.5%,#1e293b 47.5% 52.5%,#fff0 52.5%)}.variant-option-value{color:#0f172a;font-weight:500}.variant-size-guide{color:#475569;text-underline-offset:2px;font-size:.75rem;line-height:1rem;text-decoration:underline} \ No newline at end of file +*,:before,:after,::backdrop{--tw-translate-x:0;--tw-translate-y:0;--tw-translate-z:0;--tw-rotate-x:initial;--tw-rotate-y:initial;--tw-rotate-z:initial;--tw-skew-x:initial;--tw-skew-y:initial;--tw-space-y-reverse:0;--tw-divide-y-reverse:0;--tw-border-style:solid;--tw-leading:initial;--tw-font-weight:initial;--tw-tracking:initial;--tw-shadow:0 0 #0000;--tw-shadow-color:initial;--tw-shadow-alpha:100%;--tw-inset-shadow:0 0 #0000;--tw-inset-shadow-color:initial;--tw-inset-shadow-alpha:100%;--tw-ring-color:initial;--tw-ring-shadow:0 0 #0000;--tw-inset-ring-color:initial;--tw-inset-ring-shadow:0 0 #0000;--tw-ring-top:initial;right:initial;bottom:initial;left:initial;--tw-ring-offset-width:0px;--tw-ring-offset-color:#fff;--tw-ring-offset-shadow:0 0 #0000;--tw-outline-style:solid;--tw-blur:initial;--tw-brightness:initial;--tw-contrast:initial;--tw-grayscale:initial;--tw-hue-rotate:initial;--tw-invert:initial;--tw-opacity:initial;--tw-saturate:initial;--tw-sepia:initial;--tw-drop-shadow:initial;--tw-drop-shadow-color:initial;--tw-drop-shadow-alpha:100%;--tw-drop-shadow-size:initial;--tw-duration:initial;--tw-scale-x:1;--tw-scale-y:1;--tw-scale-z:1}:root,:host{--font-sans:-apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, "Helvetica Neue", Arial, sans-serif;--font-mono:ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace;--color-red-50:#fef2f2;--color-red-200:#ffc9c9;--color-red-400:#ff6467;--color-red-500:#fb2c36;--color-red-600:#e7000b;--color-red-800:#9f0712;--color-amber-50:#fffbeb;--color-amber-200:#fee685;--color-amber-400:#ffb900;--color-amber-800:#973c00;--color-green-50:#f0fdf4;--color-green-200:#b9f8cf;--color-green-600:#00a63e;--color-green-800:#016630;--color-blue-50:#eff6ff;--color-blue-200:#bedbff;--color-blue-800:#193cb8;--color-slate-50:#f8fafc;--color-slate-100:#f1f5f9;--color-slate-200:#e2e8f0;--color-slate-300:#cbd5e1;--color-slate-400:#94a3b8;--color-slate-500:#64748b;--color-slate-600:#475569;--color-slate-700:#334155;--color-slate-800:#1e293b;--color-slate-900:#0f172a;--color-black:#000;--color-white:#fff;--spacing:.25rem;--container-md:28rem;--container-xl:36rem;--container-2xl:42rem;--container-3xl:48rem;--container-4xl:56rem;--text-xs:.75rem;--text-xs--line-height:calc(1 / .75);--text-sm:.875rem;--text-sm--line-height:calc(1.25 / .875);--text-base:1rem;--text-base--line-height:calc(1.5 / 1);--text-lg:1.125rem;--text-lg--line-height:calc(1.75 / 1.125);--text-xl:1.25rem;--text-xl--line-height:calc(1.75 / 1.25);--text-2xl:1.5rem;--text-2xl--line-height:calc(2 / 1.5);--text-3xl:1.875rem;--text-3xl--line-height:calc(2.25 / 1.875);--text-4xl:2.25rem;--text-4xl--line-height:calc(2.5 / 2.25);--text-6xl:3.75rem;--text-6xl--line-height:1;--font-weight-normal:400;--font-weight-medium:500;--font-weight-semibold:600;--tracking-wide:.025em;--tracking-wider:.05em;--leading-tight:1.25;--leading-relaxed:1.625;--radius-md:.375rem;--default-transition-duration:.15s;--default-transition-timing-function:cubic-bezier(.4, 0, .2, 1);--default-font-family:var(--font-sans);--default-mono-font-family:var(--font-mono);--radius-card:var(--card-radius,0);--spacing-section-y:var(--section-padding-y,3rem);--spacing-section-y-md:calc(var(--section-padding-y,3rem) * 4 / 3);--spacing-band-y:calc(var(--section-padding-y,3rem) * 4 / 3);--spacing-band-y-md:calc(var(--section-padding-y,3rem) * 5 / 3);--spacing-content-md:calc(var(--content-gap,1.5rem) * 4 / 3);--spacing-content-lg:calc(var(--content-gap,1.5rem) * 2);--text-h3:calc(1.25rem * var(--heading-scale,1));--text-h3--line-height:calc(1.75 / 1.25);--text-h2:calc(1.5rem * var(--heading-scale,1));--text-h2--line-height:calc(2 / 1.5);--text-h1:calc(1.875rem * var(--heading-scale,1));--text-h1--line-height:calc(2.25 / 1.875);--text-h1-md:calc(2.25rem * var(--heading-scale,1));--text-h1-md--line-height:calc(2.5 / 2.25);--text-display:calc(1.875rem * var(--heading-scale,1));--text-display--line-height:calc(2.25 / 1.875);--text-display-md:calc(3rem * var(--heading-scale,1));--text-display-md--line-height:1}*,:after,:before,::backdrop{box-sizing:border-box;border:0 solid;margin:0;padding:0}::file-selector-button{box-sizing:border-box;border:0 solid;margin:0;padding:0}html,:host{-webkit-text-size-adjust:100%;tab-size:4;line-height:1.5;font-family:var(--default-font-family,ui-sans-serif, system-ui, sans-serif, "Apple Color Emoji", "Segoe UI Emoji", "Segoe UI Symbol", "Noto Color Emoji");font-feature-settings:var(--default-font-feature-settings,normal);font-variation-settings:var(--default-font-variation-settings,normal);-webkit-tap-highlight-color:transparent}hr{height:0;color:inherit;border-top-width:1px}abbr[title]{-webkit-text-decoration:underline dotted;text-decoration:underline dotted}h1,h2,h3,h4,h5,h6{font-size:inherit;font-weight:inherit}a{color:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;-webkit-text-decoration:inherit;text-decoration:inherit}b,strong{font-weight:bolder}code,kbd,samp,pre{font-family:var(--default-mono-font-family,ui-monospace, SFMono-Regular, Menlo, Monaco, Consolas, "Liberation Mono", "Courier New", monospace);font-feature-settings:var(--default-mono-font-feature-settings,normal);font-variation-settings:var(--default-mono-font-variation-settings,normal);font-size:1em}small{font-size:80%}sub,sup{vertical-align:baseline;font-size:75%;line-height:0;position:relative}sub{bottom:-.25em}sup{top:-.5em}table{text-indent:0;border-color:inherit;border-collapse:collapse}:-moz-focusring{outline:auto}progress{vertical-align:baseline}summary{display:list-item}ol,ul,menu{list-style:none}img,svg,video,canvas,audio,iframe,embed,object{vertical-align:middle;display:block}img,video{max-width:100%;height:auto}button,input,select,optgroup,textarea{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}::file-selector-button{font:inherit;font-feature-settings:inherit;font-variation-settings:inherit;letter-spacing:inherit;color:inherit;opacity:1;background-color:#0000;border-radius:0}select[multiple],[size] optgroup{font-weight:bolder}select[multiple],[size] optgroup option{padding-left:20px}::file-selector-button{margin-right:4px}::placeholder{opacity:1}textarea{resize:vertical}::-webkit-search-decoration{-webkit-appearance:none}::-webkit-date-and-time-value{min-height:1lh;text-align:inherit}::-webkit-datetime-edit{display:inline-flex}::-webkit-datetime-edit-fields-wrapper{padding:0}::-webkit-datetime-edit{padding-top:0;padding-bottom:0}::-webkit-datetime-edit-year-field{padding-top:0;padding-bottom:0}::-webkit-datetime-edit-month-field{padding-top:0;padding-bottom:0}::-webkit-datetime-edit-day-field{padding-top:0;padding-bottom:0}::-webkit-datetime-edit-hour-field{padding-top:0;padding-bottom:0}::-webkit-datetime-edit-minute-field{padding-top:0;padding-bottom:0}::-webkit-datetime-edit-second-field{padding-top:0;padding-bottom:0}::-webkit-datetime-edit-millisecond-field{padding-top:0;padding-bottom:0}::-webkit-datetime-edit-meridiem-field{padding-top:0;padding-bottom:0}::-webkit-calendar-picker-indicator{line-height:1}:-moz-ui-invalid{box-shadow:none}button,input[type=button],[type=reset],[type=submit]{appearance:button}::file-selector-button{appearance:button}::-webkit-inner-spin-button{height:auto}::-webkit-outer-spin-button{height:auto}[hidden]:not([hidden=until-found]){display:none!important}:root{--primary-color:#1e293b;--accent-color:#475569}body{font-family:var(--font-sans);font-size:var(--body-size,1rem);color:#1e293b;background-color:var(--body-bg-color,#fff);line-height:1.6}h1,h2,h3,h4,h5,h6{color:#1e293b;font-weight:600;line-height:1.2}a{color:var(--primary-color);text-decoration:none}a:hover{opacity:.8}img{max-width:100%;height:auto}.btn{border-radius:var(--control-radius,.25rem);cursor:pointer;justify-content:center;align-items:center;padding:.625rem 1.5rem;font-size:.875rem;font-weight:500;line-height:1.25rem;transition:opacity .15s ease-out;display:inline-flex}.btn:hover{opacity:.9}.btn:disabled{opacity:.5;cursor:not-allowed}.btn-primary{background-color:var(--primary-color)!important;color:#fff!important;border-color:var(--primary-color)!important}.btn-accent{background-color:var(--accent-color)!important;color:#fff!important;border-color:var(--accent-color)!important}.btn-outline{color:var(--btn-color,var(--primary-color))!important;border:1px solid var(--btn-color,var(--primary-color))!important;background-color:#0000!important}.btn-secondary{color:var(--primary-color);border:1px solid var(--border-color,#e2e8f0);background-color:#0000}.btn-secondary:hover{background-color:#f8fafc}.container{width:100%;max-width:var(--container-max,1280px);margin-left:auto;margin-right:auto;padding-left:1rem;padding-right:1rem}@media (min-width:768px){.container{padding-left:1.5rem;padding-right:1.5rem}}.product-card{border-radius:var(--card-radius,0);flex-direction:column;display:flex;overflow:hidden}.product-card-bordered{border:1px solid var(--border-color,#e2e8f0)}.product-card-bordered .product-title{padding-left:1rem;padding-right:1rem}.product-card-bordered .product-price{padding-bottom:1rem;padding-left:1rem;padding-right:1rem}.product-card img{aspect-ratio:1;object-fit:cover;background-color:#f8fafc}.product-card .product-title{margin-top:.75rem;font-size:.875rem;font-weight:500}.product-card .product-price{color:#475569;margin-top:.25rem;font-size:.875rem}[data-spark-membership-price][data-membership-active=true] [data-spark-public-price]{color:#94a3b8;font-weight:400;text-decoration-line:line-through}.spark-member-price{color:#1e293b;flex-wrap:wrap;align-items:baseline;gap:.375rem;margin-top:.25rem;display:flex}.spark-member-price-label{color:var(--primary-color);text-transform:uppercase;font-size:.75rem;font-weight:600}.spark-member-price-value{font-weight:700}.spark-member-price-detail{color:#64748b;font-size:.75rem;font-weight:400}.product-card .spark-member-price,[data-spark-membership-layout=sticky] .spark-member-price{gap:.25rem;margin-top:.125rem}.product-card .spark-member-price-label,[data-spark-membership-layout=sticky] .spark-member-price-label{font-size:.6875rem}[data-spark-membership-layout=sticky][data-membership-active=true]{line-height:1.25;display:block}.product-grid{gap:var(--content-gap,1.5rem);grid-template-columns:repeat(1,1fr);display:grid}@media (min-width:768px){.product-grid{grid-template-columns:repeat(2,1fr)}}@media (min-width:1024px){.product-grid{grid-template-columns:repeat(var(--product-grid-columns,3), 1fr)}}.category-grid{gap:calc(var(--content-gap,1.5rem) * 2 / 3);grid-template-columns:repeat(1,1fr);display:grid}@media (min-width:768px){.category-grid{grid-template-columns:repeat(var(--category-grid-columns,2), 1fr)}}.skeleton-card{flex-direction:column;display:flex}.skeleton-card .skeleton-image{aspect-ratio:1;background-color:#f1f5f9;position:relative;overflow:hidden}.skeleton-card .skeleton-image:after{content:"";background:linear-gradient(90deg,#f1f5f9,#e2e8f0,#f1f5f9);animation:1.5s ease-in-out infinite shimmer;position:absolute;top:0;right:0;bottom:0;left:0}.skeleton-card .skeleton-text{background-color:#f1f5f9;height:12px;margin-top:.75rem;position:relative;overflow:hidden}.skeleton-card .skeleton-text:after{content:"";background:linear-gradient(90deg,#f1f5f9,#e2e8f0,#f1f5f9);animation:1.5s ease-in-out infinite shimmer;position:absolute;top:0;right:0;bottom:0;left:0}.skeleton-card .skeleton-text-short{background-color:#f1f5f9;width:40%;height:12px;margin-top:.25rem;position:relative;overflow:hidden}.skeleton-card .skeleton-text-short:after{content:"";background:linear-gradient(90deg,#f1f5f9,#e2e8f0,#f1f5f9);animation:1.5s ease-in-out infinite shimmer;position:absolute;top:0;right:0;bottom:0;left:0}@keyframes shimmer{0%{transform:translate(-100%)}to{transform:translate(100%)}}.cart-badge{background-color:var(--primary-color);color:#fff;text-align:center;pointer-events:none;border-radius:50%;width:16px;height:16px;font-size:10px;font-weight:600;line-height:16px;position:absolute;top:-4px;right:-4px}[data-light-primary] .cart-badge{color:#1e293b}.cart-badge[data-animate]{animation:.3s ease-out badge-pulse}@keyframes badge-pulse{0%{transform:scale(1)}50%{transform:scale(1.3)}to{transform:scale(1)}}.spark-gallery{flex-direction:column;gap:.75rem;display:flex}@media (min-width:768px){.spark-gallery-left{flex-direction:row-reverse;gap:.75rem}.spark-gallery-left .spark-gallery-main{flex:1;min-width:0}.spark-gallery-left .spark-gallery-thumbs{flex-direction:column;justify-content:flex-start;width:72px;max-height:520px;overflow:hidden auto}.spark-gallery-left .spark-gallery-thumb{flex-shrink:0;width:72px;height:72px}}.spark-gallery-main{aspect-ratio:1;background-color:#f8fafc;max-height:560px;position:relative;overflow:hidden}.spark-gallery-hero{object-fit:contain;width:100%;height:100%;transition:opacity .2s ease-out}.spark-gallery-hero.spark-gallery-fading{opacity:0}.spark-gallery-thumbs{scrollbar-width:thin;scrollbar-color:#cbd5e1 transparent;justify-content:center;gap:.5rem;padding:.25rem 0;display:flex;overflow:auto hidden}.spark-gallery-thumb{cursor:pointer;opacity:.6;background:#f8fafc;border:2px solid #0000;flex-shrink:0;width:72px;height:72px;padding:0;transition:border-color .2s ease-out,opacity .2s ease-out;overflow:hidden}.spark-gallery-thumb:hover{opacity:1;border-color:#cbd5e1}.spark-gallery-thumb-active{opacity:1!important;border-color:var(--primary-color)!important}.spark-gallery-prev,.spark-gallery-next{cursor:pointer;color:#475569;opacity:0;z-index:2;background:#ffffffe6;border:none;border-radius:50%;justify-content:center;align-items:center;width:36px;height:36px;transition:opacity .2s ease-out,background-color .15s ease-out;display:flex;position:absolute;top:50%;transform:translateY(-50%);box-shadow:0 1px 3px #0000001a}.spark-gallery-prev{left:.75rem}.spark-gallery-next{right:.75rem}.spark-gallery-prev:hover,.spark-gallery-next:hover{color:#1e293b;background:#fff}.spark-gallery:hover .spark-gallery-prev,.spark-gallery:hover .spark-gallery-next,.spark-gallery:focus-within .spark-gallery-prev,.spark-gallery:focus-within .spark-gallery-next{opacity:1}@media (hover:none){.spark-gallery-prev,.spark-gallery-next{opacity:.7}}.sticky-atc{z-index:50;background:#fff;border-top:1px solid #e2e8f0;transition:transform .25s ease-out;position:fixed;bottom:0;left:0;right:0;transform:translateY(100%);box-shadow:0 -2px 8px #0000000f}.sticky-atc-visible{transform:translateY(0)}.sticky-atc-inner{justify-content:space-between;align-items:center;gap:1rem;max-width:1280px;margin:0 auto;padding:.75rem 1rem;display:flex}.sticky-atc-info{flex-direction:column;min-width:0;display:flex}.sticky-atc-title{color:#1e293b;white-space:nowrap;text-overflow:ellipsis;font-size:.875rem;font-weight:500;overflow:hidden}.sticky-atc-price{color:#475569;font-size:.875rem}.sticky-atc-btn{white-space:nowrap;flex-shrink:0;padding:.625rem 1.5rem}@media (min-width:1024px){.sticky-atc{display:none}}.search-overlay{z-index:60;opacity:0;visibility:hidden;justify-content:center;align-items:flex-start;transition:opacity .2s ease-out,visibility .2s ease-out;display:flex;position:fixed;top:0;right:0;bottom:0;left:0}.search-overlay-visible{opacity:1;visibility:visible}.search-overlay-backdrop{background:#0006;position:absolute;top:0;right:0;bottom:0;left:0}.search-overlay-content{z-index:1;width:100%;max-width:640px;margin-top:5rem;padding:0 1rem;transition:transform .2s ease-out;position:relative;transform:translateY(-12px)}.search-overlay-visible .search-overlay-content{transform:translateY(0)}.search-overlay-form{background:#fff;border:1px solid #e2e8f0;align-items:center;display:flex;box-shadow:0 8px 30px #0000001f}.search-overlay-input{color:#1e293b;background:0 0;border:none;outline:none;flex:1;padding:1rem 1.25rem;font-size:1rem}.search-overlay-input::placeholder{color:#94a3b8}.search-overlay-submit{color:#475569;cursor:pointer;background:0 0;border:none;padding:.75rem}.search-overlay-submit:hover{color:#1e293b}.search-overlay-close{color:#94a3b8;cursor:pointer;background:0 0;border:none;padding:.75rem}.search-overlay-close:hover{color:#475569}.sr-only,.skip-to-content{clip:rect(0, 0, 0, 0);white-space:nowrap;border:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.skip-to-content:focus{z-index:100;clip:auto;width:auto;height:auto;font-family:var(--font-sans);background-color:#1e293b;border:1px solid #1e293b;border-radius:2px;margin:0;padding:.625rem 1rem;font-size:.875rem;font-weight:500;line-height:1.25rem;top:1rem;left:1rem;overflow:visible;color:#fff!important}@media (min-width:1024px){.catalogue-layout{grid-template-columns:15rem minmax(0,1fr);align-items:start;gap:2rem;display:grid}}.catalogue-rail{display:none}@media (min-width:1024px){.catalogue-rail{display:block;position:sticky;top:5.5rem}}.catalogue-filter-inline{margin-bottom:1.5rem;display:flex}@media (min-width:1024px){.catalogue-filter-inline{display:none}}.catalogue-filter-button{color:#1e293b;cursor:pointer;background:#fff;border:1px solid #e2e8f0;border-radius:.25rem;justify-content:center;align-items:center;gap:.5rem;width:100%;min-height:44px;padding:.625rem 1rem;font-family:inherit;font-size:.875rem;font-weight:500;line-height:1.25rem;display:inline-flex}.catalogue-bar{z-index:50;visibility:hidden;pointer-events:none;background:#fff;border-top:1px solid #e2e8f0;transition:transform .25s ease-in,visibility 0s linear .25s;position:fixed;bottom:0;left:0;right:0;transform:translateY(100%)}.catalogue-bar-visible{visibility:visible;pointer-events:auto;transition:transform .25s ease-out;transform:translateY(0)}.catalogue-bar-inner{align-items:stretch;gap:.75rem;max-width:1280px;margin:0 auto;padding:.75rem 1rem;display:flex}.catalogue-bar-control{flex:1 1 0;min-width:0}.catalogue-bar-count{background:var(--primary-color);color:#fff;border-radius:9999px;justify-content:center;align-items:center;min-width:1.25rem;height:1.25rem;padding:0 .25rem;font-size:10px;font-weight:600;line-height:1;display:inline-flex}@media (min-width:1024px){.catalogue-bar{display:none}}.filter-drawer{z-index:60;visibility:hidden;pointer-events:none;position:fixed;top:0;right:0;bottom:0;left:0}.filter-drawer-visible{visibility:visible;pointer-events:auto}.filter-drawer-backdrop{opacity:0;background:#0000004d;transition:opacity .2s ease-out;position:absolute;top:0;right:0;bottom:0;left:0}.filter-drawer-visible .filter-drawer-backdrop{opacity:1}.filter-drawer-panel{background:#fff;border-top:1px solid #e2e8f0;flex-direction:column;max-height:85vh;transition:transform .25s ease-out;display:flex;position:absolute;bottom:0;left:0;right:0;transform:translateY(100%)}.filter-drawer-visible .filter-drawer-panel{transform:translateY(0)}.filter-drawer-header{border-bottom:1px solid #e2e8f0;justify-content:space-between;align-items:center;gap:1rem;padding:.75rem 1rem;display:flex}.filter-drawer-title{color:#1e293b;font-size:.875rem;font-weight:600}.filter-drawer-close{color:#475569;cursor:pointer;background:0 0;border:none;justify-content:center;align-items:center;width:44px;height:44px;margin-right:-.75rem;display:inline-flex}.filter-drawer-body{padding:1rem;overflow-y:auto}@media (min-width:1024px){.filter-drawer{display:none}}:focus-visible{outline:2px solid var(--primary-color);outline-offset:2px}@media print{header,footer,#announcement-bar,.sidecart,.breadcrumb,nav[aria-label=breadcrumb]{display:none!important}.container{max-width:100%;padding:0}body{color:#000;background:#fff;font-size:12pt}a{color:#000;text-decoration:underline}img{max-width:100%}.product-grid{grid-template-columns:repeat(2,1fr)}}.sidecart-open{overflow:hidden}.sidecart-open .sidecart{overflow-x:hidden}.sidecart{outline:0;font-family:inherit;position:absolute;top:0;overflow:hidden}.sidecart .sidecart-dialog{width:100vw;transition:all .6s cubic-bezier(.75,0,.55,1);right:-100vw}.sidecart.show .sidecart-dialog{transition:all .6s cubic-bezier(.33,0,0,1);right:0}.sidecart .cart-btn{color:#1e293b;cursor:pointer;text-align:center;white-space:nowrap;-webkit-user-select:none;user-select:none;background-color:#e2e8f0;border:1px solid #0000;justify-content:center;padding:.8rem 1rem;transition:background-color .15s ease-out}.sidecart .cart-btn:hover{background-color:#cbd5e1;border:1px solid #0000}.sidecart .cart-btn:focus:not(:active){box-shadow:0 0 0 2px var(--primary-color)}.sidecart .text-right{text-align:right}.sidecart .text-center{text-align:center}.sidecart .error-text{color:#ef4444;font-size:small}.sidecart a{color:#1e293b}.sidecart a:hover{color:#475569}.sidecart button{-webkit-appearance:none;cursor:pointer;background-color:#0000;border:0;padding:0;font-family:inherit}.sidecart button.close{color:#475569;float:right;font-weight:400;line-height:.5;transition:color .15s ease-out}.sidecart button.close:hover{color:#1e293b}.sidecart-dialog{pointer-events:none;z-index:99999;height:100%;margin:auto;position:fixed}.sidecart-content{pointer-events:auto;background-color:#fff;background-clip:padding-box;border:none;border-radius:0;outline:0;flex-direction:column;height:100%;display:flex;position:relative;overflow-y:auto;box-shadow:0 0 1px 1px #8890c333}.sidecart-header{background-color:#fff;border-bottom:none;justify-content:center;align-items:center;height:60px;padding:1rem;display:flex;position:relative}.sidecart-header button.close{color:#475569;font-size:2rem;position:absolute;right:1rem}.sidecart-header button.close:hover{color:#1e293b}.sidecart-title{text-align:center;margin-bottom:0;font-weight:700}#basket-content-div{overflow-y:auto}.sidecart-body{position:inherit;flex:auto;padding:.5rem .5rem 6rem;overflow-y:auto}.sidecart-footer{background-color:#fff;border-top:1px solid #e2e8f0;width:auto;padding:1rem;display:flex;bottom:0}.sidecart-footer .cart-btn{text-align:center;cursor:pointer;border:1px solid #0000;border-radius:.3rem;width:100%;height:auto;padding:1.25rem;font-size:24px;font-weight:700;text-decoration:none;display:inline-block;background-color:var(--primary-color)!important;color:#fff!important}.sidecart-footer .cart-btn:hover{border:1px solid #0000;background-color:var(--primary-color-darker)!important;color:#fff!important}.sidecart .form-control{color:#1e293b;appearance:none;background-color:#fff;background-image:url("data:image/svg+xml,%3csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16'%3e%3cpath fill='none' stroke='%23475569' stroke-linecap='round' stroke-linejoin='round' stroke-width='2' d='M2 5l6 6 6-6'/%3e%3c/svg%3e");background-position:right .5rem center;background-repeat:no-repeat;background-size:14px 10px;border:1px solid #e2e8f0;border-radius:.25rem;width:100%;padding:.25rem 2rem .25rem .5rem;font-size:.875rem;line-height:1.5;display:block}.cart-items{margin:0 0 20px}.cart-item{box-shadow:none;border-bottom:1px solid #e2e8f0;flex-flow:row;justify-content:flex-start;margin:0 0 .75rem;padding:.75rem;display:flex}.cart-item:first-child{border-top:1px solid #e2e8f0}.cart-item img{width:100%}.cart-item .item-img{width:110px;margin-right:5%;padding:5px}.cart-item .item-details{width:100%;padding-top:.5rem}.cart-item .item-title{margin-bottom:.5rem;font-family:inherit;font-size:1rem;font-weight:500;line-height:1.2;display:block}.cart-item .item-description{flex-flow:wrap;align-items:center;width:100%;margin:0 0 .5em;display:flex}.cart-item .item-description .basket-action{flex-direction:column;flex:40%;height:fit-content;margin-right:20px;display:flex}.cart-item .item-description .basket-action:last-child{margin-right:0}.cart-item .item-description label{margin-bottom:0;display:inline-block}.cart-item .item-description button.close{font-size:smaller}.cart-item .item-price{width:100%}.cart-item .close{font-size:1.5rem}table.sidecart-table{background-color:#0000;width:100%}table.sidecart-table td{vertical-align:top;border-top:1px solid #e2e8f0;padding:.75rem}table.sidecart-table tr:first-child td{border-top:none}table.sidecart-table.table-sm td{padding:.15rem;font-size:small}.cart-totals{font-size:initial}.cart-coupon-wrapper{padding:.75rem}.cart-coupon-wrapper h5{font-family:inherit;font-size:1em}.cart-coupon-wrapper table{width:100%}.cart-coupon-wrapper td:first-child{width:75%}.cart-coupon-wrapper .form-input-group .btn{padding:.75em;font-size:1.125em}.cart-coupon{margin:5% 0}.coupon-remove{font-size:1.5em}.coupon-form{margin:20px 0}.coupon-form .form-input-group{flex-wrap:wrap;width:auto;display:flex}.coupon-form .form-input-group input{background-color:#fff;border:1px solid #e2e8f0;flex:75%;padding:.8rem 1rem}.coupon-form .form-input-group .cart-btn{text-align:center;white-space:nowrap;flex:1;margin-top:10px}.coupon-code{font-size:initial;text-align:center;white-space:nowrap;vertical-align:baseline;color:#fff;background-color:#22c55e;padding:.1em .5em;font-weight:700;line-height:1}.loader{margin:25% 0}.loader .loading{text-align:center;margin:30px;padding:25px}.loader .loading:before{content:"";border-style:solid;border-width:4px;border-color:var(--primary-color) #eee #eee;border-radius:100%;width:50px;height:50px;margin:-25px auto auto -25px;animation:.7s linear infinite rotation;position:absolute;left:50%}@keyframes rotation{0%{transform:rotate(0)}to{transform:rotate(359deg)}}.cart-empty{margin:25% 0}@media (min-width:768px) and (max-width:991.98px){.sidecart .sidecart-dialog{width:60vw}.sidecart-body{padding:1rem 1rem 6rem}.coupon-form .form-input-group input{flex:50%}.coupon-form .form-input-group .cart-btn{margin-top:0}}@media (min-width:992px){.sidecart .sidecart-dialog{width:50vw}.sidecart-body{padding:1rem 2rem 6rem}.coupon-form .form-input-group input{flex:50%}.coupon-form .form-input-group .cart-btn{margin-top:0}}@media (min-width:1200px){.sidecart .sidecart-dialog{width:45vw}}@media (min-width:1400px){.sidecart .sidecart-dialog{width:40vw}}@media (min-width:1600px){.sidecart .sidecart-dialog{width:35vw}}@media (max-width:576px){.cart-coupon-wrapper .form-input-group .btn{padding:.9em}}.cart-popover-wrapper{display:inline-block;position:relative}.cart-popover-wrapper .popover-content{opacity:0;visibility:hidden;background-color:#fff;width:auto;padding:.5rem;position:absolute;top:50px;left:-130px;transform:translateY(10px);box-shadow:0 2px 5px #00000042}.cart-popover-wrapper .popover-content p{margin-bottom:.5rem}.cart-popover-wrapper .popover-content table.popover-table{margin-top:.75em;margin-bottom:.75em;font-size:.875rem}.cart-popover-wrapper .popover-content table.popover-table tr:first-child td{border-top:none}.cart-popover-wrapper .popover-content table.popover-table tr:first-child td,.cart-popover-wrapper .popover-content table.popover-table tr:last-child td,.cart-popover-wrapper .popover-content table.popover-table tr:nth-last-child(2){font-weight:600}.cart-popover-wrapper .popover-content table.popover-table td{border-top:1px solid #dee2e6;padding:.375rem}.cart-popover-wrapper:hover .popover-content{z-index:10;opacity:1;visibility:visible;width:max-content;transition:all .5s cubic-bezier(.75,-.02,.2,.97);transform:translateY(-20px)}.cart-popover-wrapper .help-icon{cursor:pointer;vertical-align:middle;background-image:url("data:image/svg+xml,%3Csvg version='1.1' xmlns='http://www.w3.org/2000/svg' xmlns:xlink='http://www.w3.org/1999/xlink' x='0px' y='0px' width='18px' height='18px' fill='%23606975' viewBox='0 0 24 24' style='enable-background:new 0 0 24 24;' xml:space='preserve'%3E%3Cg id='' style='opacity:0.75;'%3E%3Cg id='help'%3E%3Cpath id='circle' style='fill-rule:evenodd;clip-rule:evenodd;' d='M12.001,2.085c-5.478,0-9.916,4.438-9.916,9.916 c0,5.476,4.438,9.914,9.916,9.914c5.476,0,9.914-4.438,9.914-9.914C21.915,6.523,17.477,2.085,12.001,2.085z M12.002,20.085 c-4.465,0-8.084-3.619-8.084-8.083c0-4.465,3.619-8.084,8.084-8.084c4.464,0,8.083,3.619,8.083,8.084 C20.085,16.466,16.466,20.085,12.002,20.085z' /%3E%3Cg id='question_mark'%3E%3Cpath id='top' style='fill-rule:evenodd;clip-rule:evenodd;' d='M11.766,6.688c-2.5,0-3.219,2.188-3.219,2.188l1.411,0.854 c0,0,0.298-0.791,0.901-1.229c0.516-0.375,1.625-0.625,2.219,0.125c0.701,0.885-0.17,1.587-1.078,2.719 C11.047,12.531,11,15,11,15h1.969c0,0,0.135-2.318,1.041-3.381c0.603-0.707,1.443-1.338,1.443-2.494S14.266,6.688,11.766,6.688z' /%3E%3Crect id='bottom' x='11' y='16' style='fill-rule:evenodd;clip-rule:evenodd;' width='2' height='2' /%3E%3C/g%3E%3C/g%3E%3C/g%3E%3C/svg%3E");background-repeat:no-repeat;width:20px;height:20px;display:inline-block}.visible{visibility:visible}.sr-only{clip-path:inset(50%);white-space:nowrap;border-width:0;width:1px;height:1px;margin:-1px;padding:0;position:absolute;overflow:hidden}.absolute{position:absolute}.fixed{position:fixed}.relative{position:relative}.static{position:static}.sticky{position:sticky}.inset-0{top:calc(var(--spacing) * 0);right:calc(var(--spacing) * 0);bottom:calc(var(--spacing) * 0);left:calc(var(--spacing) * 0)}.end{right:var(--spacing)}.top-0{top:calc(var(--spacing) * 0)}.top-full{top:100%}.bottom-0{bottom:calc(var(--spacing) * 0)}.bottom-2{bottom:calc(var(--spacing) * 2)}.left-0{left:calc(var(--spacing) * 0)}.left-1\/2{left:50%}.z-50{z-index:50}.order-1{order:1}.container{width:100%}@media (min-width:40rem){.container{max-width:40rem}}@media (min-width:48rem){.container{max-width:48rem}}@media (min-width:64rem){.container{max-width:64rem}}@media (min-width:80rem){.container{max-width:80rem}}@media (min-width:96rem){.container{max-width:96rem}}.mx-4{margin-left:calc(var(--spacing) * 4);margin-right:calc(var(--spacing) * 4)}.mx-auto{margin-left:auto;margin-right:auto}.my-8{margin-top:calc(var(--spacing) * 8);margin-bottom:calc(var(--spacing) * 8)}.mt-1{margin-top:calc(var(--spacing) * 1)}.mt-2{margin-top:calc(var(--spacing) * 2)}.mt-3{margin-top:calc(var(--spacing) * 3)}.mt-4{margin-top:calc(var(--spacing) * 4)}.mt-6{margin-top:calc(var(--spacing) * 6)}.mt-8{margin-top:calc(var(--spacing) * 8)}.mt-10{margin-top:calc(var(--spacing) * 10)}.mt-12{margin-top:calc(var(--spacing) * 12)}.mt-16{margin-top:calc(var(--spacing) * 16)}.mt-auto{margin-top:auto}.mb-1{margin-bottom:calc(var(--spacing) * 1)}.mb-2{margin-bottom:calc(var(--spacing) * 2)}.mb-3{margin-bottom:calc(var(--spacing) * 3)}.mb-4{margin-bottom:calc(var(--spacing) * 4)}.mb-6{margin-bottom:calc(var(--spacing) * 6)}.mb-8{margin-bottom:calc(var(--spacing) * 8)}.mb-10{margin-bottom:calc(var(--spacing) * 10)}.-ml-2{margin-left:calc(var(--spacing) * -2)}.ml-1{margin-left:calc(var(--spacing) * 1)}.ml-2{margin-left:calc(var(--spacing) * 2)}.ml-10{margin-left:calc(var(--spacing) * 10)}.ml-auto{margin-left:auto}.block{display:block}.contents{display:contents}.flex{display:flex}.grid{display:grid}.hidden{display:none}.inline{display:inline}.inline-block{display:inline-block}.inline-flex{display:inline-flex}.aspect-\[4\/3\]{aspect-ratio:4/3}.aspect-\[4\/5\]{aspect-ratio:4/5}.aspect-square{aspect-ratio:1}.h-1\.5{height:calc(var(--spacing) * 1.5)}.h-3{height:calc(var(--spacing) * 3)}.h-4{height:calc(var(--spacing) * 4)}.h-5{height:calc(var(--spacing) * 5)}.h-6{height:calc(var(--spacing) * 6)}.h-8{height:calc(var(--spacing) * 8)}.h-10{height:calc(var(--spacing) * 10)}.h-16{height:calc(var(--spacing) * 16)}.h-20{height:calc(var(--spacing) * 20)}.h-28{height:calc(var(--spacing) * 28)}.h-48{height:calc(var(--spacing) * 48)}.h-\[50vh\]{height:50vh}.h-\[65vh\]{height:65vh}.h-auto{height:auto}.h-full{height:100%}.h-screen{height:100vh}.max-h-16{max-height:calc(var(--spacing) * 16)}.max-h-20{max-height:calc(var(--spacing) * 20)}.max-h-80{max-height:calc(var(--spacing) * 80)}.min-h-\[44px\]{min-height:44px}.min-h-\[70vh\]{min-height:70vh}.w-1\.5{width:calc(var(--spacing) * 1.5)}.w-1\/3{width:33.3333%}.w-1\/4{width:25%}.w-2\/3{width:66.6667%}.w-3\/4{width:75%}.w-4\/6{width:66.6667%}.w-5\/6{width:83.3333%}.w-10{width:calc(var(--spacing) * 10)}.w-20{width:calc(var(--spacing) * 20)}.w-28{width:calc(var(--spacing) * 28)}.w-32{width:calc(var(--spacing) * 32)}.w-48{width:calc(var(--spacing) * 48)}.w-52{width:calc(var(--spacing) * 52)}.w-56{width:calc(var(--spacing) * 56)}.w-72{width:calc(var(--spacing) * 72)}.w-auto{width:auto}.w-full{width:100%}.max-w-2xl{max-width:var(--container-2xl)}.max-w-3xl{max-width:var(--container-3xl)}.max-w-4xl{max-width:var(--container-4xl)}.max-w-md{max-width:var(--container-md)}.max-w-none{max-width:none}.max-w-xl{max-width:var(--container-xl)}.min-w-0{min-width:calc(var(--spacing) * 0)}.flex-1{flex:1}.flex-shrink{flex-shrink:1}.flex-shrink-0,.shrink-0{flex-shrink:0}.-translate-x-1\/2{--tw-translate-x:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.transform{transform:var(--tw-rotate-x,) var(--tw-rotate-y,) var(--tw-rotate-z,) var(--tw-skew-x,) var(--tw-skew-y,)}.cursor-pointer{cursor:pointer}.resize{resize:both}.grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.flex-col{flex-direction:column}.flex-wrap{flex-wrap:wrap}.items-baseline{align-items:baseline}.items-center{align-items:center}.items-end{align-items:flex-end}.items-start{align-items:flex-start}.justify-between{justify-content:space-between}.justify-center{justify-content:center}.justify-end{justify-content:flex-end}.justify-start{justify-content:flex-start}.gap-1{gap:calc(var(--spacing) * 1)}.gap-2{gap:calc(var(--spacing) * 2)}.gap-3{gap:calc(var(--spacing) * 3)}.gap-4{gap:calc(var(--spacing) * 4)}.gap-6{gap:calc(var(--spacing) * 6)}.gap-8{gap:calc(var(--spacing) * 8)}.gap-content-md{gap:var(--spacing-content-md)}.space-y-1>:not(:last-child){--tw-space-y-reverse:0;margin-top:calc(calc(var(--spacing) * 1) * var(--tw-space-y-reverse));margin-bottom:calc(calc(var(--spacing) * 1) * calc(1 - var(--tw-space-y-reverse)))}.space-y-2>:not(:last-child){--tw-space-y-reverse:0;margin-top:calc(calc(var(--spacing) * 2) * var(--tw-space-y-reverse));margin-bottom:calc(calc(var(--spacing) * 2) * calc(1 - var(--tw-space-y-reverse)))}.space-y-3>:not(:last-child){--tw-space-y-reverse:0;margin-top:calc(calc(var(--spacing) * 3) * var(--tw-space-y-reverse));margin-bottom:calc(calc(var(--spacing) * 3) * calc(1 - var(--tw-space-y-reverse)))}.space-y-4>:not(:last-child){--tw-space-y-reverse:0;margin-top:calc(calc(var(--spacing) * 4) * var(--tw-space-y-reverse));margin-bottom:calc(calc(var(--spacing) * 4) * calc(1 - var(--tw-space-y-reverse)))}.space-y-6>:not(:last-child){--tw-space-y-reverse:0;margin-top:calc(calc(var(--spacing) * 6) * var(--tw-space-y-reverse));margin-bottom:calc(calc(var(--spacing) * 6) * calc(1 - var(--tw-space-y-reverse)))}.divide-y>:not(:last-child){--tw-divide-y-reverse:0;border-bottom-style:var(--tw-border-style);border-top-style:var(--tw-border-style);border-top-width:calc(1px * var(--tw-divide-y-reverse));border-bottom-width:calc(1px * calc(1 - var(--tw-divide-y-reverse)))}.divide-slate-100>:not(:last-child){border-color:var(--color-slate-100)}.divide-slate-200>:not(:last-child){border-color:var(--color-slate-200)}.overflow-hidden{overflow:hidden}.overflow-y-auto{overflow-y:auto}.rounded{border-radius:.25rem}.rounded-card{border-radius:var(--radius-card)}.rounded-full{border-radius:9999px}.rounded-md{border-radius:var(--radius-md)}.border{border-style:var(--tw-border-style);border-width:1px}.border-0{border-style:var(--tw-border-style);border-width:0}.border-2{border-style:var(--tw-border-style);border-width:2px}.border-t{border-top-style:var(--tw-border-style);border-top-width:1px}.border-b{border-bottom-style:var(--tw-border-style);border-bottom-width:1px}.border-dashed{--tw-border-style:dashed;border-style:dashed}.border-none{--tw-border-style:none;border-style:none}.border-amber-200{border-color:var(--color-amber-200)}.border-blue-200{border-color:var(--color-blue-200)}.border-green-200{border-color:var(--color-green-200)}.border-red-200{border-color:var(--color-red-200)}.border-slate-100{border-color:var(--color-slate-100)}.border-slate-200{border-color:var(--color-slate-200)}.border-slate-800{border-color:var(--color-slate-800)}.bg-\[var\(--primary-color\)\]{background-color:var(--primary-color)}.bg-amber-50{background-color:var(--color-amber-50)}.bg-black\/30{background-color:#0000004d}.bg-black\/30{background-color:rgba(0,0,0,0.3)}.bg-blue-50{background-color:var(--color-blue-50)}.bg-green-50{background-color:var(--color-green-50)}.bg-red-50{background-color:var(--color-red-50)}.bg-slate-50{background-color:var(--color-slate-50)}.bg-slate-100{background-color:var(--color-slate-100)}.bg-slate-200{background-color:var(--color-slate-200)}.bg-slate-300{background-color:var(--color-slate-300)}.bg-slate-800{background-color:var(--color-slate-800)}.bg-transparent{background-color:#0000}.bg-white{background-color:var(--color-white)}.object-cover{object-fit:cover}.p-2{padding:calc(var(--spacing) * 2)}.p-4{padding:calc(var(--spacing) * 4)}.p-6{padding:calc(var(--spacing) * 6)}.p-8{padding:calc(var(--spacing) * 8)}.px-2{padding-left:calc(var(--spacing) * 2);padding-right:calc(var(--spacing) * 2)}.px-3{padding-left:calc(var(--spacing) * 3);padding-right:calc(var(--spacing) * 3)}.px-4{padding-left:calc(var(--spacing) * 4);padding-right:calc(var(--spacing) * 4)}.px-5{padding-left:calc(var(--spacing) * 5);padding-right:calc(var(--spacing) * 5)}.px-6{padding-left:calc(var(--spacing) * 6);padding-right:calc(var(--spacing) * 6)}.px-8{padding-left:calc(var(--spacing) * 8);padding-right:calc(var(--spacing) * 8)}.py-0\.5{padding-top:calc(var(--spacing) * .5);padding-bottom:calc(var(--spacing) * .5)}.py-1{padding-top:calc(var(--spacing) * 1);padding-bottom:calc(var(--spacing) * 1)}.py-2{padding-top:calc(var(--spacing) * 2);padding-bottom:calc(var(--spacing) * 2)}.py-2\.5{padding-top:calc(var(--spacing) * 2.5);padding-bottom:calc(var(--spacing) * 2.5)}.py-3{padding-top:calc(var(--spacing) * 3);padding-bottom:calc(var(--spacing) * 3)}.py-4{padding-top:calc(var(--spacing) * 4);padding-bottom:calc(var(--spacing) * 4)}.py-6{padding-top:calc(var(--spacing) * 6);padding-bottom:calc(var(--spacing) * 6)}.py-8{padding-top:calc(var(--spacing) * 8);padding-bottom:calc(var(--spacing) * 8)}.py-12{padding-top:calc(var(--spacing) * 12);padding-bottom:calc(var(--spacing) * 12)}.py-16{padding-top:calc(var(--spacing) * 16);padding-bottom:calc(var(--spacing) * 16)}.py-20{padding-top:calc(var(--spacing) * 20);padding-bottom:calc(var(--spacing) * 20)}.py-band-y{padding-top:var(--spacing-band-y);padding-bottom:var(--spacing-band-y)}.py-section-y{padding-top:var(--spacing-section-y);padding-bottom:var(--spacing-section-y)}.pt-2{padding-top:calc(var(--spacing) * 2)}.pt-3{padding-top:calc(var(--spacing) * 3)}.pt-6{padding-top:calc(var(--spacing) * 6)}.pl-3{padding-left:calc(var(--spacing) * 3)}.pl-4{padding-left:calc(var(--spacing) * 4)}.text-center{text-align:center}.text-left{text-align:left}.text-right{text-align:right}.text-2xl{font-size:var(--text-2xl);line-height:var(--tw-leading,var(--text-2xl--line-height))}.text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.text-6xl{font-size:var(--text-6xl);line-height:var(--tw-leading,var(--text-6xl--line-height))}.text-base{font-size:var(--text-base);line-height:var(--tw-leading,var(--text-base--line-height))}.text-display{font-size:var(--text-display);line-height:var(--tw-leading,var(--text-display--line-height))}.text-h1{font-size:var(--text-h1);line-height:var(--tw-leading,var(--text-h1--line-height))}.text-h2{font-size:var(--text-h2);line-height:var(--tw-leading,var(--text-h2--line-height))}.text-h3{font-size:var(--text-h3);line-height:var(--tw-leading,var(--text-h3--line-height))}.text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}.text-sm{font-size:var(--text-sm);line-height:var(--tw-leading,var(--text-sm--line-height))}.text-xl{font-size:var(--text-xl);line-height:var(--tw-leading,var(--text-xl--line-height))}.text-xs{font-size:var(--text-xs);line-height:var(--tw-leading,var(--text-xs--line-height))}.leading-5{--tw-leading:calc(var(--spacing) * 5);line-height:calc(var(--spacing) * 5)}.leading-relaxed{--tw-leading:var(--leading-relaxed);line-height:var(--leading-relaxed)}.leading-tight{--tw-leading:var(--leading-tight);line-height:var(--leading-tight)}.font-medium{--tw-font-weight:var(--font-weight-medium);font-weight:var(--font-weight-medium)}.font-normal{--tw-font-weight:var(--font-weight-normal);font-weight:var(--font-weight-normal)}.font-semibold{--tw-font-weight:var(--font-weight-semibold);font-weight:var(--font-weight-semibold)}.tracking-wide{--tw-tracking:var(--tracking-wide);letter-spacing:var(--tracking-wide)}.tracking-wider{--tw-tracking:var(--tracking-wider);letter-spacing:var(--tracking-wider)}.text-amber-400{color:var(--color-amber-400)}.text-amber-800{color:var(--color-amber-800)}.text-blue-800{color:var(--color-blue-800)}.text-current{color:currentColor}.text-green-600{color:var(--color-green-600)}.text-green-800{color:var(--color-green-800)}.text-red-400{color:var(--color-red-400)}.text-red-500{color:var(--color-red-500)}.text-red-800{color:var(--color-red-800)}.text-slate-200{color:var(--color-slate-200)}.text-slate-300{color:var(--color-slate-300)}.text-slate-400{color:var(--color-slate-400)}.text-slate-500{color:var(--color-slate-500)}.text-slate-600{color:var(--color-slate-600)}.text-slate-700{color:var(--color-slate-700)}.text-slate-800{color:var(--color-slate-800)}.text-slate-900{color:var(--color-slate-900)}.text-white{color:var(--color-white)}.text-white\/70{color:#ffffffb3}.text-white\/70{color:rgba(255,255,255,0.7)}.uppercase{text-transform:uppercase}.not-italic{font-style:normal}.line-through{text-decoration-line:line-through}.underline{text-decoration-line:underline}.accent-\[var\(--primary-color\)\]{accent-color:var(--primary-color)}.opacity-80{opacity:.8}.opacity-90{opacity:.9}.shadow{--tw-shadow:0 1px 3px 0 var(--tw-shadow-color,#0000001a), 0 1px 2px -1px var(--tw-shadow-color,#0000001a);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.shadow-2xl{--tw-shadow:0 25px 50px -12px var(--tw-shadow-color,#00000040);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.ring{--tw-ring-shadow:var(--tw-ring-inset,) 0 0 0 calc(1px + var(--tw-ring-offset-width)) var(--tw-ring-color,currentcolor);box-shadow:var(--tw-inset-shadow), var(--tw-inset-ring-shadow), var(--tw-ring-offset-shadow), var(--tw-ring-shadow), var(--tw-shadow)}.outline{outline-style:var(--tw-outline-style);outline-width:1px}.filter{filter:var(--tw-blur,) var(--tw-brightness,) var(--tw-contrast,) var(--tw-grayscale,) var(--tw-hue-rotate,) var(--tw-invert,) var(--tw-saturate,) var(--tw-sepia,) var(--tw-drop-shadow,)}.transition{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to,opacity,box-shadow,transform,translate,scale,rotate,filter,-webkit-backdrop-filter,backdrop-filter,display,content-visibility,overlay,pointer-events;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-colors{transition-property:color,background-color,border-color,outline-color,text-decoration-color,fill,stroke,--tw-gradient-from,--tw-gradient-via,--tw-gradient-to;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.transition-transform{transition-property:transform,translate,scale,rotate;transition-timing-function:var(--tw-ease,var(--default-transition-timing-function));transition-duration:var(--tw-duration,var(--default-transition-duration))}.duration-300{--tw-duration:.3s;transition-duration:.3s}.outline-none{--tw-outline-style:none;outline-style:none}@media (hover:hover){.group-hover\:scale-105.group:hover *{--tw-scale-x:105%;--tw-scale-y:105%;--tw-scale-z:105%;scale:var(--tw-scale-x) var(--tw-scale-y)}.group-hover\:text-slate-600.group:hover *{color:var(--color-slate-600)}}.focus-within\:border-slate-400:focus-within{border-color:var(--color-slate-400)}@media (hover:hover){.hover\:border-slate-400:hover{border-color:var(--color-slate-400)}.hover\:bg-slate-50:hover{background-color:var(--color-slate-50)}.hover\:bg-slate-100:hover{background-color:var(--color-slate-100)}.hover\:bg-slate-200:hover{background-color:var(--color-slate-200)}.hover\:text-red-500:hover{color:var(--color-red-500)}.hover\:text-red-600:hover{color:var(--color-red-600)}.hover\:text-slate-600:hover{color:var(--color-slate-600)}.hover\:text-slate-700:hover{color:var(--color-slate-700)}.hover\:text-slate-800:hover{color:var(--color-slate-800)}.hover\:text-slate-900:hover{color:var(--color-slate-900)}.hover\:text-white:hover{color:var(--color-white)}.hover\:opacity-70:hover{opacity:.7}.hover\:opacity-80:hover{opacity:.8}}@media (min-width:40rem){.sm\:mb-0{margin-bottom:calc(var(--spacing) * 0)}.sm\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.sm\:flex-row{flex-direction:row}.sm\:items-center{align-items:center}.sm\:justify-between{justify-content:space-between}}@media (min-width:48rem){.md\:absolute{position:absolute}.md\:sticky{position:sticky}.md\:top-24{top:calc(var(--spacing) * 24)}.md\:left-1\/2{left:50%}.md\:order-1{order:1}.md\:order-2{order:2}.md\:order-3{order:3}.md\:order-none{order:0}.md\:col-span-1{grid-column:span 1/span 1}.md\:col-span-2{grid-column:span 2/span 2}.md\:col-span-3{grid-column:span 3/span 3}.md\:mx-8{margin-left:calc(var(--spacing) * 8);margin-right:calc(var(--spacing) * 8)}.md\:ml-auto{margin-left:auto}.md\:block{display:block}.md\:flex{display:flex}.md\:hidden{display:none}.md\:h-64{height:calc(var(--spacing) * 64)}.md\:h-\[70vh\]{height:70vh}.md\:h-\[80vh\]{height:80vh}.md\:-translate-x-1\/2{--tw-translate-x:calc(calc(1 / 2 * 100%) * -1);translate:var(--tw-translate-x) var(--tw-translate-y)}.md\:grid-cols-1{grid-template-columns:repeat(1,minmax(0,1fr))}.md\:grid-cols-2{grid-template-columns:repeat(2,minmax(0,1fr))}.md\:grid-cols-3{grid-template-columns:repeat(3,minmax(0,1fr))}.md\:grid-cols-4{grid-template-columns:repeat(4,minmax(0,1fr))}.md\:grid-cols-5{grid-template-columns:repeat(5,minmax(0,1fr))}.md\:flex-row{flex-direction:row}.md\:items-center{align-items:center}.md\:justify-between{justify-content:space-between}.md\:gap-12{gap:calc(var(--spacing) * 12)}.md\:gap-content-lg{gap:var(--spacing-content-lg)}.md\:p-12{padding:calc(var(--spacing) * 12)}.md\:py-12{padding-top:calc(var(--spacing) * 12);padding-bottom:calc(var(--spacing) * 12)}.md\:py-16{padding-top:calc(var(--spacing) * 16);padding-bottom:calc(var(--spacing) * 16)}.md\:py-24{padding-top:calc(var(--spacing) * 24);padding-bottom:calc(var(--spacing) * 24)}.md\:py-32{padding-top:calc(var(--spacing) * 32);padding-bottom:calc(var(--spacing) * 32)}.md\:py-band-y-md{padding-top:var(--spacing-band-y-md);padding-bottom:var(--spacing-band-y-md)}.md\:py-section-y-md{padding-top:var(--spacing-section-y-md);padding-bottom:var(--spacing-section-y-md)}.md\:text-3xl{font-size:var(--text-3xl);line-height:var(--tw-leading,var(--text-3xl--line-height))}.md\:text-4xl{font-size:var(--text-4xl);line-height:var(--tw-leading,var(--text-4xl--line-height))}.md\:text-display-md{font-size:var(--text-display-md);line-height:var(--tw-leading,var(--text-display-md--line-height))}.md\:text-h1-md{font-size:var(--text-h1-md);line-height:var(--tw-leading,var(--text-h1-md--line-height))}.md\:text-lg{font-size:var(--text-lg);line-height:var(--tw-leading,var(--text-lg--line-height))}}.nav-item{position:relative}.nav-item:hover>.nav-submenu,.nav-item:focus-within>.nav-submenu{opacity:1;pointer-events:auto;transform:scale(1)}.nav-submenu{opacity:0;transform-origin:0 0;pointer-events:none;z-index:80;transition:all .2s ease-out;transform:scale(.98)}.spark-preview-indicator{z-index:100;color:#fff;background:#0f172a;border:1px solid #fff3;border-radius:.75rem;align-items:center;gap:.75rem;max-width:calc(100vw - 2rem);padding:.75rem 1rem;font-size:.875rem;line-height:1.25rem;display:flex;position:fixed;bottom:1rem;right:1rem;box-shadow:0 12px 32px #0f172a47}.spark-preview-indicator__message{font-weight:600}.spark-preview-indicator__exit{color:#0f172a;background:#fff;border-radius:.375rem;padding:.25rem .5rem;font-weight:700;text-decoration:none}.spark-preview-indicator__exit:focus-visible{outline-offset:3px;outline:3px solid #fbbf24}@media (hover:hover){.spark-preview-indicator__exit:hover{color:#0f172a;opacity:1;background:#e2e8f0}}@media not all and (min-width:30rem){.spark-preview-indicator{justify-content:space-between;max-width:none;bottom:.75rem;left:.75rem;right:.75rem}}.variant-option-list{flex-wrap:wrap;gap:.5rem;display:flex}.variant-option{display:inline-flex;position:relative}.variant-option input{opacity:0;cursor:pointer;width:100%;height:100%;margin:0;position:absolute;top:0;right:0;bottom:0;left:0}.variant-option-label{color:#334155;white-space:nowrap;cursor:pointer;background-color:#fff;border:1px solid #e2e8f0;justify-content:center;align-items:center;min-height:44px;padding:0 1.25rem;font-size:.875rem;line-height:1.25rem;transition:border-color .15s ease-out,background-color .15s ease-out,color .15s ease-out;display:inline-flex}.variant-option:hover .variant-option-label{border-color:#94a3b8}.variant-option input:focus-visible+.variant-option-label{outline:2px solid var(--primary-color);outline-offset:2px}.variant-option input:checked+.variant-option-label{background-color:var(--primary-color);border-color:var(--primary-color);color:#fff}[data-light-primary] .variant-option input:checked+.variant-option-label{color:#1e293b}.variant-option[data-variant-unavailable=true] input:not(:checked)+.variant-option-label{opacity:.55;color:#64748b}.variant-option[data-variant-unavailable=true] .variant-option-label:after{content:"";pointer-events:none;background-image:linear-gradient(to top right,#fff0 47.5%,#94a3b8 47.5% 52.5%,#fff0 52.5%);position:absolute;top:0;right:0;bottom:0;left:0}.variant-option input:checked+.variant-option-label:after{background-image:linear-gradient(to top right,#fff0 47.5%,#fff 47.5% 52.5%,#fff0 52.5%)}[data-light-primary] .variant-option input:checked+.variant-option-label:after{background-image:linear-gradient(to top right,#fff0 47.5%,#1e293b 47.5% 52.5%,#fff0 52.5%)}.variant-option-value{color:#0f172a;font-weight:500}.variant-size-guide{color:#475569;text-underline-offset:2px;font-size:.75rem;line-height:1rem;text-decoration:underline} \ No newline at end of file diff --git a/configs/settings_data.json b/configs/settings_data.json index 292724b..a7a373a 100644 --- a/configs/settings_data.json +++ b/configs/settings_data.json @@ -3,6 +3,7 @@ "show_featured_product": false, "show_featured_products": true, "show_featured_categories": true, + "show_image_text": false, "homepage_hero_image": "", "homepage_hero_image_mobile": "", "homepage_hero_heading": "", @@ -126,6 +127,17 @@ "promo_banner_text_color": "", "promo_banner_cta_style": "primary", "promo_banner_cta_outline": true, + "image_text_image": "", + "image_text_image_alt": "", + "image_text_image_position": "left", + "image_text_image_ratio": "square", + "image_text_eyebrow": "", + "image_text_heading": "", + "image_text_body": "", + "image_text_cta_url": "", + "image_text_cta_style": "primary", + "image_text_bg_color": "", + "image_text_text_color": "", "site_index": false, "account_only": false, "account_only_show_footer": true, diff --git a/configs/settings_optional.txt b/configs/settings_optional.txt index 1fb05ed..9edc91b 100644 --- a/configs/settings_optional.txt +++ b/configs/settings_optional.txt @@ -8,6 +8,7 @@ featured_products_cta_text # template applies a default filter featured_products_header # template applies a default filter final_step_message # template applies a default filter homepage_hero_cta # template applies a default filter +image_text_cta_text # template applies a default filter membership_active_status_value # template applies a default filter membership_detail_text # template applies a default filter membership_discount_percent # template applies a default filter diff --git a/configs/settings_schema.json b/configs/settings_schema.json index 991a6dd..f81c330 100644 --- a/configs/settings_schema.json +++ b/configs/settings_schema.json @@ -42,6 +42,13 @@ "type": "checkbox", "default": false, "help_text": "Display a full-width promotional banner with a heading and call-to-action button." + }, + { + "name": "show_image_text", + "label": "Show Image with Text", + "type": "checkbox", + "default": false, + "help_text": "Display a two-column image and text section for a brand story, ingredient callout, or explainer." } ], "Hero": [ @@ -714,6 +721,131 @@ "help_text": "Text color for category tile labels. Leave blank for white." } ], + "Image with Text": [ + { + "name": "image_text_image", + "label": "Image", + "type": "image_picker", + "help_text": "Image for one half of the section. Use a product or lifestyle photo that survives cropping." + }, + { + "name": "image_text_image_alt", + "label": "Image Alt Text", + "type": "text", + "help_text": "Accessible description of the image. Leave blank to use the heading.", + "max_length": 250 + }, + { + "name": "image_text_image_position", + "label": "Image Position", + "type": "select", + "options": [ + { + "name": "Left", + "value": "left" + }, + { + "name": "Right", + "value": "right" + } + ], + "default": "left", + "help_text": "Which side the image sits on at tablet and desktop widths. On mobile the image always stacks above the text." + }, + { + "name": "image_text_image_ratio", + "label": "Image Ratio", + "type": "select", + "options": [ + { + "name": "Square", + "value": "square" + }, + { + "name": "Portrait", + "value": "portrait" + }, + { + "name": "Landscape", + "value": "landscape" + }, + { + "name": "Native", + "value": "native" + } + ], + "default": "square", + "help_text": "Crop the image to a fixed ratio, or use Native to show the image at its own proportions." + }, + { + "name": "image_text_eyebrow", + "label": "Eyebrow", + "type": "text", + "help_text": "Short uppercase label above the heading.", + "max_length": 50 + }, + { + "name": "image_text_heading", + "label": "Heading", + "type": "text", + "help_text": "Section heading beside the image.", + "max_length": 100 + }, + { + "name": "image_text_body", + "label": "Body", + "type": "richtext", + "help_text": "Supporting copy below the heading.", + "max_length": 1000 + }, + { + "name": "image_text_cta_text", + "label": "Button Text", + "type": "text", + "help_text": "Optional button text. Leave blank to use the localized default.", + "max_length": 50 + }, + { + "name": "image_text_cta_url", + "label": "Button URL", + "type": "url", + "help_text": "Where the button links to (e.g. /catalogue/). Leave blank to hide the button.", + "max_length": 500 + }, + { + "name": "image_text_cta_style", + "label": "Button Style", + "type": "select", + "options": [ + { + "name": "Primary", + "value": "primary" + }, + { + "name": "Secondary", + "value": "secondary" + }, + { + "name": "Outline", + "value": "outline" + } + ], + "default": "primary", + "help_text": "Primary fills with your primary color, Secondary is a quiet bordered button, Outline follows the section text color." + }, + { + "name": "image_text_bg_color", + "label": "Background Color", + "type": "color", + "help_text": "Section background color. Leave blank for the page background." + }, + { + "name": "image_text_text_color", + "label": "Text Color", + "type": "color", + "help_text": "Heading, body, and outline button color. Leave blank for the theme defaults." + } + ], "Custom CSS": [ { "name": "home_page_css", diff --git a/locales/de.json b/locales/de.json index e93d913..07e01aa 100644 --- a/locales/de.json +++ b/locales/de.json @@ -298,7 +298,8 @@ "featured_products": "Ausgewählte Produkte - Einstellungen > Homepage > Ausgewählte Produkte", "featured_categories": "Ausgewählte Kategorien - Einstellungen > Homepage > Ausgewählte Kategorien", "on_sale": "Im Angebot - Einstellungen > Homepage > Im Angebot > Produkte", - "featured_product": "Ausgewähltes Produkt - Einstellungen > Homepage > Ausgewähltes Produkt" + "featured_product": "Ausgewähltes Produkt - Einstellungen > Homepage > Ausgewähltes Produkt", + "image_text": "Image with Text - Settings > Homepage > Image with Text" } } } diff --git a/locales/en.default.json b/locales/en.default.json index 922df06..f9c5a18 100644 --- a/locales/en.default.json +++ b/locales/en.default.json @@ -298,7 +298,8 @@ "featured_products": "Featured Products - Settings > Homepage > Featured Products", "featured_categories": "Featured Categories - Settings > Homepage > Featured Categories", "on_sale": "On Sale - Settings > Homepage > On Sale > Products", - "featured_product": "Featured Product - Settings > Homepage > Featured Product" + "featured_product": "Featured Product - Settings > Homepage > Featured Product", + "image_text": "Image with Text - Settings > Homepage > Image with Text" } } } diff --git a/locales/es.json b/locales/es.json index c7a1591..cd4cf48 100644 --- a/locales/es.json +++ b/locales/es.json @@ -298,7 +298,8 @@ "featured_products": "Productos destacados - Configuración > Inicio > Productos destacados", "featured_categories": "Categorías destacadas - Configuración > Inicio > Categorías destacadas", "on_sale": "En oferta - Configuración > Inicio > En oferta > Productos", - "featured_product": "Producto destacado - Configuración > Inicio > Producto destacado" + "featured_product": "Producto destacado - Configuración > Inicio > Producto destacado", + "image_text": "Image with Text - Settings > Homepage > Image with Text" } } } diff --git a/locales/fi.json b/locales/fi.json index 93e8e17..98620cc 100644 --- a/locales/fi.json +++ b/locales/fi.json @@ -298,7 +298,8 @@ "featured_products": "Featured Products - Settings > Homepage > Featured Products", "featured_categories": "Featured Categories - Settings > Homepage > Featured Categories", "on_sale": "On Sale - Settings > Homepage > On Sale > Products", - "featured_product": "Featured Product - Settings > Homepage > Featured Product" + "featured_product": "Featured Product - Settings > Homepage > Featured Product", + "image_text": "Image with Text - Settings > Homepage > Image with Text" } } } diff --git a/locales/fr.json b/locales/fr.json index 3d56ce8..3d202bf 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -298,7 +298,8 @@ "featured_products": "Produits en vedette - Paramètres > Accueil > Produits en vedette", "featured_categories": "Catégories en vedette - Paramètres > Accueil > Catégories en vedette", "on_sale": "En promotion - Paramètres > Accueil > En promotion > Produits", - "featured_product": "Produit en vedette - Paramètres > Accueil > Produit en vedette" + "featured_product": "Produit en vedette - Paramètres > Accueil > Produit en vedette", + "image_text": "Image with Text - Settings > Homepage > Image with Text" } } } diff --git a/locales/it.json b/locales/it.json index d7b2a7a..d6393a9 100644 --- a/locales/it.json +++ b/locales/it.json @@ -298,7 +298,8 @@ "featured_products": "Featured Products - Settings > Homepage > Featured Products", "featured_categories": "Featured Categories - Settings > Homepage > Featured Categories", "on_sale": "On Sale - Settings > Homepage > On Sale > Products", - "featured_product": "Featured Product - Settings > Homepage > Featured Product" + "featured_product": "Featured Product - Settings > Homepage > Featured Product", + "image_text": "Image with Text - Settings > Homepage > Image with Text" } } } diff --git a/locales/nl.json b/locales/nl.json index e12599f..e6b4b96 100644 --- a/locales/nl.json +++ b/locales/nl.json @@ -298,7 +298,8 @@ "featured_products": "Featured Products - Settings > Homepage > Featured Products", "featured_categories": "Featured Categories - Settings > Homepage > Featured Categories", "on_sale": "On Sale - Settings > Homepage > On Sale > Products", - "featured_product": "Featured Product - Settings > Homepage > Featured Product" + "featured_product": "Featured Product - Settings > Homepage > Featured Product", + "image_text": "Image with Text - Settings > Homepage > Image with Text" } } } diff --git a/locales/no.json b/locales/no.json index 75a7872..17ae4ee 100644 --- a/locales/no.json +++ b/locales/no.json @@ -298,7 +298,8 @@ "featured_products": "Featured Products - Settings > Homepage > Featured Products", "featured_categories": "Featured Categories - Settings > Homepage > Featured Categories", "on_sale": "On Sale - Settings > Homepage > On Sale > Products", - "featured_product": "Featured Product - Settings > Homepage > Featured Product" + "featured_product": "Featured Product - Settings > Homepage > Featured Product", + "image_text": "Image with Text - Settings > Homepage > Image with Text" } } } diff --git a/locales/pt.json b/locales/pt.json index 7b68189..afc6e39 100644 --- a/locales/pt.json +++ b/locales/pt.json @@ -298,7 +298,8 @@ "featured_products": "Featured Products - Settings > Homepage > Featured Products", "featured_categories": "Featured Categories - Settings > Homepage > Featured Categories", "on_sale": "On Sale - Settings > Homepage > On Sale > Products", - "featured_product": "Featured Product - Settings > Homepage > Featured Product" + "featured_product": "Featured Product - Settings > Homepage > Featured Product", + "image_text": "Image with Text - Settings > Homepage > Image with Text" } } } diff --git a/locales/sv.json b/locales/sv.json index 02889de..8125302 100644 --- a/locales/sv.json +++ b/locales/sv.json @@ -298,7 +298,8 @@ "featured_products": "Featured Products - Settings > Homepage > Featured Products", "featured_categories": "Featured Categories - Settings > Homepage > Featured Categories", "on_sale": "On Sale - Settings > Homepage > On Sale > Products", - "featured_product": "Featured Product - Settings > Homepage > Featured Product" + "featured_product": "Featured Product - Settings > Homepage > Featured Product", + "image_text": "Image with Text - Settings > Homepage > Image with Text" } } } diff --git a/locales/th.json b/locales/th.json index ce0e742..13cd9ad 100644 --- a/locales/th.json +++ b/locales/th.json @@ -298,7 +298,8 @@ "featured_products": "Featured Products - Settings > Homepage > Featured Products", "featured_categories": "Featured Categories - Settings > Homepage > Featured Categories", "on_sale": "On Sale - Settings > Homepage > On Sale > Products", - "featured_product": "Featured Product - Settings > Homepage > Featured Product" + "featured_product": "Featured Product - Settings > Homepage > Featured Product", + "image_text": "Image with Text - Settings > Homepage > Image with Text" } } } diff --git a/partials/cta_button.html b/partials/cta_button.html index dd98049..4c67a53 100644 --- a/partials/cta_button.html +++ b/partials/cta_button.html @@ -1,7 +1,7 @@ -{# Shared CTA helper. Expects href, label, style, outline. Optional: outline_color. style: primary|accent. #} +{# Shared CTA helper. Expects href, label, style, outline. Optional: outline_color. style: primary|accent|secondary. #} {% if href and label %} {{ label }} diff --git a/partials/section_image_text.html b/partials/section_image_text.html new file mode 100644 index 0000000..7a16868 --- /dev/null +++ b/partials/section_image_text.html @@ -0,0 +1,63 @@ +{# Spark homepage image with text section: 50/50 image and copy split. Controlled by Homepage > Sections + Image with Text settings. #} +{% if settings.show_image_text %} + {% if settings.image_text_image or settings.image_text_heading or settings.image_text_body %} +
+
+
+
+ {% if settings.image_text_image %} + {% if settings.image_text_image_ratio == 'native' %} + {{ settings.image_text_image_alt|default:settings.image_text_heading }} + {% else %} +
+ {{ settings.image_text_image_alt|default:settings.image_text_heading }} +
+ {% endif %} + {% else %} +
+ {% endif %} +
+
+ {% if settings.image_text_eyebrow %} +

{{ settings.image_text_eyebrow }}

+ {% endif %} + {% if settings.image_text_heading %} +

{{ settings.image_text_heading }}

+ {% endif %} + {% if settings.image_text_body %} +
{{ settings.image_text_body|safe }}
+ {% endif %} + {% if settings.image_text_cta_url %} + {% t "homepage.cta.shop_now" as default_shop_now %} + {% if settings.image_text_cta_style == 'outline' %} + {% include "partials/cta_button.html" with href=settings.image_text_cta_url label=settings.image_text_cta_text|default:default_shop_now style="primary" outline=True outline_color=settings.image_text_text_color %} + {% else %} + {% include "partials/cta_button.html" with href=settings.image_text_cta_url label=settings.image_text_cta_text|default:default_shop_now style=settings.image_text_cta_style %} + {% endif %} + {% endif %} +
+
+
+
+ {% else %} +
+
+
+
+
+
+
+
+
+
+

{% t "homepage.placeholder.image_text" %}

+
+ {% endif %} +{% endif %} diff --git a/templates/index.html b/templates/index.html index d5cf942..b4dc9bf 100644 --- a/templates/index.html +++ b/templates/index.html @@ -38,6 +38,7 @@ {% include "partials/section_featured_product.html" %} {% include "partials/section_featured_products.html" %} {% include "partials/section_featured_categories.html" %} +{% include "partials/section_image_text.html" %} {% include "partials/section_on_sale.html" %} {% include "partials/section_promo_banner.html" %} diff --git a/tests/test_localization_contracts.py b/tests/test_localization_contracts.py index 237b4a4..45f639e 100644 --- a/tests/test_localization_contracts.py +++ b/tests/test_localization_contracts.py @@ -16,6 +16,7 @@ "featured_products_header", "final_step_message", "homepage_hero_cta", + "image_text_cta_text", "membership_detail_text", "membership_label", "on_sale_header", From 553f89d055bf4d10d640b7286d77e6041bd2e5b7 Mon Sep 17 00:00:00 2001 From: Devin Michael Date: Mon, 21 Sep 2026 15:21:03 +0700 Subject: [PATCH 2/6] Document the Image with Text section Add docs/section-specs/image-text.md in the existing spec format (render contract, setting map, Figma states, QA checklist, migration table, gaps), list it in the section-specs index, the roster's Tier 0 table and shipped counts, the Figma plan's section-unit table, the architecture proposal's migration list, and the settings-to-partials catalog. Update the homepage section enumerations in CLAUDE.md and README.md, note the new `secondary` CTA style in the design-block guide, and add the Unreleased changelog entry. Co-Authored-By: Claude Fable 5.1 --- CHANGELOG.md | 2 + CLAUDE.md | 2 +- README.md | 2 +- docs/design-block-authoring.md | 1 + docs/figma-section-library-plan.md | 3 +- docs/section-roster.md | 17 ++- docs/section-specs/README.md | 1 + docs/section-specs/image-text.md | 174 +++++++++++++++++++++++++ docs/sections-architecture-proposal.md | 1 + docs/theme-settings-partials.md | 1 + 10 files changed, 194 insertions(+), 10 deletions(-) create mode 100644 docs/section-specs/image-text.md diff --git a/CHANGELOG.md b/CHANGELOG.md index c8286ed..0737579 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -8,6 +8,8 @@ The GitHub release body is a summary, not a copy of the changelog section. Write ## Unreleased +- Added the Image with Text homepage section (`partials/section_image_text.html`, Theme Settings > Homepage > Image with Text, toggle `show_image_text`, off by default). It is a 50/50 image and copy split with image left/right, a square/portrait/landscape/native image ratio, eyebrow, heading, rich-text body, a CTA with primary/secondary/outline styles, and background and text colours; on mobile it stacks image first. It is included from `templates/index.html` between Featured Categories and On Sale. With the toggle on and nothing configured it renders the same setup placeholder pattern as the other sections. `partials/cta_button.html` gained a `secondary` style so the section can reuse the existing `.btn-secondary` class. Spec: `docs/section-specs/image-text.md`. `assets/main.css` grew from 50298 to 50536 bytes for the new ratio and ordering utilities. + - Added a theme contract so a Spark-derived store theme can prove it still carries the platform integration points Spark declares. `theme-contract.json` lists them (today: `{% pixels %}` in `layouts/base.html`, required since 1.3.0, with the reason the gate prints on failure) and `scripts/check-theme-contract.py` asserts them against a working copy (`--root`) or a live theme (`--store` + `--theme-id`, reading the store admin API). This exists because a derived theme never updates from this repo and the resulting failure is silent: the storefront renders, apps stay installed and enabled, and only the events go missing. A theme can also stop satisfying the contract without anyone editing it, when an older working copy is republished over the active theme, so the live mode matters as much as the local one. The gate reads more than the tag: it masks comments, so a commented-out tag does not satisfy the contract, and it fails a child template that overrides the block without the tag, which a plain text search would pass. `pixels` was also added to `REQUIRED_BASE_BLOCKS` in `scripts/check-templates.py`, so dropping the block from Spark itself fails CI. Run it with `make contract`; it is part of `make verify-theme` and runs in CI against Spark's own copy. ## 1.4.1 - 2026-09-09 diff --git a/CLAUDE.md b/CLAUDE.md index c670b7f..eca4fb5 100644 --- a/CLAUDE.md +++ b/CLAUDE.md @@ -111,7 +111,7 @@ CSS custom properties set in `layouts/base.html` from `store.branding`: Stable `{% app_hook 'NAME' %}` extension surfaces for Apps (Reviews app uses them today). Future Apps should target the same pattern rather than fork the theme. Grep `{% app_hook %}` in `templates/` and `partials/` to see what's exposed. ### Settings (`configs/settings_schema.json`) -Typography (fonts, text/heading/link colors), Navigation (main menu, navbar colors), Footer (menu, colors, social links ×8, payment icons, disclaimer), homepage section partials (hero with text overlay, featured products/categories, recommended products, On Sale, Promo Banner, Featured Product — each with its own toggle), Announcement Bar, Advanced (noindex, account-only mode). v1.1 added 22 settings and restructured homepage settings around fixed-order section partials. +Typography (fonts, text/heading/link colors), Navigation (main menu, navbar colors), Footer (menu, colors, social links ×8, payment icons, disclaimer), homepage section partials (hero with text overlay, featured products/categories, recommended products, On Sale, Promo Banner, Featured Product, Image with Text — each with its own toggle), Announcement Bar, Advanced (noindex, account-only mode). v1.1 added 22 settings and restructured homepage settings around fixed-order section partials. ### Architecture Docs - `CONTEXT.md` defines Spark's domain language for architecture reviews. diff --git a/README.md b/README.md index 614ec95..09b57fe 100644 --- a/README.md +++ b/README.md @@ -139,7 +139,7 @@ Tracked theme documentation starts at [docs/README.md](docs/README.md). Current - **Documented extension modules** — `SparkEvents`, `SparkVariantState`, `SparkCartRewards`, and the cart drawer renderer keep extension points explicit for theme developers and apps. - **Cart milestones** — multi-step progress bar with default thresholds for free shipping and free gifts; auto-add/remove driven by progress events. - **Tailwind CSS v4** — standalone CLI binary, no Node dependency. CSS-based config with `@theme`, `@layer base`, and `@layer components`. -- **Homepage section partials (Wave 1 + 1.1.1)** — hero with text overlay, featured products, featured categories, recommended products, On Sale, Promo Banner, Featured Product. Each section partial has its own toggle in Theme Settings and is included from `templates/index.html` in a fixed order. +- **Homepage section partials (Wave 1 + 1.1.1)** — hero with text overlay, featured products, featured categories, recommended products, On Sale, Promo Banner, Featured Product, Image with Text. Each section partial has its own toggle in Theme Settings and is included from `templates/index.html` in a fixed order. - **Sale badges** — automatic on product cards when `compare_at_price > price`. - **Review app hooks** — template surfaces (`{% app_hook %}`) for product cards, PDP rating summary, full reviews module, home/collection review feeds, and global social proof. Apps render into Spark without theme edits. - **Membership price presentation** — optional client-side member price display driven by logged-in customer metadata, designed to pair with platform-side checkout discounts. diff --git a/docs/design-block-authoring.md b/docs/design-block-authoring.md index 20c77a3..87cd5d1 100644 --- a/docs/design-block-authoring.md +++ b/docs/design-block-authoring.md @@ -90,6 +90,7 @@ Supported styles: - `primary` - `accent` +- `secondary` (quiet bordered button, used by Image with Text) Set `outline` to render an outline button using the selected brand color. diff --git a/docs/figma-section-library-plan.md b/docs/figma-section-library-plan.md index 8c6de9d..10b4c6f 100644 --- a/docs/figma-section-library-plan.md +++ b/docs/figma-section-library-plan.md @@ -103,8 +103,9 @@ Use snake_case for exported properties. If Figma display labels need to be short | Featured categories | `partials/section_featured_categories.html` | `product_categories`, overlay settings | First-pass spec in `docs/section-specs/featured-categories.md`. Needs 2-5 desktop column states, missing category image state, overlay opacity states, and long category label behavior. | | On Sale | `partials/section_on_sale.html` | `products`, product card sale state | First-pass spec in `docs/section-specs/on-sale.md`. Needs sale badge and compare-at price states from `partials/product_card.html`; otherwise this should share grid anatomy with Featured products. | | Promo banner | `partials/section_promo_banner.html` | text, CTA settings, colors | First-pass spec in `docs/section-specs/promo-banner.md`. Needs contrast states, short and long copy states, no-CTA state, and primary/accent/outline button states. | +| Image with text | `partials/section_image_text.html` | `image_picker`, text, rich text, CTA settings, colors | First-pass spec in `docs/section-specs/image-text.md`. Needs image left/right states, four image ratio states, missing-image state, long heading state, and primary/secondary/outline button states. | -These are enough for the first Figma library pass. The full roster of section partials Spark needs to ship to fully cover D2C storefronts — including image-with-text, FAQ, comparison table, process steps, press logos, testimonials, value props, and the rest of the Tier 1/2/3 catalog — lives in [`section-roster.md`](./section-roster.md). New section ideas should follow the section authoring unit pattern below. +These are enough for the first Figma library pass. The full roster of section partials Spark needs to ship to fully cover D2C storefronts — including FAQ, comparison table, process steps, press logos, testimonials, value props, and the rest of the Tier 1/2/3 catalog — lives in [`section-roster.md`](./section-roster.md). New section ideas should follow the section authoring unit pattern below. ## Section Spec Template diff --git a/docs/section-roster.md b/docs/section-roster.md index bad2ba5..7f1bd8b 100644 --- a/docs/section-roster.md +++ b/docs/section-roster.md @@ -22,9 +22,9 @@ Three inputs shaped this roster: / Crave (free D2C-friendly), Impulse / Motion / Prestige (paid). Section catalogs across these are the de facto standard for what merchants expect to find in a theme editor. -3. **Spark's existing partials** — 6 homepage sections shipped today +3. **Spark's existing partials** — 7 homepage sections shipped today (hero, featured_product, featured_products, featured_categories, - on_sale, promo_banner). See `figma-section-library-plan.md` for the + on_sale, promo_banner, image_text). See `figma-section-library-plan.md` for the current "Current Homepage Section Units" table. ## Naming and conventions @@ -39,7 +39,7 @@ Three inputs shaped this roster: - Each section gets a spec in `docs/section-specs/.md` following the [Section Spec Template](./figma-section-library-plan.md#section-spec-template). -## Tier 0 — already shipped (6) +## Tier 0 — already shipped (7) | Section | Partial | Status | |---|---|---| @@ -49,8 +49,9 @@ Three inputs shaped this roster: | Featured categories | `section_featured_categories` | First-pass spec | | On sale | `section_on_sale` | First-pass spec | | Promo banner | `section_promo_banner` | First-pass spec | +| Image with text | `section_image_text` | First-pass spec | -## Tier 1 — must-have for D2C parity (12) +## Tier 1 — must-have for D2C parity (11 remaining) These cover the layout patterns that today require either bespoke partial work or static HTML in Page content. Each one shows up across @@ -180,6 +181,8 @@ pages, mission copy between product showcases. - `text_bg_color`, `text_color` ### 10. `section_image_text` +**Status:** Shipped. Spec in [`section-specs/image-text.md`](./section-specs/image-text.md); +the settings below are the implemented names, plus `image_text_image_alt`. **Purpose:** 50/50 image + text split. Foundational layout for any story or explainer page. **Pattern:** Founder story chapters, ingredient deep-dives, "how it @@ -361,9 +364,9 @@ closes the most merchant friction fastest is: | 7 | Tier 3 on demand | Lookbook, countdown, instagram_feed if merchants ask. | | 8 | Commerce primitives — sticky_atc, quick_view, stock_indicator | PDP polish. | -12 Tier-1 + 8 Tier-2 = 20 sections. With variants on existing -partials, the total Figma component library lands at roughly 28 -section components plus the 6 already-shipped — call it 34. +11 remaining Tier-1 + 8 Tier-2 = 19 sections. With variants on +existing partials, the total Figma component library lands at roughly +27 section components plus the 7 already-shipped — call it 34. ## Coverage validation diff --git a/docs/section-specs/README.md b/docs/section-specs/README.md index 1a54142..5f3dcfe 100644 --- a/docs/section-specs/README.md +++ b/docs/section-specs/README.md @@ -14,6 +14,7 @@ Each spec should describe one merchant-configurable section across Figma, Theme | [Featured categories](featured-categories.md) | First pass | `partials/section_featured_categories.html` | | [On Sale](on-sale.md) | First pass | `partials/section_on_sale.html` | | [Promo banner](promo-banner.md) | First pass | `partials/section_promo_banner.html` | +| [Image with text](image-text.md) | First pass | `partials/section_image_text.html` | Use `docs/figma-section-library-plan.md` for the overall workflow and library structure, and [`docs/section-roster.md`](../section-roster.md) for the full Tier 1/2/3 partial roster Spark should ship to fully cover modern D2C storefronts. diff --git a/docs/section-specs/image-text.md b/docs/section-specs/image-text.md new file mode 100644 index 0000000..17cd23d --- /dev/null +++ b/docs/section-specs/image-text.md @@ -0,0 +1,174 @@ +# Image with Text Section Spec + +Status: first-pass section authoring unit for the Spark Figma component library. + +The Image with text section renders a 50/50 split of one image and one copy column (eyebrow, heading, rich-text body, optional CTA). It is the foundational story and explainer layout: founder story chapters, ingredient deep-dives, technology callouts. It is not a product spotlight; use Featured product when the copy belongs to a catalogue product. + +## Module Summary + +| Field | Value | +| --- | --- | +| Stable slug | `image_text` | +| Merchant label | Image with Text | +| Current Interface | Global Theme Settings under `show_image_text` and `image_text_*` | +| Current Implementation | `partials/section_image_text.html` | +| Included from | `templates/index.html` | +| Settings groups | `Homepage > Sections`, `Homepage > Image with Text` | +| Defaults file | `configs/settings_data.json` | +| Design references | `image_text-desktop`, `image_text-tablet`, `image_text-mobile` | +| Runtime JS | None | +| App hooks | None | + +## Current Render Contract + +- The section renders only when `settings.show_image_text` is true. +- With the toggle on and no image, heading, or body configured, the section renders the setup placeholder (dashed border, neutral blocks, pointer to the settings location) instead of an empty band. +- With any of image, heading, or body configured, the section renders live: two columns at `md` and above, one column below `md` with the image always first in DOM order and therefore on top. +- `image_text_image_position` moves the image column with `md:order-*`; it has no effect below `md`. +- `image_text_image_ratio` wraps the image in a fixed-ratio box with `object-cover` for `square` (1:1), `portrait` (4:5), and `landscape` (4:3). `native` renders the image at its own proportions with no crop. +- A missing image with copy present renders a neutral `bg-slate-100` block at the selected ratio (square when `native`), so the layout keeps its shape while the merchant finishes setup. +- Image `alt` is `image_text_image_alt`, falling back to `image_text_heading`. +- Eyebrow, heading, and body each render only when present. Body is rich text and renders with `|safe`. +- CTA renders only when `image_text_cta_url` is present, through `partials/cta_button.html`. Label falls back to the localized `homepage.cta.shop_now`. +- `image_text_cta_style` maps to the shared button classes: `primary` renders `btn-primary`, `secondary` renders `btn-secondary`, `outline` renders `btn-outline` with the outline colour following `image_text_text_color` when set and the primary brand colour otherwise. +- Background colour is applied only when set; the default is the page background. +- Text colour is applied to the section only when set. When empty the copy uses the theme slate defaults (`text-slate-800` heading, `text-slate-600` body, `text-slate-500` eyebrow). When set, the eyebrow and body inherit the colour at reduced opacity. +- The image is lazy-loaded. This section is not expected to hold the LCP image. +- There is no client-side behavior and no GraphQL dependency. + +## Figma Component + +Recommended component name: + +```text +Homepage/Image with Text +``` + +Recommended reference frame names: + +```text +image_text-desktop +image_text-tablet +image_text-mobile +``` + +Recommended component properties: + +| Property | Type | Maps To | +| --- | --- | --- | +| `show` | Boolean | `show_image_text` | +| `image` | Image placeholder | `image_text_image` | +| `alt_text` | Text annotation | `image_text_image_alt` | +| `image_position` | Variant | `image_text_image_position` | +| `image_ratio` | Variant | `image_text_image_ratio` | +| `eyebrow` | Text | `image_text_eyebrow` | +| `heading` | Text | `image_text_heading` | +| `body` | Text | `image_text_body` | +| `cta_label` | Text | `image_text_cta_text` | +| `cta_url` | Link annotation | `image_text_cta_url` | +| `cta_style` | Variant | `image_text_cta_style` | +| `background_color` | Color | `image_text_bg_color` | +| `text_color` | Color | `image_text_text_color` | + +## Setting Map + +| Key | Type | Default | Figma Property | Values / Limits | Notes | +| --- | --- | --- | --- | --- | --- | +| `show_image_text` | `checkbox` | `false` | `show` | `true`, `false` | Lives in `Homepage > Sections`; hides the whole section when false. Off by default so existing stores are unchanged. | +| `image_text_image` | `image_picker` | empty | `image` | Image asset | Empty with copy present renders a neutral block; empty with no copy renders the setup placeholder. | +| `image_text_image_alt` | `text` | empty | `alt_text` | Max 250 chars | Falls back to the heading. | +| `image_text_image_position` | `select` | `left` | `image_position` | `left`, `right` | Desktop and tablet only; mobile always stacks image first. | +| `image_text_image_ratio` | `select` | `square` | `image_ratio` | `square`, `portrait`, `landscape`, `native` | Fixed ratios crop with `object-cover`; `native` never crops. | +| `image_text_eyebrow` | `text` | empty | `eyebrow` | Max 50 chars | Uppercase label above the heading. | +| `image_text_heading` | `text` | empty | `heading` | Max 100 chars | Renders as an `h2`. | +| `image_text_body` | `richtext` | empty | `body` | Max 1000 chars | Rendered with `\|safe`. | +| `image_text_cta_text` | `text` | `Shop now` (localized) | `cta_label` | Max 50 chars | No schema default; the template applies the localized fallback. Requires URL to render. | +| `image_text_cta_url` | `url` | empty | `cta_url` | Max 500 chars | Empty hides the CTA. | +| `image_text_cta_style` | `select` | `primary` | `cta_style` | `primary`, `secondary`, `outline` | Shared `.btn-*` classes. Outline follows the text colour. | +| `image_text_bg_color` | `color` | empty | `background_color` | Color | Empty renders the page background. | +| `image_text_text_color` | `color` | empty | `text_color` | Color | Empty renders the theme slate defaults. | + +## Layout Details + +| Element | Current Implementation | +| --- | --- | +| Section | `py-section-y md:py-section-y-md`, optional inline background and text colour. | +| Container | Shared `.container`. | +| Grid | `grid-cols-1 md:grid-cols-2`, `gap-content-md md:gap-content-lg`, `items-center`. | +| Image box | Ratio wrapper with `overflow-hidden rounded-card`; `native` uses `w-full h-auto rounded-card` on the image directly. | +| Eyebrow | `text-xs font-semibold uppercase tracking-wide mb-3`. | +| Heading | `text-h1 md:text-h1-md font-semibold mb-4`. | +| Body | Rich text, `mb-6`. | +| CTA | Shared Spark button helper. | + +## Image Guidance + +- Recommended source minimum: 1200px on the long edge. The column is at most half the container, so 1600px covers 2x displays. +- Keep the subject centred for `square` and `portrait`; `object-cover` crops from the edges as the column width changes between tablet and desktop. +- Use `native` for product cut-outs, illustrations, or any image whose edges carry meaning. +- Do not bake copy into the image. Use the eyebrow, heading, and body settings so the text stays editable and translatable. + +## Required Figma States + +| State | Why It Matters | +| --- | --- | +| Default, image left | Eyebrow, heading, body, primary CTA, square image. | +| Image right | `image_text_image_position` set to `right`. | +| Mobile stacked | Image above copy regardless of position. | +| Square, portrait, landscape, native | One frame per ratio so cropping expectations are explicit. | +| No eyebrow | Heading, body, CTA only. | +| No CTA | CTA URL is empty. | +| Secondary and outline CTA | Both alternate button styles, outline on both default and custom text colour. | +| Custom background and text colour | Dark background with light text. | +| Long heading | Heading at 100 chars wrapping across three lines beside a square image. | +| Missing image | Copy present, neutral block in the image column. | +| Setup placeholder | Toggle on, no image, heading, or body. | + +## Accessibility + +- The heading renders as `h2`; Hero image keeps the homepage `h1`. +- Every configured image should have merchant-authored alt text. Empty alt falls back to the heading, which is the section's own description of the image subject. +- Custom background and text colours are merchant choices; Figma should include at least one low-contrast pairing that the design guardrails reject. +- The CTA is a normal link and receives the shared focus-visible treatment. + +## Performance + +- The image uses `loading="lazy"` and is not preloaded. If a merchant places this section directly under a hidden Hero so that it becomes the LCP, revisit the loading hint before changing defaults. +- No JavaScript, no per-user data. + +## QA Checklist + +- Verify at desktop 1440px, tablet 768px, and the chosen mobile width. +- Confirm image left and image right swap at `md` and stack image-first on mobile. +- Confirm each ratio value renders the expected box and that `native` does not crop. +- Confirm the long-heading state (100 chars) wraps without overflow and keeps the image vertically centred. +- Confirm the missing-image state renders the neutral block at the selected ratio and the copy still renders. +- Confirm no CTA renders when `image_text_cta_url` is empty, and the localized `Shop now` label renders when only the URL is set. +- Confirm primary, secondary, and outline CTA states match shared Spark button styling, and that outline follows a custom text colour. +- Confirm background and text colours apply only when set and default stores render unchanged. +- Confirm the setup placeholder appears with the toggle on and no content, and disappears once any of image, heading, or body is set. + +## Future Theme Section Migration + +| Current Key | Future Instance Setting | +| --- | --- | +| `image_text_image` | `section.settings.image` | +| `image_text_image_alt` | `section.settings.alt_text` | +| `image_text_image_position` | `section.settings.image_position` | +| `image_text_image_ratio` | `section.settings.image_ratio` | +| `image_text_eyebrow` | `section.settings.eyebrow` | +| `image_text_heading` | `section.settings.heading` | +| `image_text_body` | `section.settings.body` | +| `image_text_cta_text` | `section.settings.cta_label` | +| `image_text_cta_url` | `section.settings.cta_url` | +| `image_text_cta_style` | `section.settings.cta_style` | +| `image_text_bg_color` | `section.settings.background_color` | +| `image_text_text_color` | `section.settings.text_color` | + +`show_image_text` should become section presence once merchants can add, remove, reorder, and duplicate section instances. This section is the first one where duplication matters: alternating image/text chapters need several instances with different content, which the fixed-order global settings model cannot express. + +## Implementation Gaps Exposed + +- `image_text_cta_style` uses a single three-way select (`primary`, `secondary`, `outline`) as the roster sketch specifies, while the six earlier sections use a `*_cta_style` (`primary`/`accent`) select plus a `*_cta_outline` checkbox. `partials/cta_button.html` gained a `secondary` style so both models share one helper. Decide before public release whether new sections standardise on the three-way select or the older pair. +- Only one instance is possible until true theme sections exist, so "alternating image/text blocks" from the roster still needs static page content today. +- The CTA label fallback reuses `homepage.cta.shop_now`. A story section may want a "Learn more" default; that needs a new locale key across every locale file. diff --git a/docs/sections-architecture-proposal.md b/docs/sections-architecture-proposal.md index 0273e47..bc1ee83 100644 --- a/docs/sections-architecture-proposal.md +++ b/docs/sections-architecture-proposal.md @@ -116,6 +116,7 @@ The important contract: inside a section partial, `section.settings.foo` should - `section_featured_categories.html` - `section_on_sale.html` - `section_promo_banner.html` + - `section_image_text.html` 3. Generate a default homepage composition from current `show_*` toggles and settings. 4. Update section partials to read `section.settings.*` first and fall back to global `settings.*` during migration. 5. Once stable, deprecate homepage-only global section settings in favor of section instances. diff --git a/docs/theme-settings-partials.md b/docs/theme-settings-partials.md index 35dfe85..a535f02 100644 --- a/docs/theme-settings-partials.md +++ b/docs/theme-settings-partials.md @@ -20,6 +20,7 @@ These are the primary design blocks for quick storefront builds. They are includ | `partials/section_featured_categories.html` | Homepage > Featured Categories | Category image tile grid. | `show_featured_categories`, `featured_categories`, `featured_categories_header`, `featured_categories_header_size`, `featured_categories_header_align`, `featured_categories_columns`, `featured_categories_bg_color`, `featured_categories_overlay_color`, `featured_categories_overlay_opacity`, `featured_categories_text_color` | | `partials/section_on_sale.html` | Homepage > On Sale | Merchant-curated sale product grid. | `show_on_sale`, `on_sale_products`, `on_sale_header`, `on_sale_header_size`, `on_sale_header_align`, `on_sale_columns`, `on_sale_bg_color`, `on_sale_card_bg` | | `partials/section_promo_banner.html` | Homepage > Promo Banner | Full-width text and CTA promotion band. | `show_promo_banner`, `promo_banner_heading`, `promo_banner_subheading`, `promo_banner_cta_text`, `promo_banner_cta_url`, `promo_banner_bg_color`, `promo_banner_text_color`, `promo_banner_cta_style`, `promo_banner_cta_outline` | +| `partials/section_image_text.html` | Homepage > Image with Text | 50/50 image and text split with eyebrow, heading, rich-text body, and CTA. | `show_image_text`, `image_text_image`, `image_text_image_alt`, `image_text_image_position`, `image_text_image_ratio`, `image_text_eyebrow`, `image_text_heading`, `image_text_body`, `image_text_cta_text`, `image_text_cta_url`, `image_text_cta_style`, `image_text_bg_color`, `image_text_text_color` | ## Global Partials From e0f8d24a4a1f5f8ced3d7bcc602417c5641c2cf8 Mon Sep 17 00:00:00 2001 From: Devin Michael Date: Mon, 21 Sep 2026 15:22:03 +0700 Subject: [PATCH 3/6] Resolve the image alt fallback with firstof, not a settings filter argument settings.* as a filter argument raises a 500 on the platform once the setting is populated; firstof yields the same string safely. Co-Authored-By: Claude Fable 5.1 --- partials/section_image_text.html | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/partials/section_image_text.html b/partials/section_image_text.html index 7a16868..e59de92 100644 --- a/partials/section_image_text.html +++ b/partials/section_image_text.html @@ -9,13 +9,13 @@ {% if settings.image_text_image %} {% if settings.image_text_image_ratio == 'native' %} {{ settings.image_text_image_alt|default:settings.image_text_heading }} {% else %}
{{ settings.image_text_image_alt|default:settings.image_text_heading }}
From 4029e00bc4538de8e3400216b48fcac2b7864838 Mon Sep 17 00:00:00 2001 From: Devin Michael Date: Mon, 21 Sep 2026 15:45:31 +0700 Subject: [PATCH 4/6] Require copy before the Image with Text section goes live Review follow-ups on #68: the live guard needs a heading or body, so an image alone renders the setup placeholder instead of a lone image beside an empty column; the outline CTA branch drops a redundant style kwarg; the spec records the |safe trust on the richtext setting; the placeholder caption is translated in the locales that translate their siblings. Co-Authored-By: Claude Fable 5.1 --- docs/section-specs/image-text.md | 13 +++++++------ locales/de.json | 2 +- locales/es.json | 2 +- locales/fr.json | 2 +- partials/section_image_text.html | 4 ++-- 5 files changed, 12 insertions(+), 11 deletions(-) diff --git a/docs/section-specs/image-text.md b/docs/section-specs/image-text.md index 17cd23d..0f6cc4b 100644 --- a/docs/section-specs/image-text.md +++ b/docs/section-specs/image-text.md @@ -22,15 +22,15 @@ The Image with text section renders a 50/50 split of one image and one copy colu ## Current Render Contract - The section renders only when `settings.show_image_text` is true. -- With the toggle on and no image, heading, or body configured, the section renders the setup placeholder (dashed border, neutral blocks, pointer to the settings location) instead of an empty band. -- With any of image, heading, or body configured, the section renders live: two columns at `md` and above, one column below `md` with the image always first in DOM order and therefore on top. +- With the toggle on and neither heading nor body configured, the section renders the setup placeholder (dashed border, neutral blocks, pointer to the settings location) instead of an empty band. An image alone is not enough: a lone image beside an empty copy column is never rendered live. +- With a heading or body configured, the section renders live: two columns at `md` and above, one column below `md` with the image always first in DOM order and therefore on top. - `image_text_image_position` moves the image column with `md:order-*`; it has no effect below `md`. - `image_text_image_ratio` wraps the image in a fixed-ratio box with `object-cover` for `square` (1:1), `portrait` (4:5), and `landscape` (4:3). `native` renders the image at its own proportions with no crop. - A missing image with copy present renders a neutral `bg-slate-100` block at the selected ratio (square when `native`), so the layout keeps its shape while the merchant finishes setup. - Image `alt` is `image_text_image_alt`, falling back to `image_text_heading`. -- Eyebrow, heading, and body each render only when present. Body is rich text and renders with `|safe`. +- Eyebrow, heading, and body each render only when present. Body is rich text and renders with `|safe`, trusting the platform's `richtext` setting type to deliver sanitised HTML, the same assumption the other rich-text sections make. Any change that lets unsanitised markup reach a `richtext` setting must revisit this filter. - CTA renders only when `image_text_cta_url` is present, through `partials/cta_button.html`. Label falls back to the localized `homepage.cta.shop_now`. -- `image_text_cta_style` maps to the shared button classes: `primary` renders `btn-primary`, `secondary` renders `btn-secondary`, `outline` renders `btn-outline` with the outline colour following `image_text_text_color` when set and the primary brand colour otherwise. +- `image_text_cta_style` maps to the shared button classes: `primary` renders `btn-primary`, `secondary` renders `btn-secondary`, `outline` renders `btn-outline` with the outline colour following `image_text_text_color` when set and the primary brand colour otherwise (the helper's default, so no `style` argument is passed in that branch). - Background colour is applied only when set; the default is the page background. - Text colour is applied to the section only when set. When empty the copy uses the theme slate defaults (`text-slate-800` heading, `text-slate-600` body, `text-slate-500` eyebrow). When set, the eyebrow and body inherit the colour at reduced opacity. - The image is lazy-loaded. This section is not expected to hold the LCP image. @@ -75,7 +75,7 @@ Recommended component properties: | Key | Type | Default | Figma Property | Values / Limits | Notes | | --- | --- | --- | --- | --- | --- | | `show_image_text` | `checkbox` | `false` | `show` | `true`, `false` | Lives in `Homepage > Sections`; hides the whole section when false. Off by default so existing stores are unchanged. | -| `image_text_image` | `image_picker` | empty | `image` | Image asset | Empty with copy present renders a neutral block; empty with no copy renders the setup placeholder. | +| `image_text_image` | `image_picker` | empty | `image` | Image asset | Empty with copy present renders a neutral block. Set without copy still renders the setup placeholder; the image alone never goes live. | | `image_text_image_alt` | `text` | empty | `alt_text` | Max 250 chars | Falls back to the heading. | | `image_text_image_position` | `select` | `left` | `image_position` | `left`, `right` | Desktop and tablet only; mobile always stacks image first. | | `image_text_image_ratio` | `select` | `square` | `image_ratio` | `square`, `portrait`, `landscape`, `native` | Fixed ratios crop with `object-cover`; `native` never crops. | @@ -122,7 +122,8 @@ Recommended component properties: | Custom background and text colour | Dark background with light text. | | Long heading | Heading at 100 chars wrapping across three lines beside a square image. | | Missing image | Copy present, neutral block in the image column. | -| Setup placeholder | Toggle on, no image, heading, or body. | +| Setup placeholder | Toggle on, no heading or body. | +| Image only | Image set, no heading or body: renders the setup placeholder, never a lone image. | ## Accessibility diff --git a/locales/de.json b/locales/de.json index 07e01aa..8c7d28c 100644 --- a/locales/de.json +++ b/locales/de.json @@ -299,7 +299,7 @@ "featured_categories": "Ausgewählte Kategorien - Einstellungen > Homepage > Ausgewählte Kategorien", "on_sale": "Im Angebot - Einstellungen > Homepage > Im Angebot > Produkte", "featured_product": "Ausgewähltes Produkt - Einstellungen > Homepage > Ausgewähltes Produkt", - "image_text": "Image with Text - Settings > Homepage > Image with Text" + "image_text": "Bild mit Text - Einstellungen > Homepage > Bild mit Text" } } } diff --git a/locales/es.json b/locales/es.json index cd4cf48..00aca16 100644 --- a/locales/es.json +++ b/locales/es.json @@ -299,7 +299,7 @@ "featured_categories": "Categorías destacadas - Configuración > Inicio > Categorías destacadas", "on_sale": "En oferta - Configuración > Inicio > En oferta > Productos", "featured_product": "Producto destacado - Configuración > Inicio > Producto destacado", - "image_text": "Image with Text - Settings > Homepage > Image with Text" + "image_text": "Imagen con texto - Configuración > Inicio > Imagen con texto" } } } diff --git a/locales/fr.json b/locales/fr.json index 3d202bf..8a2fcb1 100644 --- a/locales/fr.json +++ b/locales/fr.json @@ -299,7 +299,7 @@ "featured_categories": "Catégories en vedette - Paramètres > Accueil > Catégories en vedette", "on_sale": "En promotion - Paramètres > Accueil > En promotion > Produits", "featured_product": "Produit en vedette - Paramètres > Accueil > Produit en vedette", - "image_text": "Image with Text - Settings > Homepage > Image with Text" + "image_text": "Image avec texte - Paramètres > Accueil > Image avec texte" } } } diff --git a/partials/section_image_text.html b/partials/section_image_text.html index e59de92..db88a32 100644 --- a/partials/section_image_text.html +++ b/partials/section_image_text.html @@ -1,6 +1,6 @@ {# Spark homepage image with text section: 50/50 image and copy split. Controlled by Homepage > Sections + Image with Text settings. #} {% if settings.show_image_text %} - {% if settings.image_text_image or settings.image_text_heading or settings.image_text_body %} + {% if settings.image_text_heading or settings.image_text_body %}
@@ -37,7 +37,7 @@

Date: Mon, 21 Sep 2026 16:26:16 +0700 Subject: [PATCH 5/6] Carry the custom text colour onto the section heading The base stylesheet and the global heading colour setting give every heading an explicit colour, so dropping text-slate-800 left the h2 dark on a dark custom background. Apply the section colour inline on the heading. Co-Authored-By: Claude Fable 5.1 --- docs/section-specs/image-text.md | 2 +- partials/section_image_text.html | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/docs/section-specs/image-text.md b/docs/section-specs/image-text.md index 0f6cc4b..ac1ca7b 100644 --- a/docs/section-specs/image-text.md +++ b/docs/section-specs/image-text.md @@ -32,7 +32,7 @@ The Image with text section renders a 50/50 split of one image and one copy colu - CTA renders only when `image_text_cta_url` is present, through `partials/cta_button.html`. Label falls back to the localized `homepage.cta.shop_now`. - `image_text_cta_style` maps to the shared button classes: `primary` renders `btn-primary`, `secondary` renders `btn-secondary`, `outline` renders `btn-outline` with the outline colour following `image_text_text_color` when set and the primary brand colour otherwise (the helper's default, so no `style` argument is passed in that branch). - Background colour is applied only when set; the default is the page background. -- Text colour is applied to the section only when set. When empty the copy uses the theme slate defaults (`text-slate-800` heading, `text-slate-600` body, `text-slate-500` eyebrow). When set, the eyebrow and body inherit the colour at reduced opacity. +- Text colour is applied to the section only when set. When empty the copy uses the theme slate defaults (`text-slate-800` heading, `text-slate-600` body, `text-slate-500` eyebrow). When set, the eyebrow and body inherit the colour at reduced opacity, and the heading carries it as an inline style because the base stylesheet (and the global heading colour setting) give every heading an explicit colour that inheritance cannot override. - The image is lazy-loaded. This section is not expected to hold the LCP image. - There is no client-side behavior and no GraphQL dependency. diff --git a/partials/section_image_text.html b/partials/section_image_text.html index db88a32..116eecd 100644 --- a/partials/section_image_text.html +++ b/partials/section_image_text.html @@ -29,7 +29,7 @@

{{ settings.image_text_eyebrow }}

{% endif %} {% if settings.image_text_heading %} -

{{ settings.image_text_heading }}

+

{{ settings.image_text_heading }}

{% endif %} {% if settings.image_text_body %}
{{ settings.image_text_body|safe }}
From a3f77f5915c818ab68a08649b8f8f0104f014721 Mon Sep 17 00:00:00 2001 From: Devin Michael Date: Mon, 21 Sep 2026 16:44:17 +0700 Subject: [PATCH 6/6] Drop a stray diff3 marker from the changelog merge Co-Authored-By: Claude Fable 5.1 --- CHANGELOG.md | 1 - 1 file changed, 1 deletion(-) diff --git a/CHANGELOG.md b/CHANGELOG.md index 0ee9c72..3e54893 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -9,7 +9,6 @@ The GitHub release body is a summary, not a copy of the changelog section. Write ## Unreleased - Added the Image with Text homepage section (`partials/section_image_text.html`, Theme Settings > Homepage > Image with Text, toggle `show_image_text`, off by default). It is a 50/50 image and copy split with image left/right, a square/portrait/landscape/native image ratio, eyebrow, heading, rich-text body, a CTA with primary/secondary/outline styles, and background and text colours; on mobile it stacks image first. It is included from `templates/index.html` between Featured Categories and On Sale. With the toggle on and nothing configured it renders the same setup placeholder pattern as the other sections. `partials/cta_button.html` gained a `secondary` style so the section can reuse the existing `.btn-secondary` class. Spec: `docs/section-specs/image-text.md`. `assets/main.css` grew from 50298 to 50536 bytes for the new ratio and ordering utilities. -||||||| bdf40f2 - Published the app-hook contract in `docs/app-hooks.md`: an inventory of all 13 `{% app_hook %}` sites with the file, page position, surrounding DOM, and whether each renders once per page or once per product card; the `_` naming rule derived from the existing names; and a stability policy (existing hook names are stable, new hooks are additive, renames and removals keep the old hook for at least one minor release). The two locations the platform Tag Reference documents, `global_header` and `global_footer`, are both carried. Linked from `docs/README.md`, `docs/extending-spark.md`, and `CLAUDE.md`; the matching `TODOS.md` entry is closed. - The default for `membership_status_metadata_key` is now the neutral `member_status` in `configs/settings_schema.json`, `layouts/base.html`, and `assets/js/spark-membership-pricing.js`. It previously shipped a store-specific key. A store that enabled membership pricing without setting the key explicitly must now set it in Theme Settings; the migration note in `docs/theme-settings-partials.md` names the old value. - `PLAN.md` now lists all six Web Components (it omitted ``) and states the shipped version as 1.4.1 rather than 1.1.1.