
        /* v4.20.8/9 — CRITICAL MOBILE FIX: defeat the v4.4.61 gallery freeze
         * on mobile + body-level overflow safety net.
         *
         * v4.20.17 — strengthened: added !important to the body overflow
         * rule and a JavaScript runtime safety net at the end of the
         * style block that sets inline style on html+body. Inline styles
         * beat external CSS, so this is the final escape hatch if any
         * theme or third-party CSS rule has overflow:visible !important
         * on html/body. */
        @media (max-width: 720px) {
            /* === v4.20.17 — Gallery responsive fix, frame-AGNOSTIC ===
             *
             * The diagnostic at 396px viewport showed:
             *   gallery width=438px inside parent width=364px
             *   gallery parent: <div class="tm-has-options ..."> (the
             *   product container, display:flex)
             *   Frame present: NO
             *
             * The .flowla-gallery-frame rules below don't apply when the
             * EOR relocator goes into SEO mode (not gallery mode) — no
             * frame wraps the gallery. Without a frame, the gallery is
             * a direct flex child of the product container with
             * min-width:auto (the default for flex items), which means
             * it refuses to shrink below the image's intrinsic width.
             * Image at ~510px intrinsic = gallery locked at 510px or
             * whatever its content forces.
             *
             * Fix: target .woocommerce-product-gallery directly with
             * min-width:0 to unblock flex shrinking, max-width:100% to
             * bound to parent. Plus images max-width:100% height:auto
             * to scale them responsively. */
            .woocommerce-product-gallery,
            .product .woocommerce-product-gallery,
            .product div.images,
            .product .images {
                max-width: 100% !important;
                width: auto !important;
                min-width: 0 !important;
                box-sizing: border-box !important;
            }
            .woocommerce-product-gallery img,
            .woocommerce-product-gallery .wp-post-image,
            .product div.images img,
            .product div.images .wp-post-image {
                max-width: 100% !important;
                height: auto !important;
            }
            /* FlexSlider viewport — WC's gallery wraps slides in this.
             * Without min-width:0 it inherits the same flex-shrink:auto
             * problem and the slide wrapper inside (with width:400% for
             * slide count) magnifies it. */
            .woocommerce-product-gallery .flex-viewport {
                max-width: 100% !important;
                min-width: 0 !important;
                box-sizing: border-box !important;
            }

            /* Product container — ensure direct children are
             * shrinkable so flex distribution works correctly. */
            [id^="product-"].product {
                max-width: 100% !important;
                box-sizing: border-box !important;
            }
            [id^="product-"].product > * {
                min-width: 0;
                max-width: 100%;
                box-sizing: border-box;
            }


            /* Variation-add-to-cart wrapper — the QC's grand-parent.
             * Same flex-shrink unblock so its children can size to
             * available width rather than to content intrinsic width. */
            .woocommerce-variation-add-to-cart,
            .variations_button,
            .variations_form,
            .single_variation_wrap {
                max-width: 100% !important;
                min-width: 0 !important;
                box-sizing: border-box !important;
            }

            /* === v4.20.8 — Frame-scoped overrides (kept for pages where
             *     the relocator does create a .flowla-gallery-frame) === */
            .flowla-gallery-frame,
            .flowla-gallery-frame > .woocommerce-product-gallery,
            .flowla-gallery-frame > div.images,
            .flowla-gallery-frame .woocommerce-product-gallery,
            .flowla-gallery-frame .images {
                max-width: 100% !important;
                width: auto !important;
                min-width: 0 !important;
                box-sizing: border-box !important;
            }
            .flowla-gallery-frame img,
            .flowla-gallery-frame .wp-post-image {
                max-width: 100% !important;
                width: auto !important;
                height: auto !important;
            }

            /* Body-level overflow safety net. */
            html, body {
                max-width: 100vw !important;
                overflow-x: clip !important;
                overflow-x: hidden !important; /* fallback chain */
            }
            @supports (overflow-x: clip) {
                html, body { overflow-x: clip !important; }
            }
        }

        .fx-eor-wrap { max-width: 1180px; margin: 30px auto; padding: 0 24px; font-family: 'Jost', system-ui, -apple-system, sans-serif; color: #1A1A1A; }
        /* v4.7.15: when the relocator has placed the wrapper inside .fbf-seo-copy
           or the gallery frame, drop the auto-margins + max-width so the wrapper
           fills its container naturally. The relocator adds a marker class. */
        .fx-eor-wrap.fx-eor-wrap--inline { max-width: none; margin: 0 0 24px 0; padding: 0; }
        /* v4.18.5 — gallery frame defensive clamps. The relocator wraps the
         * WC product gallery in .flowla-gallery-frame and freezes it at the
         * pre-wrap width. If the freeze step ever fails (offsetWidth=0 at
         * freeze time — the trait-checkout-angel.php abort-on-zero-width
         * fix is the primary defence), these rules ensure the gallery and
         * its image stay clamped to the frame width regardless.
         * Low specificity so themes that intentionally want a wider gallery
         * can still win. */
        .flowla-gallery-frame {
            max-width: 100%;
            box-sizing: border-box;
        }
        .flowla-gallery-frame .woocommerce-product-gallery,
        .flowla-gallery-frame > div.images {
            max-width: 100%;
            box-sizing: border-box;
        }
        .flowla-gallery-frame .woocommerce-product-gallery img,
        .flowla-gallery-frame .woocommerce-product-gallery .wp-post-image,
        .flowla-gallery-frame > div.images img {
            max-width: 100%;
            height: auto;
        }
        /* Anti-flash: hidden until the relocator finishes placing it. */
        .fx-eor-wrap--anchoring { visibility: hidden; }
        /* v4.7.25: diagnostic panel — visible on-page after Add to Basket click. */
        .fx-eor-diag { margin: 14px 0; background: #1F3A24; color: #E8F7D5; border-radius: 6px; overflow: hidden; font-family: 'Jost', system-ui, sans-serif; box-shadow: 0 4px 14px -6px rgba(31, 58, 36, 0.4); }
        .fx-eor-diag-header { padding: 10px 14px; background: rgba(0,0,0,0.18); display: flex; justify-content: space-between; align-items: center; gap: 10px; font-size: 13px; }
        .fx-eor-diag-header strong { font-weight: 600; }
        .fx-eor-diag-copy { background: #85C727; color: #1F3A24; border: 0; padding: 6px 14px; font-size: 11px; font-weight: 600; letter-spacing: 1px; text-transform: uppercase; cursor: pointer; border-radius: 3px; font-family: inherit; }
        .fx-eor-diag-copy:hover { background: #95D837; }
        .fx-eor-diag-copy:active { background: #75B717; }
        .fx-eor-diag-close { background: transparent; color: #fff; border: 1px solid rgba(255,255,255,0.3); width: 26px; height: 26px; cursor: pointer; border-radius: 3px; font-size: 16px; line-height: 1; padding: 0; font-family: inherit; }
        .fx-eor-diag-close:hover { background: rgba(255,255,255,0.1); }
        .fx-eor-diag-body { padding: 12px 14px; margin: 0; font-family: 'Courier New', monospace; font-size: 11px; line-height: 1.4; color: #E8F7D5; white-space: pre-wrap; word-break: break-word; max-height: 400px; overflow-y: auto; background: rgba(0,0,0,0.1); }
        .fx-eor-diag--error { background: #5C1F1F; }
        .fx-eor-diag--error .fx-eor-diag-body { color: #FFD3B3; }
        .fx-eor-diag--success { background: #1F4A24; }
        .fx-eor-accordion { background: #FAFAF8; border: 1px solid #C8DEC0; border-radius: 6px; overflow: hidden; transition: all 0.2s; }
        /* v4.25.79 — SPEED (industry best practice): skip layout + paint of any
         * EOR section that is off-screen, so the browser doesn't have to render
         * all the below-the-fold content (especially the eager custom panes)
         * up front. The section renders the moment it nears the viewport, so
         * every product is still shown when scrolled to. contain-intrinsic-size
         * reserves an estimated height (auto = remember the real size after the
         * first render) to keep the scrollbar stable. This restores the
         * "only render what's needed" discipline and fixes the mobile scroll
         * hang caused by heavy off-screen sections. */
        /* v0.2.36 — THE PHANTOM SPACE CURED (owner, 15 Aug: mobile "took
         * ages and didn't load properly"). The Guardian's v4.25.79 rule
         * above put content-visibility on the WHOLE section with a 600px
         * estimate — but the common case is a COLLAPSED section whose real
         * height is ~90px, so nine sections reserved ~3,600px of blank,
         * scroll-snapping space on every product page, both viewports
         * (measured live: wrap 5,515px reserved vs 1,885px real; the fix
         * measured live at 2,091px — 3,424px reclaimed). The optimisation
         * MOVES to the section BODY: a collapsed body is display:none and
         * costs nothing, an expanded off-screen body still skips layout/
         * paint exactly as v4.25.79 intended for the heavy panes. */
        .fx-eor-acc-body { content-visibility: auto; contain-intrinsic-size: auto 500px; }
        /* v0.2.57 — RENDER ONCE, THEN STAY (the owner, 16 Aug: "if you scroll
         * the explore section out of the page — top and bottom — the images
         * disappear when you scroll back; make sure they remain stable").
         * content-visibility:auto lets the browser DISCARD a body's rendering
         * while it is off-screen and rebuild it on return — the images then
         * re-decode late (or blank) on every scroll-back. It buys a saving
         * only for sections that are heavy AND never seen; now that batches
         * are 8 cards and the code is in cached files, stability is worth
         * more than that sliver. The rule below is applied by the tiny
         * observer in the footer: the moment a body has been on screen once
         * it is marked fx-eor-seen and rendered permanently — the initial
         * page still skips the far-below-fold work exactly as before. */
        .fx-eor-acc-body.fx-eor-seen { content-visibility: visible; contain-intrinsic-size: auto; }
        /* v4.7.33: spacing between stacked accordions when multiple sections present. */
        .fx-eor-accordion + .fx-eor-accordion { margin-top: 12px; }
        .fx-eor-accordion.open { border-color: #85C727; box-shadow: 0 4px 14px -6px rgba(31, 58, 36, 0.12); }
        .fx-eor-acc-header { padding: 16px 22px; display: flex; align-items: center; gap: 16px; cursor: pointer; user-select: none; transition: background 0.15s; }
        .fx-eor-acc-header:hover { background: #F0F7E5; }
        /* v4.7.31: gradient flows cream → mint so it visually pours into the
         * mint body below. Eliminates the abrupt color shift James flagged. */
        .fx-eor-accordion.open .fx-eor-acc-header { background: linear-gradient(180deg, #FAFAF8, #E8F7D5); border-bottom: 1px solid #C8DEC0; }
        /* v4.7.40: direct-link tab variant — visually matches the accordions
         * but the entire row is a navigation anchor. No dropdown body, no
         * chevron rotation. Arrow → replaces chevron ▼ as the affordance
         * indicator. Hover state mirrors accordion behaviour for consistency. */
        a.fx-eor-accordion--direct { display: block; text-decoration: none; color: inherit; }
        a.fx-eor-accordion--direct:hover { border-color: #85C727; box-shadow: 0 4px 14px -6px rgba(31, 58, 36, 0.12); transform: translateY(-1px); }
        a.fx-eor-accordion--direct:hover .fx-eor-acc-header { background: linear-gradient(180deg, #FAFAF8, #E8F7D5); }
        a.fx-eor-accordion--direct:hover .fx-eor-acc-title h3 { color: #2A6132; }
        a.fx-eor-accordion--direct:hover .fx-eor-chevron--arrow { transform: translateX(4px); color: #2A6132; }
        .fx-eor-chevron--arrow { transition: transform 0.15s, color 0.15s; }
        .fx-eor-pill { background: #1F3A24; color: #85C727; font-size: 9px; padding: 4px 10px; border-radius: 14px; letter-spacing: 1.5px; text-transform: uppercase; font-weight: 600; flex-shrink: 0; }

        /* v4.20.1 — In Season accordion pill + heading + items.
         *
         * The In Season pill uses a warm amber/gold tone (#C68B3A on cream)
         * to distinguish it from the sage-green pill family used by
         * Siblings / Floral Bunches / By Colour. Customers reading the
         * column register four distinct accordion "kinds":
         *   ★    Siblings        — sage pill (this product's variants)
         *   🌺   In Season Now    — amber pill (what's at peak THIS month)
         *   ★★   Floral Bunches  — sage pill (all varieties index)
         *   ★★★  By Colour       — sage pill (colour-axis collections)
         *
         * The amber tone signals "special, premium, time-limited" — same
         * language as the "Currently in season" copy in the order-received
         * card. The accordion also gets a faint amber top border so it
         * doesn't visually merge with the adjacent sage accordions when
         * collapsed. */
        .fx-eor-pill--inseason {
            background: #C68B3A;
            color: #FFF8EC;
            letter-spacing: 1px;
            font-size: 10px;
            padding: 5px 11px;
        }
        .fx-eor-accordion--inseason {
            border-top: 2px solid #C68B3A;
        }
        .fx-eor-accordion--inseason.open {
            border-color: #C68B3A;
            box-shadow: 0 4px 14px -6px rgba(198, 139, 58, 0.20);
        }
        .fx-eor-accordion--inseason .fx-eor-acc-header:hover {
            background: #FFF8EC;
        }
        .fx-eor-accordion--inseason.open .fx-eor-acc-header {
            background: linear-gradient(180deg, #FAFAF8, #FFE8C5);
            border-bottom: 1px solid #E0C088;
        }

        /* Variety list — In Season variant. Star-marked items (peak bloom
         * this month) get a subtle amber background, bolder name, and
         * a star prefix that pulses gently on hover.
         *
         * v4.20.7 — Star items now span the FULL WIDTH of the accordion
         * body (grid-column: 1 / -1). The narrow 1/3 column was cramping
         * the blurb and forcing the sourcing line into a 3-column flex
         * wrap. Going full-width gives the star bloom room to breathe
         * and lets the sourcing line flow horizontally as a single
         * paragraph beneath the blurb. */
        /* v4.20.18 — Star variety panel background.
         *
         * Was: linear-gradient(90deg, #FFF8EC 0%, transparent 60%) —
         * cream/gold fading right. James (2026-05-19): "the blended
         * gold on the Peonies one — just have the gold at the edge
         * as is and green for the rest". The gold border-left stays
         * (it's the only gold accent now); the inner panel switches
         * to FBF mint #E8F7D5 — same colour as .fx-eor-acc-body so
         * the star panel reads as a slightly-bordered subdivision
         * of the accordion rather than a separately-toned card. */
        .fx-eor-variety-list--inseason .fx-eor-variety-item--peak {
            background: #E8F7D5;
            border-left: 3px solid #C68B3A;
            grid-column: 1 / -1;
            margin-bottom: 8px;
            border-radius: 0 4px 4px 0;
        }
        .fx-eor-variety-list--inseason .fx-eor-variety-item--peak .fx-eor-variety-name {
            font-weight: 600;
            color: #1F3A24;
        }
        .fx-eor-variety-peak-badge {
            display: inline-flex;
            align-items: center;
            justify-content: center;
            width: 18px;
            height: 18px;
            background: #C68B3A;
            color: #fff;
            border-radius: 50%;
            font-size: 11px;
            line-height: 1;
            margin-right: 10px;
            flex-shrink: 0;
            box-shadow: 0 1px 3px rgba(198, 139, 58, 0.3);
        }
        .fx-eor-variety-list--inseason .fx-eor-variety-link:hover .fx-eor-variety-peak-badge {
            transform: scale(1.12);
            transition: transform 0.15s;
        }

        /* v4.20.2 — Variety BLURB display.
         *
         * Star variety: blurb shown permanently beneath the name in
         * small italic grey. This is the month's headline flower so it
         * deserves a sentence of romance front-and-centre.
         *
         * Supporting cast: blurbs hide by default and reveal on hover
         * (desktop) — keeps the list scannable. On mobile, where hover
         * doesn't exist, all blurbs show inline because the list is
         * already vertically expanded so the extra height is fine.
         *
         * The .fx-eor-variety-row wrapper groups badge + name + arrow
         * into a single flex line so the blurb wraps neatly below the
         * name row, not inline with it. */
        .fx-eor-variety-list--inseason .fx-eor-variety-link {
            display: flex;
            flex-direction: column;
            align-items: stretch;
            gap: 4px;
            padding: 12px 16px;
        }
        .fx-eor-variety-list--inseason .fx-eor-variety-row {
            display: flex;
            align-items: center;
            justify-content: space-between;
            gap: 0;
        }
        /* v4.20.7 — Star variety: the row's first cluster (star + name)
         * sits left, the arrow pushes to the far right via space-between.
         * The blurb and sourcing then flow as full-width block paragraphs
         * below, aligned under the name (margin-left compensates for the
         * star badge so the text indents under "PEONIES" not under "★"). */
        .fx-eor-variety-list--inseason .fx-eor-variety-item--peak .fx-eor-variety-row {
            justify-content: space-between;
        }
        .fx-eor-variety-list--inseason .fx-eor-variety-item--peak .fx-eor-variety-blurb {
            margin: 6px 0 0 28px;
            max-width: 100%;
        }
        .fx-eor-variety-list--inseason .fx-eor-variety-item--peak .fx-eor-variety-sourcing {
            margin: 4px 0 0 28px;
            max-width: 100%;
        }
        .fx-eor-variety-blurb {
            display: none;
            font-size: 12px;
            color: #4A5F4F;
            font-style: italic;
            line-height: 1.45;
            margin: 2px 0 0 28px;
            font-family: 'Jost', sans-serif;
        }
        /* Star variety: blurb always visible. */
        .fx-eor-variety-list--inseason .fx-eor-variety-item--peak .fx-eor-variety-blurb {
            display: block;
            color: #2A6132;
            font-size: 12.5px;
        }
        /* Hover on any item reveals its blurb (desktop). */
        @media (hover: hover) and (pointer: fine) {
            .fx-eor-variety-list--inseason .fx-eor-variety-link:hover .fx-eor-variety-blurb {
                display: block;
            }
            .fx-eor-variety-list--inseason .fx-eor-variety-link:hover .fx-eor-variety-sourcing {
                display: block;
            }
        }
        /* Mobile / coarse pointer: always show all blurbs. */
        @media (hover: none), (pointer: coarse) {
            .fx-eor-variety-list--inseason .fx-eor-variety-blurb {
                display: block;
                font-size: 11.5px;
                margin-left: 0;
            }
            .fx-eor-variety-list--inseason .fx-eor-variety-item--peak .fx-eor-variety-blurb {
                margin-left: 0;
            }
            .fx-eor-variety-list--inseason .fx-eor-variety-sourcing {
                display: block;
                margin-left: 0;
            }
        }

        /* v4.20.5/7 — Sourcing line ("This month from: Netherlands + France...").
         * v4.20.7 — Changed from display:flex (which created 3 stacked
         * mini-columns when squeezed into a narrow card) to display:block
         * with inline children. Now flows as one horizontal paragraph
         * that wraps naturally like body text. Star items get this at
         * full accordion width since they span the whole grid row. */
        .fx-eor-variety-sourcing {
            display: none;
            font-size: 11.5px;
            color: #4A5F4F;
            margin: 4px 0 0 28px;
            font-family: 'Jost', sans-serif;
            line-height: 1.5;
            max-width: 100%;
        }
        .fx-eor-variety-sourcing-icon {
            display: inline;
            font-size: 12px;
            opacity: 0.7;
            margin-right: 4px;
        }
        .fx-eor-variety-sourcing-label {
            display: inline;
            font-weight: 500;
            color: #2A6132;
            margin-right: 4px;
        }
        .fx-eor-variety-sourcing-text {
            display: inline;
            font-style: italic;
            color: #4A5F4F;
        }
        /* Star variety: sourcing always visible (same as blurb). */
        .fx-eor-variety-list--inseason .fx-eor-variety-item--peak .fx-eor-variety-sourcing {
            display: block;
        }

        /* ─────────────────────────────────────────────────────────────
         * v4.20.18 — Desktop layout overhaul for In Season Now accordion
         * (James 2026-05-19 image: 3-column variety grid + cramped Also
         * Available footer were rendering "terribly" inside the narrow
         * left column of the order-pay product page).
         *
         * v4.20.20 — Specificity bug fix + always-visible blurb/sourcing.
         *
         * Bug in v4.20.18: the single-column override `grid-template-
         * columns: 1fr` used selector `.fx-eor-variety-list--inseason`
         * (specificity 0,1,0). The base rule at line ~10873 uses
         * `.fx-eor-variety-list { ... grid-template-columns: repeat(3,
         * 1fr) }` — also (0,1,0). At equal specificity, the LATER rule
         * wins; base sits 1490 lines further down, so it won. The
         * in-season list stayed 3-column. Fix: chain both classes —
         * `.fx-eor-variety-list.fx-eor-variety-list--inseason` —
         * specificity (0,2,0), beats the base cleanly without needing
         * !important.
         *
         * Always-visible blurb + sourcing (NEW in v4.20.20). James
         * 2026-05-19 image IMG_2348: "I don't like the hover element
         * to it ... the text in the green section should look like
         * the PEONIES title and text section". Previously only the
         * star variety (PEONIES) showed its blurb + sourcing inline;
         * supporting varieties (Freesia, Iris, Lilies, Lisianthus,
         * Ranunculus, Stocks, Sweet Peas) only revealed theirs on
         * desktop hover via @media (hover: hover) rule at line ~9277.
         * On a touch device that pattern degrades fine (the
         * hover-none media query at line ~9286 shows everything) —
         * but James saw it on desktop and found the hover reveal
         * jumpy. v4.20.20 makes blurb + sourcing visible on ALL
         * in-season list items, on all viewports, no hover required.
         * The @media (hover: hover) rules below become no-ops (same
         * display:block they would have applied) — left in place so
         * the cascade is explicit if anyone reads it.
         *
         * Three changes wrapped together:
         *
         *   (a) Variety list = single-column scrollable on every
         *       viewport (was 3-column grid on desktop, 1-col on
         *       mobile). Chained-class selector wins specificity.
         *       Max-height keeps the accordion compact even when
         *       many varieties peak.
         *
         *   (b) text-decoration:none !important on the in-season
         *       variety link AND on all its descendants. The theme's
         *       a:link underline was bleeding through the plugin's
         *       text-decoration:none from line 10775.
         *
         *   (c) .fx-eor-also-available-list becomes a 3-column CSS
         *       grid (was inline flow with " · " separators).
         *       Separator dots display:none. 2 columns on mobile.
         *
         *   (d) Blurb + sourcing visible on ALL in-season items,
         *       styled to match PEONIES typography (green #2A6132,
         *       12.5px, italic). Left margin reset to 0 on supporting
         *       items (no star badge so the 28px indent doesn't
         *       align with anything).
         * ───────────────────────────────────────────────────────────── */
        .fx-eor-variety-list.fx-eor-variety-list--inseason {
            grid-template-columns: 1fr;
            gap: 0;
            max-height: 380px;
            overflow-y: auto;
            scrollbar-width: thin;
            scrollbar-color: #C8DEC0 transparent;
            padding-right: 4px; /* breathing room for scrollbar */
        }
        .fx-eor-variety-list--inseason::-webkit-scrollbar {
            width: 6px;
        }
        .fx-eor-variety-list--inseason::-webkit-scrollbar-track {
            background: transparent;
        }
        .fx-eor-variety-list--inseason::-webkit-scrollbar-thumb {
            background: #C8DEC0;
            border-radius: 3px;
        }
        .fx-eor-variety-list--inseason::-webkit-scrollbar-thumb:hover {
            background: #A8C8A0;
        }
        .fx-eor-variety-list--inseason .fx-eor-variety-link,
        .fx-eor-variety-list--inseason .fx-eor-variety-link:link,
        .fx-eor-variety-list--inseason .fx-eor-variety-link:visited,
        .fx-eor-variety-list--inseason .fx-eor-variety-link:hover,
        .fx-eor-variety-list--inseason .fx-eor-variety-link:active {
            text-decoration: none !important;
        }
        .fx-eor-variety-list--inseason .fx-eor-variety-link *,
        .fx-eor-variety-list--inseason .fx-eor-variety-blurb,
        .fx-eor-variety-list--inseason .fx-eor-variety-sourcing,
        .fx-eor-variety-list--inseason .fx-eor-variety-sourcing-text,
        .fx-eor-variety-list--inseason .fx-eor-variety-sourcing-label,
        .fx-eor-variety-list--inseason .fx-eor-variety-name {
            text-decoration: none !important;
        }

        /* v4.20.20 — Always-visible blurb + sourcing on ALL in-season
         * varieties. Specificity (0,2,0) beats the base (0,1,0)
         * `.fx-eor-variety-blurb { display: none }` at line ~9261.
         * Star variety's (0,3,0) peak-specific rule at line ~9271 still
         * wins for PEONIES (which keeps its larger 12.5px green
         * styling) — supporting varieties inherit the new defaults
         * here: green-italic to match PEONIES family voice but at
         * 12px so PEONIES still reads as the dominant star. */
        .fx-eor-variety-list--inseason .fx-eor-variety-blurb {
            display: block;
            color: #2A6132;
            font-size: 12px;
            font-style: italic;
            line-height: 1.45;
            font-family: 'Jost', sans-serif;
            margin: 4px 0 0 0;
            max-width: 100%;
        }
        .fx-eor-variety-list--inseason .fx-eor-variety-sourcing {
            display: block;
            font-size: 11.5px;
            color: #4A5F4F;
            margin: 3px 0 0 0;
            font-family: 'Jost', sans-serif;
            line-height: 1.5;
            max-width: 100%;
        }
        /* Per-item spacing: supporting items get a soft divider so the
         * stacked title+blurb+sourcing chunks read as separate cards
         * even without a box around each one. PEONIES already has its
         * own background + gold border treatment from line ~9198, so
         * exclude it from this rule. */
        .fx-eor-variety-list--inseason .fx-eor-variety-item:not(.fx-eor-variety-item--peak) {
            border-bottom: 1px solid #D8E8D0;
        }
        .fx-eor-variety-list--inseason .fx-eor-variety-item:not(.fx-eor-variety-item--peak):last-child {
            border-bottom: 0;
        }
        .fx-eor-variety-list--inseason .fx-eor-variety-item:not(.fx-eor-variety-item--peak) .fx-eor-variety-link {
            padding: 10px 14px;
        }
        /* Hover: NO padding-jump (James 2026-05-19 called the layout-
         * shifting hover "the hover element" — getting rid of it).
         * Subtle background tint on supporting items only — PEONIES
         * has its own green-mint box so leave it alone. Name colour
         * darkens slightly + arrow nudges right as the click cue. */
        .fx-eor-variety-list--inseason .fx-eor-variety-link:hover {
            background: transparent;
        }
        .fx-eor-variety-list--inseason .fx-eor-variety-item:not(.fx-eor-variety-item--peak) .fx-eor-variety-link:hover {
            background: #F4FAEB;
        }
        .fx-eor-variety-list--inseason .fx-eor-variety-link:hover .fx-eor-variety-name {
            color: #1F3A24;
        }
        .fx-eor-variety-list--inseason .fx-eor-variety-link:hover .fx-eor-variety-arrow {
            opacity: 1;
            color: #2A6132;
            transform: translateX(2px);
        }
        /* End v4.20.18/v4.20.20 in-season list overhaul ─────────────── */

        /* v4.20.5 — "Also available this month" footer.
         * Lives below the variety list. Year-round varieties not at peak this
         * month still get a quiet shout-out so customers know they're sourceable. */
        .fx-eor-also-available {
            margin-top: 22px;
            padding: 14px 16px;
            background: #FAFAF8;
            border-left: 3px solid #D8E8D0;
            border-radius: 0 4px 4px 0;
            font-family: 'Jost', sans-serif;
            max-width: 100%;
            box-sizing: border-box;
        }
        .fx-eor-also-available-label {
            display: block;
            font-size: 11px;
            text-transform: uppercase;
            letter-spacing: 1px;
            color: #2A6132;
            font-weight: 600;
            margin-bottom: 8px;
        }
        .fx-eor-also-available-list {
            /* v4.20.18 — 3-column CSS grid (was inline flow with " · "
             * separators). James (2026-05-19): "for the alternative
             * flower types these used to be split on three rows —
             * make that look like it does on mobile too". Mobile
             * gets 2 columns inside the @media (max-width: 700px)
             * block below. Separators (.fx-eor-also-available-sep)
             * are display:none so they don't occupy grid cells. */
            display: grid;
            grid-template-columns: repeat(3, 1fr);
            gap: 6px 14px;
            font-size: 13px;
            line-height: 1.5;
            color: #4A5F4F;
            overflow-wrap: anywhere;
        }
        .fx-eor-also-available-item {
            /* v4.20.18 — Clean text row inside the grid (was inline
             * link with dotted bottom-border on hover). Underline
             * forced off — theme a:link was bleeding through. */
            color: #2A6132;
            text-decoration: none !important;
            padding: 2px 0;
            transition: color 0.15s, background 0.15s;
            border-radius: 2px;
        }
        .fx-eor-also-available-item:hover {
            color: #1F3A24;
            background: #F4FAEB;
            padding-left: 4px;
        }
        .fx-eor-also-available-sep {
            /* v4.20.18 — Hidden in grid mode (used to be the " · "
             * separator for inline flow). Kept in markup for
             * graceful degradation if CSS fails to load. */
            display: none;
        }

        /* v4.20.5/6 — Floral Bunches accordion: peak + out-of-season indicators.
         *
         * Peak: small amber 🌺 inline-block before the name, no flex-shrink
         * dependency. Out: reduced opacity on the whole item + a small
         * "Returns Dec" inline pill. Both elements are inline so they
         * participate in normal text flow and wrap correctly on mobile. */
        .fx-eor-variety-bunches-peak-badge {
            display: inline-block;
            font-size: 13px;
            line-height: 1;
            vertical-align: -1px;
        }
        .fx-eor-variety-item--bunches-peak .fx-eor-variety-name {
            color: #1F3A24;
            font-weight: 500;
        }
        .fx-eor-variety-item--bunches-out {
            opacity: 0.55;
        }
        .fx-eor-variety-item--bunches-out:hover {
            opacity: 0.85;
        }
        .fx-eor-variety-returns {
            display: inline-block;
            padding: 1px 7px;
            background: #F3F0EA;
            color: #806950;
            font-size: 10px;
            text-transform: uppercase;
            letter-spacing: 0.5px;
            font-weight: 500;
            border-radius: 10px;
            font-family: 'Jost', sans-serif;
            vertical-align: 1px;
            white-space: nowrap;
        }

        .fx-eor-acc-title { flex: 1; min-width: 0; }
        .fx-eor-acc-title h3 { font-family: 'Cormorant Garamond', Georgia, serif; font-size: 22px; color: #1F3A24; font-weight: 500; line-height: 1.1; margin: 0; }
        .fx-eor-preview { font-size: 11px; color: #4A5F4F; letter-spacing: 0.5px; margin-top: 1px; }
        /* v4.19.0 — Small inline count pill in header preview ("(207)" etc). */
        .fx-eor-preview-count {
            color: #2A6132; font-weight: 500;
            margin-left: 2px;
        }

        /* v4.19.96 — MULTI-SELECT INSTRUCTION CUE.
         *
         * The "tick the boxes" instruction in the accordion header needs
         * to feel like a button-shaped pill — not a sentence of body text
         * that customers skim past. Sage-green chip, leading tick glyph
         * in a contrast circle, slight letter-spacing so it reads as a
         * label rather than running into the surrounding copy.
         *
         * The pill stays inline so the line still reads naturally:
         *   "Same flower, different varieties. [✓ Tick the boxes...] (12)"
         * but the visual weight pulls the eye to the imperative.
         * ──────────────────────────────────────────────────────────────── */
        .fx-eor-tick-cue {
            display: inline-flex;
            align-items: center;
            gap: 6px;
            background: #E8F7D5;
            border: 1px solid #85C727;
            color: #1F3A24;
            font-weight: 500;
            padding: 3px 10px 3px 4px;
            border-radius: 12px;
            margin: 0 4px;
            font-size: 11px;
            letter-spacing: 0.3px;
            text-transform: none;
            white-space: nowrap;
        }
        .fx-eor-tick-cue-icon {
            display: inline-flex;
            align-items: center;
            justify-content: center;
            width: 16px;
            height: 16px;
            background: #2A6132;
            color: #fff;
            border-radius: 3px;
            font-size: 11px;
            font-weight: 700;
            line-height: 1;
            flex-shrink: 0;
        }
        /* Mobile — wrap pill onto its own line so it doesn't crush.
         * v4.20.17: also allow the text INSIDE the pill to wrap so
         * the full "Tick the boxes to add multiple varieties to your
         * order" message doesn't extend past the viewport and overlap
         * the accordion's chevron arrow (which is what James reported
         * on 2026-05-19 14:53). The base rule's white-space:nowrap is
         * the right default for desktop where the pill sits inline
         * with the description on a single wide row; on mobile the
         * pill needs to soft-wrap. align-items:flex-start keeps the
         * ✓ icon aligned to the first line of text when wrapping. */
        @media (max-width: 720px) {
            .fx-eor-tick-cue {
                display: inline-flex;
                margin: 4px 0 0 0;
                font-size: 10.5px;
                padding: 4px 10px 4px 4px;
                white-space: normal;
                max-width: 100%;
                align-items: flex-start;
                line-height: 1.35;
            }
            .fx-eor-tick-cue-icon {
                width: 14px;
                height: 14px;
                font-size: 10px;
                margin-top: 1px;
            }
        }

        /* v4.19.96 — TICK ATTENTION PULSE.
         *
         * When the accordion transitions to preview state, the ticks on
         * the visible cards pulse 3 times to teach the affordance.
         * Customers who've used the site before see one quick reminder;
         * customers who never noticed the ticks now notice them.
         *
         * Why 3 reps not infinite: an always-on pulse becomes noise.
         * 3 reps × 900ms = ~2.7s total then completely still — long
         * enough to teach, short enough not to nag.
         *
         * Animation only fires on .is-preview state (not .open). Once
         * the customer expands to full open OR ticks any card, we
         * remove the pulse class via JS so it doesn't replay.
         *
         * Pure CSS transforms + box-shadow only (cheap to animate). */
        @keyframes fx-eor-tick-pulse {
            0%   { transform: scale(1);    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08); }
            30%  { transform: scale(1.18); box-shadow: 0 0 0 8px rgba(133, 199, 39, 0.45); }
            60%  { transform: scale(1);    box-shadow: 0 0 0 0   rgba(133, 199, 39, 0); }
            100% { transform: scale(1);    box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08); }
        }
        @media (prefers-reduced-motion: no-preference) {
            .fx-eor-accordion.is-preview .fx-eor-frontcard:not(.selected) .fx-eor-check-col.fx-eor-tick-attention {
                animation: fx-eor-tick-pulse 900ms ease-out 3;
            }
        }
        /* Permanent visual reinforcement on the first card's tick in
         * preview state — a sage drop-shadow halo so even after the
         * pulse stops, the eye still lands there. */
        .fx-eor-accordion.is-preview .fx-eor-frontcard:first-child:not(.selected) .fx-eor-check-col {
            box-shadow: 0 0 0 3px rgba(133, 199, 39, 0.25), 0 1px 3px rgba(0, 0, 0, 0.08);
        }

        .fx-eor-chevron { color: #4A5F4F; font-size: 14px; transition: transform 0.25s; }
        .fx-eor-accordion.open .fx-eor-chevron { transform: rotate(180deg); color: #2A6132; }
        /* v4.19.95 — Preview-state chevron: half-rotated so it sits between
         * closed (0°) and fully-open (180°), signalling tri-state cycle. */
        .fx-eor-accordion.is-preview .fx-eor-chevron { transform: rotate(90deg); color: #4A5F4F; }
        .fx-eor-acc-body { padding: 20px 22px 22px; display: none; background: #E8F7D5; }
        .fx-eor-accordion.open .fx-eor-acc-body { display: block; }

        /* v4.19.95 — TRI-STATE ACCORDION (.is-preview).
         *
         * The Explore Our Range accordion now cycles through three states
         * instead of the binary open/closed:
         *
         *   State A — collapsed (default, no class)
         *     → click expands to preview
         *   State B — preview (.is-preview class on .fx-eor-accordion)
         *     → body is visible, but only first N cards show, where N
         *       matches the grid column count. Signals to customers that
         *       there ARE more products without overwhelming them.
         *     → click expands to fully open
         *   State C — fully open (.open class on .fx-eor-accordion)
         *     → all cards visible, current behaviour
         *     → click returns to collapsed
         *
         * Implementation: .is-preview reuses the same display:block as .open
         * for the body, then a :nth-child(n+X) rule hides cards beyond the
         * first row. X = column count + 1 (single→2, double→3, triple→4).
         *
         * Filter-chip toolbar and search row remain visible in preview
         * mode — only the .fx-eor-frontcard children inside .fx-eor-grid
         * are hidden. This keeps the controls discoverable so a customer
         * who likes what they see in the preview row can immediately use
         * Sort / View / Search without having to click expand first.
         * ──────────────────────────────────────────────────────────────── */
        .fx-eor-accordion.is-preview .fx-eor-acc-body { display: block; }
        /* Single-column preview: show only the first card */
        .fx-eor-accordion.is-preview .fx-eor-grid:not(.fx-eor-grid--double):not(.fx-eor-grid--triple) .fx-eor-frontcard:nth-child(n+2) {
            display: none;
        }
        /* Double-column preview: show only the first 2 cards */
        .fx-eor-accordion.is-preview .fx-eor-grid.fx-eor-grid--double .fx-eor-frontcard:nth-child(n+3) {
            display: none;
        }
        /* Triple-column preview: show only the first 3 cards */
        .fx-eor-accordion.is-preview .fx-eor-grid.fx-eor-grid--triple .fx-eor-frontcard:nth-child(n+4) {
            display: none;
        }
        @media (max-width: 719px) {
            /* Default / 1-col: preview shows 1 card. */
            .fx-eor-accordion.is-preview .fx-eor-grid .fx-eor-frontcard:nth-child(n+2) {
                display: none;
            }
            /* v4.25.69 — preview shows one full ROW for the chosen mobile column
             * count: 2-col → first 2 cards, 3-col → first 3 cards. */
            .fx-eor-accordion.is-preview .fx-eor-grid.fx-eor-mcols-2 .fx-eor-frontcard:nth-child(-n+2) { display: flex; }
            .fx-eor-accordion.is-preview .fx-eor-grid.fx-eor-mcols-2 .fx-eor-frontcard:nth-child(n+3) { display: none; }
        }
        /* v4.19.97 — In preview state, hide every basket-related CTA.
         *
         * James's screenshot showed customers confused by the inline
         * basket bar appearing in preview state: they thought the big
         * "ADD TO BASKET" button was the way to proceed and clicked it
         * without selecting anything, OR thought they had to "complete"
         * the basket before they could browse. Preview is a discovery
         * surface, not a transaction surface. Hide all basket UI until
         * the customer commits to fully-open view.
         *
         * Hidden in preview:
         *   .fx-eor-cta            — inline white basket bar with "Show basket" toggle + ADD TO BASKET btn
         *   .fx-eor-basket-bar     — fixed-position sticky bar at viewport bottom (sibling, body-level)
         *   .fx-eor-basket-modal   — popup viewer (hidden-by-default anyway, defensive)
         *
         * The card ticks themselves still respond (.fx-eor-check-col not
         * hidden) — they just don't surface a basket bar. State accrues
         * silently and the bars reveal as soon as the customer expands
         * to fully-open view. */
        .fx-eor-accordion.is-preview .fx-eor-cta {
            display: none !important;
        }
        /* Sticky bar + modal sit OUTSIDE the accordion in the DOM, so use
         * a body-level flag written by JS in sync with accordion state. */
        body.fx-eor-preview-active .fx-eor-basket-bar,
        body.fx-eor-preview-active .fx-eor-basket-modal {
            display: none !important;
        }

        /* When in preview, the bottom of the body shows a clickable
         * "Click here to Explore Our Range" button. This was a CSS-only
         * ::after pseudo-element in v4.19.95 — but pseudo-elements can't
         * be clicked, so the line looked like a link and did nothing,
         * which was its own confusion. v4.19.97 replaced it with a real
         * <button> injected via JS into the body — but the body is the
         * lazy-load AJAX target, so when cards arrived the body's
         * innerHTML was replaced and the button got wiped.
         *
         * v4.19.99 fix: render the button SERVER-SIDE as a SIBLING of
         * .fx-eor-acc-body (not inside it). The button survives lazy-
         * load because it's not inside the lazy-load target. CSS just
         * shows/hides based on the accordion's state class.
         *
         * Two button variants:
         *   .fx-eor-preview-expand-btn--inline  — server-rendered,
         *     persists across lazy-load. Default hidden, visible only
         *     when accordion has .is-preview class.
         *   (legacy) JS-injected button without the --inline modifier
         *     was used in v4.19.97; removed in v4.19.99 since the
         *     server-rendered button covers all states. */
        .fx-eor-accordion.is-preview .fx-eor-acc-body {
            position: relative;
        }
        .fx-eor-preview-expand-btn {
            display: block;
            width: 100%;
            text-align: center;
            font-size: 14px;
            color: #85C727;
            font-weight: 600;
            letter-spacing: 0.2px;
            margin-top: 14px;
            padding: 16px 0 10px;
            border: 0;
            border-top: 1px dashed #E0EBD4;
            background: transparent;
            text-transform: none;
            text-decoration: underline;
            text-decoration-thickness: 1.5px;
            text-underline-offset: 4px;
            cursor: pointer;
            font-family: 'Jost', sans-serif;
            transition: color 0.15s, text-decoration-color 0.15s;
        }
        .fx-eor-preview-expand-btn:hover {
            color: #2A6132;
            text-decoration-color: #2A6132;
            background: transparent;
        }
        .fx-eor-preview-expand-btn:focus-visible {
            outline: 2px solid #85C727;
            outline-offset: 2px;
            border-radius: 2px;
        }

        /* v4.19.99 — The server-rendered (--inline) button is hidden by
         * default. The accordion's .is-preview state class promotes it
         * to visible. When the accordion transitions to .open (full) or
         * back to closed, the button disappears via this rule, without
         * needing JS to add/remove the element. */
        .fx-eor-preview-expand-btn--inline {
            display: none;
        }
        .fx-eor-accordion.is-preview .fx-eor-preview-expand-btn--inline {
            display: block;
        }
        .fx-eor-intro { color: #1F3A24; font-size: 12px; margin-bottom: 16px; font-weight: 500; }
        /* v4.7.50: desktop grid is now 1 card per row, matching mobile.
         * Previously repeat(2, 1fr) meant each image was only 50% of a half-row
         * card — half the absolute width James wanted. Now image is 50% of the
         * FULL row width on desktop, matching mobile's measurement. */
        .fx-eor-grid { display: grid; grid-template-columns: 1fr; gap: 14px; }
        .fx-eor-grid { display: grid; grid-template-columns: 1fr; gap: 14px; align-items: stretch; }
        /* v4.16.2 — 2-column grid modifier. Only kicks in at >=720px viewport
         * so mobile stays single-column where 2-col would crush cards too
         * narrow for the 50/50 image-text internal layout. The cards themselves
         * keep their existing image-left text-right shape; with half the width,
         * each card is ~half as tall (image-driven height), so ~2.5x more
         * cards become visible in the same scroll area. */
        @media (min-width: 720px) {
            .fx-eor-grid--double { grid-template-columns: repeat(2, 1fr); gap: 10px; }
            /* v4.25.69 — narrower 2-col cards: shrink the title to fit so long
             * names (e.g. "ANNIVERSARY BOUQUET — CREAMS & PURPLES") stay whole-
             * worded. Words never break mid-character (word-break:normal on the
             * base h4). */
            .fx-eor-grid--double .fx-eor-body h4 { font-size: 14px; padding-right: 30px; }
            /* v4.19.0 — 3-col image-only grid (desktop only).
             * Cards become a third of the available width and the body
             * (name/pills/price/buy) is hidden — pure visual browsing.
             * Card-main becomes single-column so the image takes the full
             * card width. Click handlers on the image-link still route
             * to the product page. Mobile falls back to 1-col regardless. */
            .fx-eor-grid--triple { grid-template-columns: repeat(3, 1fr); gap: 8px; }
            .fx-eor-grid--triple .fx-eor-card-main { grid-template-columns: 1fr; }
            .fx-eor-grid--triple .fx-eor-body,
            .fx-eor-grid--triple .fx-eor-card-top { display: none; }
            /* Uniform square crop so the gallery feels tidy. */
            .fx-eor-grid--triple .fx-eor-img-link { aspect-ratio: 1 / 1; }
            .fx-eor-grid--triple .fx-eor-img-link img { width: 100%; height: 100%; object-fit: cover; }
            /* Hide check-col tick overlay and Add-to-Basket in 3-col —
             * pure image gallery, customer goes to product page for actions. */
            .fx-eor-grid--triple .fx-eor-check-col,
            .fx-eor-grid--triple .fx-eor-card-add { display: none; }
        }
        /* v4.19.18 — Hover-to-reveal row expansion for triple-column view.
         *
         * JAMES 2026-05-16: "when you hover over these cards in this triple
         * column view the variations pop up on hover ... they could expand
         * downwards below the images for all three cards on that row".
         *
         * Implementation:
         *  - Only kicks in for hover-capable devices (@media hover: hover)
         *  - Touch devices keep the gallery-only behavior above
         *  - Override the display:none rules with a collapsed state
         *    (max-height: 0 / opacity: 0 / overflow: hidden) so we can
         *    animate to an expanded state
         *  - JS adds class fx-eor-row-hover to ALL cards in the same row
         *    when one is hovered, so the variation/qty/add UI appears for
         *    every sibling in the row simultaneously
         *  - This avoids jagged layout (rows expand uniformly, not just
         *    the hovered card)
         */
        @media (hover: hover) and (pointer: fine) {
            .fx-eor-grid--triple .fx-eor-body,
            .fx-eor-grid--triple .fx-eor-card-top,
            .fx-eor-grid--triple .fx-eor-card-add {
                display: block;
                max-height: 0;
                opacity: 0;
                overflow: hidden;
                padding-top: 0;
                padding-bottom: 0;
                margin: 0;
                transition: max-height 0.22s ease-out, opacity 0.16s ease-out, padding 0.22s ease-out;
            }
            .fx-eor-grid--triple .fx-eor-check-col {
                display: flex;
                max-width: 0;
                opacity: 0;
                overflow: hidden;
                transition: max-width 0.22s ease-out, opacity 0.16s ease-out;
            }
            /* Expanded state — applied by JS to all cards in the hovered row. */
            .fx-eor-grid--triple .fx-eor-frontcard.fx-eor-row-hover .fx-eor-body {
                max-height: 280px;
                opacity: 1;
                padding-top: 8px;
                padding-bottom: 8px;
            }
            .fx-eor-grid--triple .fx-eor-frontcard.fx-eor-row-hover .fx-eor-card-top {
                max-height: 60px;
                opacity: 1;
            }
            .fx-eor-grid--triple .fx-eor-frontcard.fx-eor-row-hover .fx-eor-card-add {
                max-height: 80px;
                opacity: 1;
                padding-bottom: 8px;
            }
            .fx-eor-grid--triple .fx-eor-frontcard.fx-eor-row-hover .fx-eor-check-col {
                max-width: 50px;
                opacity: 1;
            }
            /* Subtle hover ring on the hovered row so the visual grouping is clear. */
            .fx-eor-grid--triple .fx-eor-frontcard.fx-eor-row-hover {
                box-shadow: 0 2px 12px rgba(31, 58, 36, 0.08);
                background: #fafff8;
                transition: box-shadow 0.18s ease, background 0.18s ease;
            }
        }
        /* ═══════════════════════════════════════════════════════════════
         * v4.20.21 — Per-view-mode polish on the Siblings/EOR product
         * cards. James 2026-05-19 (3 screenshots: 1-col/2-col/3-col):
         *
         *   1-col view → fine, no changes.
         *
         *   2-col view → tick checkbox currently at top-right of card
         *   encroaches on the title. Move to bottom-LEFT corner. Add a
         *   small explanatory note ("Tick to add to order") next to it
         *   so the multi-select UI is clearer to customers (we have no
         *   data yet on whether tick+button combo confuses; this is
         *   defensive copy).
         *
         *   3-col view → three changes:
         *     (a) Hide the "★ Currently viewing" top strip in 3-col —
         *         redundant with the lower badge that the body::before
         *         pseudo at line ~10614 already renders. James: "well
         *         located where it is lower down".
         *     (b) Pin Add-to-Basket to the bottom of the card with a
         *         2px gap from the frame. Was margin-top:10px in the
         *         flow; now margin:auto 0 2px 0 so flexbox pushes it
         *         to the bottom regardless of how many variation pills
         *         the body contains.
         *     (c) Slide-up animation replaces v4.19.18's max-height
         *         expand-below pattern. Card stays its original size;
         *         on hover the image translates UP and out of frame
         *         while the body slides UP into view from below. This
         *         keeps card heights uniform across the row and gives
         *         the "image rolls out the top of the box" effect.
         *
         * Implementation note: v4.19.18 rules (lines 9920-9966) used
         * `display: block; max-height: 0; opacity: 0` for the body and
         * card-add, then expanded max-height/opacity on .fx-eor-row-
         * hover. I replace that mechanism here for 3-col only — body
         * becomes position:absolute filling the card-main viewport,
         * translateY(100%) initially, translateY(0) on row-hover.
         * Image follows the inverse — translateY(0) → translateY(-100%).
         * Both transitions are 0.35s for a smooth slide-card feel.
         * The earlier max-height/opacity rules become no-ops because
         * mine have higher source-order priority at equal specificity
         * (0,2,0 vs 0,2,0 → last in source wins → mine win).
         * ─────────────────────────────────────────────────────────── */

        /* ─── 2-COL VIEW: tick moves to bottom-left + explanatory note
         *
         * v4.20.24 — DISABLED. James 2026-05-19: "we are going to remove
         * the tick box entirely and the pill instruction at the top of
         * the column and the little nudges". The multi-select pattern is
         * being retired in favour of single-add-to-basket → mini-basket
         * flow. Rules below kept as a "tombstone" so they're visible in
         * the cascade history; the v4.20.24 block at the end of this
         * file hides the underlying elements (tick + cue + note) with
         * display:none !important regardless of view mode. */
        @media (min-width: 720px) {
            .fx-eor-grid--double .fx-eor-check-col {
                top: auto;
                right: auto;
                bottom: 10px;
                left: 10px;
            }
        }

        /* ─── 3-COL VIEW: v4.20.21 slide-up — STRIPPED in v4.20.43
         *
         * Original v4.20.21 attempt repositioned .fx-eor-body to
         * position:absolute, translateY(100%), with the image sliding
         * up out of frame on .fx-eor-row-hover. James rejected the
         * pattern outright after testing: "it needs to scroll slowly
         * with the mouse and not jump back if the user moves out of
         * the box" → bumped to 1.5s → "auto scroll is useless from
         * too fast to slow uncontrollable" → final verdict 2026-05-19:
         * "you invented rocket scrolling we had scrolling before —
         * find out where that code went".
         *
         * v4.20.43 restores the v4.19.18 max-height expand-down (still
         * intact at lines 9920–9966 above). The trigger is rewired
         * from .fx-eor-row-hover (JS-added row-wide) to per-card
         * :hover and .fx-eor-in-cart (lock-open) — see the v4.20.43
         * block ~150 lines below.
         * ─────────────────────────────────────────────────────────── */

        /* ═══════════════════════════════════════════════════════════════
         * v4.20.24 — RETIRE THE MULTI-SELECT UI
         *
         * James 2026-05-19 (this thread): "we are going to remove the
         * tick box entirely and the pill instruction at the top of the
         * column and the little nudges. WHAT WE ARE GOING TO DO IS THIS
         * WE ARE GOING TO KEEP ADD TO BASKET. WHEN THE USER CLICKS IT
         * THE PRODUCT GOES INTO THE BASKET THE ADD TO BASKET BUTTON
         * CHANGED TO ADDED. THE USER STAYS ON THE PAGE AND AT THAT POINT
         * THE MINI BASKET APPEARS AND THE USER CAN CONTINUE SHOPPING."
         *
         * Strategy: HIDE the underlying elements via CSS, don't rip out
         * the HTML or the JS that powers them. Two reasons:
         *
         *   (1) Reversibility — if user testing shows the tick model was
         *       actually working for some customer cohort and only
         *       confusing others, we can re-enable per-segment / per-
         *       category by removing the display:none rules. The whole
         *       tick subsystem (selected[] state, .selected class, qty
         *       sync, bulk-add AJAX) stays intact in the DOM.
         *
         *   (2) Bulb support — the user said this functionality may
         *       come back when bulb categories are added. Hiding the UI
         *       leaves the working multi-select code in place ready
         *       for a per-category re-enable.
         *
         * Hidden elements:
         *   - .fx-eor-check-col       — the tick checkbox on each card
         *   - .fx-eor-tick-cue        — top-of-accordion "Tick the boxes
         *                                 to add multiple varieties" pill
         *   - .fx-eor-cta-summary     — "0 items selected · £0.00" line
         *                                 (mini-basket subtotal stays
         *                                 inside the panel; this top-line
         *                                 summary was tick-state)
         *   - The 2-col bottom-left "TICK TO MULTI-SELECT" pseudo note
         *     (the v4.20.21 ::after rule was already removed in this
         *     version's earlier edit)
         *
         * The mini-basket panel itself stays — it's the new primary
         * cart-state surface. Already configured to be expanded by
         * default (v4.19.44), so when the customer clicks Add to Basket
         * the new item appears in it immediately.
         * ─────────────────────────────────────────────────────────── */
        .fx-eor-check-col,
        .fx-eor-tick-cue,
        .fx-eor-cta-summary {
            display: none !important;
        }

        /* v4.20.24 — Sticky "✓ ADDED" button state. The .is-added class
         * is added by addSingleCard() on successful AJAX add (see
         * function at line ~13963). Visual treatment: sage tone
         * (#5C8F4F → flat green) instead of the bright #85C727 primary,
         * white text instead of dark, so the button reads as a "done"
         * confirmation rather than a "primary action waiting to be
         * clicked". Button stays clickable so a customer can add
         * another unit if they want (each click = +1 to cart). */
        .fx-eor-card-add.is-added {
            background: #5C8F4F !important;
            color: #fff !important;
            cursor: pointer;
        }
        .fx-eor-card-add.is-added:hover {
            background: #4F7F44 !important;
        }
        .fx-eor-card-add.is-added:active {
            background: #406932 !important;
        }
        /* End v4.20.24 retire-multi-select block ─────────────────────── */

        /* ═══════════════════════════════════════════════════════════════
         * v4.20.43 — RESTORE THE v4.19.18 EXPAND-DOWN PATTERN
         *
         * James 2026-05-19 (this thread, after rejecting v4.20.21
         * through v4.20.27 in succession): "YOU INVENTED ROCKET
         * SCROLLING WE HAD SCROLLING BEFORE — FIND OUT WHERE THAT
         * CODE WENT".
         *
         * The original v4.19.18 max-height expand-down code is still
         * in place at lines 9920–9966. This block REWIRES it for the
         * per-card spec James approved in-thread (v9 of the inline
         * demo):
         *
         *   1. Default state    → body collapsed (max-height:0),
         *                         image visible above.
         *   2. Per-card hover   → body expands DOWNWARD over 1.5s.
         *                         Only the hovered card opens —
         *                         siblings stay closed. Image stays
         *                         put. No translation, no transform,
         *                         no slide.
         *   3. After Add to     → card gets .fx-eor-in-cart, body
         *      Basket             stays expanded indefinitely (lock-
         *                         open). Image blurs (filter:blur
         *                         2.5px + saturate 0.85) as a basket-
         *                         state cue. Pills and qty stepper
         *                         remain interactive so the customer
         *                         can change variation / qty without
         *                         needing to re-hover.
         *   4. Remove from      → .fx-eor-in-cart removed, body
         *      basket             returns to default hover-only
         *                         behaviour, image un-blurs.
         *
         * Trigger replaces v4.19.18's .fx-eor-row-hover class (added
         * by setupTripleHover JS, row-wide reveal) with CSS-only
         * :hover on each card individually. setupTripleHover stays
         * neutered (return; at line ~15670) — pure CSS does it all
         * now. No JS row-shuffling, no grace timers, no auto-trigger.
         *
         * Currently Viewing pill: rendered via .fx-eor-body::before
         * (line ~11011) — appears at the top of the expanded body for
         * data-current="1" cards. Visible whenever the body is open
         * (hover OR .fx-eor-in-cart lock-open).
         * ─────────────────────────────────────────────────────────── */
        @media (min-width: 720px) and (hover: hover) and (pointer: fine) {
            /* v4.20.43 — Override the global .fx-eor-grid { align-items:
             * stretch; } at line 9875 for 3-col view. Stretch makes every
             * cell in a row grow to match the tallest cell's height. With
             * per-card hover/in-cart expand, the OTHER cards in the row
             * were stretching with empty white space below their images
             * to match the expanded card's height. align-items: start
             * lets each card size to its own content. James reported
             * "stop the other cells in the column expanding at the same
             * time" 2026-05-19 — this is the wiring. */
            .fx-eor-grid--triple {
                align-items: start;
            }

            /* Override v4.19.18 collapsed-state transitions (0.22s)
             * with the demo-approved 1.5s ease. Applies to both
             * reveal (collapse → expand) and revert (expand → collapse)
             * because CSS transitions are defined on the base state. */
            .fx-eor-grid--triple .fx-eor-body,
            .fx-eor-grid--triple .fx-eor-card-top,
            .fx-eor-grid--triple .fx-eor-card-add {
                transition: max-height 1.5s ease, opacity 1.2s ease, padding 1.5s ease !important;
            }
            .fx-eor-grid--triple .fx-eor-check-col {
                transition: max-width 1.5s ease, opacity 1.2s ease !important;
            }

            /* Per-card hover expand-down. Same expanded-state CSS
             * also applies to .fx-eor-in-cart so cards stay locked
             * open once the customer has added them to the basket. */
            .fx-eor-grid--triple .fx-eor-frontcard:hover .fx-eor-body,
            .fx-eor-grid--triple .fx-eor-frontcard.fx-eor-in-cart .fx-eor-body {
                max-height: 360px;
                opacity: 1;
                padding-top: 10px;
                padding-bottom: 12px;
            }
            .fx-eor-grid--triple .fx-eor-frontcard:hover .fx-eor-card-add,
            .fx-eor-grid--triple .fx-eor-frontcard.fx-eor-in-cart .fx-eor-card-add {
                max-height: 100px;
                opacity: 1;
                /* v4.20.43 — Taller button with proper top padding. James
                 * (2026-05-19): "I want the nice padding on the button
                 * just a few more pixels of button on the top of it".
                 * padding-top makes the button itself taller (not just
                 * space above it). margin-top kept small for slight
                 * breathing room from the price row above. */
                padding-top: 14px;
                padding-bottom: 12px;
                margin-top: 8px;
            }
            /* v4.20.24 retired the multi-select tick via display:none
             * on .fx-eor-check-col. The expanded-state max-width
             * rule below is kept for symmetry in case future bulb
             * categories re-enable the tick UI per-segment. */
            .fx-eor-grid--triple .fx-eor-frontcard:hover .fx-eor-check-col,
            .fx-eor-grid--triple .fx-eor-frontcard.fx-eor-in-cart .fx-eor-check-col {
                max-width: 50px;
                opacity: 1;
            }

            /* Subtle hover / in-cart hint — same styling as v4.19.18
             * row-hover but now per-card. */
            .fx-eor-grid--triple .fx-eor-frontcard:hover,
            .fx-eor-grid--triple .fx-eor-frontcard.fx-eor-in-cart {
                box-shadow: 0 2px 12px rgba(31, 58, 36, 0.08);
                background: #fafff8;
                transition: box-shadow 0.18s ease, background 0.18s ease;
            }

            /* Image blur on cards that are in the basket — visual cue
             * the product has already been added. Demo-approved
             * values: 2.5px blur + 0.85 saturation. Filter applies to
             * the <img> inside .fx-eor-img-link so the link wrapper
             * stays sharp (badges / overlays not affected). */
            .fx-eor-grid--triple .fx-eor-frontcard.fx-eor-in-cart .fx-eor-img-link img {
                filter: blur(2.5px) saturate(0.85);
                transition: filter 0.35s ease;
            }

            /* The .fx-eor-card-top top-strip stays hidden in 3-col.
             * Currently Viewing is handled by .fx-eor-body::before
             * on data-current="1" cards (line ~11011). */
            .fx-eor-grid--triple .fx-eor-card-top {
                display: none !important;
            }

            /* v0.2.55 — THE IN-BASKET NOTIFICATION IS RETIRED (the owner,
             * 16 Aug: "the blur is because they were in my basket — remove
             * the blur feature entirely, I don't want that notification
             * feature on the plugin"). The Guardian's v4.20.43 rules above
             * kept an in-basket card LOCKED OPEN in the three-column view
             * (body expanded, Add shown, image blurred + desaturated) as a
             * "you already added this" cue. That cue is gone: in-basket
             * cards now look and behave exactly like every other card —
             * hover still expands (the browsing gesture), nothing is locked
             * open, nothing is blurred. R-NOCHOP: the ported rules stay in
             * place above; these overrides win by specificity + order. The
             * .fx-eor-in-cart class itself is still applied (the qty sync
             * and basket bar read it) — only its LOOK is retired. */
            .fx-eor-grid--triple .fx-eor-frontcard.fx-eor-in-cart:not(:hover) .fx-eor-body {
                max-height: 0 !important; opacity: 0 !important; padding-top: 0 !important; padding-bottom: 0 !important;
            }
            .fx-eor-grid--triple .fx-eor-frontcard.fx-eor-in-cart:not(:hover) .fx-eor-card-add {
                max-height: 0 !important; opacity: 0 !important; padding-top: 0 !important; padding-bottom: 0 !important; margin-top: 0 !important;
            }
            .fx-eor-grid--triple .fx-eor-frontcard.fx-eor-in-cart:not(:hover) .fx-eor-check-col {
                max-width: 0 !important; opacity: 0 !important;
            }
            .fx-eor-grid--triple .fx-eor-frontcard.fx-eor-in-cart:not(:hover) {
                box-shadow: none !important; background: transparent !important;
            }
            .fx-eor-grid--triple .fx-eor-frontcard.fx-eor-in-cart .fx-eor-img-link img {
                filter: none !important;
            }
        }
        /* End v4.20.43 expand-down restoration block ─────────────────── */

        /* v4.15.0 — Filter chip row above the Siblings grid.
         * v4.16.1 — Was changed to horizontal scroll.
         * v4.19.0 — REPLACED horizontal scroll with wrap (per James).
         * Chips wrap onto multiple rows naturally. No scroll, no clipping,
         * no More/Less toggle. Customer sees every category at a glance.
         * Simple, no JS measurement, just flex-wrap. */
        .fx-eor-chiprow {
            display: flex; gap: 8px;
            flex-wrap: wrap;
            margin: 0 0 14px;
            font-family: 'Jost', system-ui, sans-serif;
        }
        .fx-eor-chip {
            flex: 0 0 auto;
            display: inline-flex; align-items: center; gap: 4px;
            padding: 6px 14px; background: #FAFAF8;
            border: 1.5px solid #D8E8D0; border-radius: 999px;
            color: #1F3A24; font-size: 13px; line-height: 1;
            cursor: pointer; font-family: inherit;
            transition: background 0.12s, border-color 0.12s, color 0.12s;
        }
        .fx-eor-chip:hover { background: #E8F7D5; border-color: #85C727; }
        .fx-eor-chip.is-active {
            background: #2A6132; border-color: #2A6132; color: #fff;
            font-weight: 500;
        }
        .fx-eor-chip-count { font-size: 11px; opacity: 0.7; }
        .fx-eor-chip.is-active .fx-eor-chip-count { opacity: 0.9; }
        .fx-eor-frontcard.is-filtered-out,
        .fx-eor-frontcard.is-search-hidden { display: none; }
        @media (max-width: 600px) {
            .fx-eor-chiprow { gap: 6px; }
            .fx-eor-chip { padding: 5px 11px; font-size: 12px; }
        }
        /* ─────────────────────────────────────────────────────────────────
         * v4.16.0 — Customer toolbar (search + sort) above the chip row.
         * Search filters by product name as the customer types (debounced
         * 120ms in JS). Sort reorders DOM cards; "Featured" restores admin
         * order via data-default-order. ───────────────────────────────── */
        .fx-eor-toolbar {
            display: flex; justify-content: space-between; align-items: center;
            flex-wrap: wrap; gap: 12px; margin: 0 0 12px;
            font-family: 'Jost', system-ui, sans-serif;
        }
        .fx-eor-search-wrap {
            position: relative; flex: 1 1 220px; min-width: 180px; max-width: 360px;
        }
        .fx-eor-search {
            width: 100%; padding: 8px 30px 8px 12px;
            border: 1.5px solid #D8E8D0; border-radius: 999px;
            background: #FAFAF8; color: #1F3A24;
            font-family: inherit; font-size: 13px; line-height: 1.2;
            transition: border-color 0.12s, background 0.12s;
        }
        .fx-eor-search::placeholder { color: #8aa291; }
        .fx-eor-search:focus {
            outline: none; border-color: #85C727; background: #fff;
        }
        .fx-eor-search-clear {
            position: absolute; right: 10px; top: 50%;
            transform: translateY(-50%);
            color: #2A6132; font-size: 18px; line-height: 1;
            cursor: pointer; padding: 2px 6px; border-radius: 50%;
        }
        .fx-eor-search-clear:hover { background: #E8F7D5; }
        .fx-eor-sort-wrap {
            display: flex; align-items: center; gap: 6px;
            font-family: inherit;
        }
        /* v4.18.4 — Sort + View live inside this single flex group so they
         * wrap as a unit, not independently. Gap between the two sub-clusters
         * matches the toolbar gap. */
        .fx-eor-sortview-group {
            display: flex; align-items: center;
            flex-wrap: wrap; gap: 6px 12px;
        }
        .fx-eor-sort-label {
            font-size: 12px; color: #5a7261; font-weight: 500;
            margin-right: 2px;
        }
        .fx-eor-sort-btn {
            background: #FAFAF8; border: 1.5px solid #D8E8D0;
            color: #1F3A24; padding: 6px 12px; border-radius: 999px;
            font-family: inherit; font-size: 12px; line-height: 1;
            cursor: pointer;
            transition: background 0.12s, border-color 0.12s, color 0.12s;
        }
        .fx-eor-sort-btn:hover { background: #E8F7D5; border-color: #85C727; }
        .fx-eor-sort-btn.is-active {
            background: #2A6132; border-color: #2A6132; color: #fff; font-weight: 500;
        }
        /* v4.18.2 — View (column) toggle. Two icon-only buttons mirroring the
         * Sort cluster's pill style. The icons are pure CSS bars (1 wide bar /
         * 2 narrow bars) so no font or SVG dependency. Hidden on narrow
         * viewports because the 2-col grid only activates at >=720px anyway.
         *
         * v4.18.4 — Visual fix: buttons now match Sort button height/padding
         * exactly (padding:6px 12px, no min-width override) so the cluster
         * looks cohesive instead of orphaned. Icons enlarged from 12px to
         * 16px and redrawn with the recognisable list-rows / 2x2-grid
         * metaphors so they read clearly at desktop viewing distance. */
        .fx-eor-view-wrap {
            display: flex; align-items: center; gap: 6px;
            font-family: inherit;
        }
        /* v4.25.70 — mobile column selector: hidden on desktop, shown ≤700px. */
        .fx-eor-mview-wrap { display: none; }
        .fx-eor-view-btn {
            background: #FAFAF8; border: 1.5px solid #D8E8D0;
            color: #1F3A24; padding: 6px 12px; border-radius: 999px;
            font-family: inherit; line-height: 1; cursor: pointer;
            display: inline-flex; align-items: center; justify-content: center;
            transition: background 0.12s, border-color 0.12s;
            height: 28px;
            box-sizing: border-box;
        }
        .fx-eor-view-btn:hover { background: #E8F7D5; border-color: #85C727; }
        .fx-eor-view-btn.is-active { background: #2A6132; border-color: #2A6132; }
        .fx-eor-view-icon {
            display: inline-block; position: relative;
            width: 16px; height: 12px;
        }
        /* 1-col icon: three stacked horizontal rows — universal "list view"
         * metaphor. Three rows render as solid dark-green bars; turn white on
         * the active button. */
        .fx-eor-view-icon--1col::before,
        .fx-eor-view-icon--1col {
            background: transparent;
        }
        .fx-eor-view-icon--1col::before {
            content: '';
            position: absolute; inset: 0;
            background-image: linear-gradient(
                to bottom,
                #1F3A24 0, #1F3A24 2px,
                transparent 2px, transparent 5px,
                #1F3A24 5px, #1F3A24 7px,
                transparent 7px, transparent 10px,
                #1F3A24 10px, #1F3A24 12px
            );
            border-radius: 1px;
        }
        .fx-eor-view-btn.is-active .fx-eor-view-icon--1col::before {
            background-image: linear-gradient(
                to bottom,
                #fff 0, #fff 2px,
                transparent 2px, transparent 5px,
                #fff 5px, #fff 7px,
                transparent 7px, transparent 10px,
                #fff 10px, #fff 12px
            );
        }
        /* 2-col icon: 2x2 grid of small filled squares — universal "grid view"
         * metaphor. Stronger visual presence than the two-bars approach. */
        .fx-eor-view-icon--2col::before,
        .fx-eor-view-icon--2col::after {
            content: '';
            position: absolute;
            width: 6px; height: 5px;
            background: #1F3A24;
            border-radius: 1px;
        }
        .fx-eor-view-icon--2col::before {
            top: 0; left: 0;
            box-shadow:
                10px 0   0 0 #1F3A24,
                0    7px 0 0 #1F3A24,
                10px 7px 0 0 #1F3A24;
        }
        .fx-eor-view-icon--2col::after { display: none; }
        .fx-eor-view-btn.is-active .fx-eor-view-icon--2col::before {
            background: #fff;
            box-shadow:
                10px 0   0 0 #fff,
                0    7px 0 0 #fff,
                10px 7px 0 0 #fff;
        }
        /* v4.19.0 — 3-col icon: 3 small filled squares in two rows (3x2 grid).
         * Visually denser than the 2x2 of the 2-col icon so the difference
         * reads immediately. */
        .fx-eor-view-icon--3col::before,
        .fx-eor-view-icon--3col::after {
            content: '';
            position: absolute;
            width: 4px; height: 5px;
            background: #1F3A24;
            border-radius: 1px;
        }
        .fx-eor-view-icon--3col::before {
            top: 0; left: 0;
            box-shadow:
                6px  0   0 0 #1F3A24,
                12px 0   0 0 #1F3A24,
                0    7px 0 0 #1F3A24,
                6px  7px 0 0 #1F3A24,
                12px 7px 0 0 #1F3A24;
        }
        .fx-eor-view-icon--3col::after { display: none; }
        .fx-eor-view-btn.is-active .fx-eor-view-icon--3col::before {
            background: #fff;
            box-shadow:
                6px  0   0 0 #fff,
                12px 0   0 0 #fff,
                0    7px 0 0 #fff,
                6px  7px 0 0 #fff,
                12px 7px 0 0 #fff;
        }
        @media (max-width: 520px) {
            /* Hide on narrow viewports — 2-col gate is at 720px so toggle is
             * meaningless here. Keeps the toolbar from feeling crowded. */
            .fx-eor-view-wrap { display: none; }
        }
        /* v4.19.1 — Skeleton loading state for the AJAX-loaded Siblings body.
         * Shown briefly between initial paint and AJAX response. Subtle
         * pulse animation gives the customer a visual cue that content is
         * coming. Once AJAX returns, the whole skeleton is replaced
         * wholesale with the real body content. */
        @keyframes fx-eor-skel-pulse {
            0%, 100% { opacity: 0.55; }
            50%      { opacity: 1; }
        }
        .fx-eor-skeleton {
            animation: fx-eor-skel-pulse 1.4s ease-in-out infinite;
            display: flex; flex-direction: column; gap: 12px;
        }
        .fx-eor-skel-toolbar {
            height: 36px; background: #D8E8D0; border-radius: 6px;
        }
        .fx-eor-skel-chiprow {
            display: flex; flex-wrap: wrap; gap: 6px;
        }
        .fx-eor-skel-chip {
            display: inline-block;
            width: 80px; height: 26px;
            background: #D8E8D0; border-radius: 999px;
        }
        .fx-eor-skel-chip:nth-child(2) { width: 110px; }
        .fx-eor-skel-chip:nth-child(3) { width: 70px; }
        .fx-eor-skel-chip:nth-child(4) { width: 95px; }
        .fx-eor-skel-card {
            height: 220px; background: #D8E8D0; border-radius: 5px;
            border: 1px solid #C8DEC0;
        }
        @media (max-width: 600px) {
            .fx-eor-skel-card { height: 180px; }
        }
        .fx-eor-no-matches {
            padding: 18px; text-align: center; color: #5a7261;
            font-size: 13px; font-family: 'Jost', system-ui, sans-serif;
            background: #FAFAF8; border: 1px dashed #D8E8D0; border-radius: 6px;
            margin-bottom: 12px;
        }
        .fx-eor-reset-link {
            background: none; border: none; color: #2A6132;
            text-decoration: underline; cursor: pointer; font-family: inherit;
            font-size: 13px; padding: 0;
        }
        .fx-eor-reset-link:hover { color: #1F3A24; }
        /* ─────────────────────────────────────────────────────────────────
         * v4.16.0 — Out-of-stock card styling. Cards stay visible but with
         * faded image + "Sold out" badge overlay + disabled buy controls.
         * Image link remains clickable (sends customer to product page).
         * Notify-me UI lands in v4.16.1. ──────────────────────────────── */
        .fx-eor-frontcard--oos .fx-eor-img { opacity: 0.55; filter: grayscale(0.4); }
        .fx-eor-frontcard--oos .fx-eor-img-link {
            position: relative; display: block;
        }
        .fx-eor-frontcard--oos .fx-eor-img-link::after {
            content: "Sold out";
            position: absolute; top: 10px; right: 10px;
            background: #1F3A24; color: #fff;
            font-family: 'Jost', system-ui, sans-serif;
            font-size: 11px; font-weight: 600; letter-spacing: 0.6px;
            text-transform: uppercase;
            padding: 5px 10px; border-radius: 3px;
            pointer-events: none; z-index: 2;
            box-shadow: 0 1px 3px rgba(0,0,0,0.2);
        }
        .fx-eor-frontcard--oos .fx-eor-check-col,
        .fx-eor-frontcard--oos .fx-eor-variations,
        .fx-eor-frontcard--oos .fx-eor-qty,
        .fx-eor-frontcard--oos .fx-eor-card-add {
            opacity: 0.4; pointer-events: none; filter: grayscale(1);
        }
        .fx-eor-frontcard--oos .fx-eor-name-link,
        .fx-eor-frontcard--oos .fx-eor-price { opacity: 0.7; }
        .fx-eor-frontcard--oos .fx-eor-card-add {
            background: #b8b8b8 !important; cursor: not-allowed;
        }
        /* v4.18.3 — notify-me UI on OOS cards. Replaces the greyed-out Add to
         * Basket button. Three states managed inline:
         *   1. Default: "✉ Notify me when back" trigger button (full width).
         *   2. After click: trigger hides, email input + Submit button show.
         *   3. After successful submit: form hides, success message shows.
         * Critical: this wrap sits INSIDE .fx-eor-frontcard--oos, so it
         * inherits pointer-events:none from earlier rules — override to
         * pointer-events:auto so clicks register. */
        .fx-eor-frontcard--oos .fx-eor-notify-wrap {
            opacity: 1;
            filter: none;
            pointer-events: auto;
            margin-top: 4px;
        }
        .fx-eor-notify-wrap { font-family: 'Jost', sans-serif; }
        .fx-eor-notify-trigger {
            display: block;
            width: 100%;
            background: #2D6A4F;
            color: #fff;
            border: 0;
            padding: 9px 12px;
            font-size: 12px;
            font-weight: 500;
            font-family: inherit;
            letter-spacing: 0.3px;
            border-radius: 3px;
            cursor: pointer;
            text-align: center;
            transition: background 0.12s;
        }
        .fx-eor-notify-trigger:hover { background: #1F3A24; }
        .fx-eor-notify-form {
            display: flex;
            gap: 4px;
            align-items: stretch;
        }
        .fx-eor-notify-email {
            flex: 1 1 auto;
            min-width: 0;
            padding: 7px 9px;
            border: 1px solid #2D6A4F;
            border-radius: 3px;
            font-size: 12px;
            font-family: inherit;
            color: #1F3A24;
            background: #fff;
        }
        .fx-eor-notify-email:focus { outline: none; border-color: #85C727; box-shadow: 0 0 0 2px rgba(133,199,39,0.15); }
        .fx-eor-notify-submit {
            background: #2D6A4F;
            color: #fff;
            border: 0;
            padding: 7px 14px;
            font-size: 12px;
            font-weight: 500;
            font-family: inherit;
            border-radius: 3px;
            cursor: pointer;
            white-space: nowrap;
        }
        .fx-eor-notify-submit:hover { background: #1F3A24; }
        .fx-eor-notify-submit:disabled { background: #888; cursor: wait; }
        .fx-eor-notify-success {
            padding: 8px 10px;
            background: #E8F7D5;
            border: 1px solid #85C727;
            color: #2D6A4F;
            border-radius: 3px;
            font-size: 11px;
            font-weight: 500;
            text-align: center;
            line-height: 1.3;
        }
        .fx-eor-notify-error {
            padding: 6px 10px;
            background: #FBE9E9;
            border: 1px solid #d99c9c;
            color: #b03030;
            border-radius: 3px;
            font-size: 11px;
            text-align: center;
            line-height: 1.3;
        }
        @media (max-width: 600px) {
            .fx-eor-toolbar { gap: 8px; }
            .fx-eor-search-wrap { flex: 1 1 100%; max-width: none; }
            .fx-eor-sort-btn { padding: 5px 10px; font-size: 11px; }
            .fx-eor-frontcard--oos .fx-eor-img-link::after {
                top: 6px; right: 6px; font-size: 10px; padding: 4px 8px;
            }
        }
        /* v4.7.38: Hopewiser-style internal scroll viewport. Caps visible
         * cards at 6 (3 rows × 2 cols on desktop) — beyond that the user
         * scrolls within the section rather than the page. Keeps EOR
         * visually compact while preserving access to all cards. Same
         * mechanism on mobile, smaller viewport (3 cards visible).
         *
         * max-height naturally collapses when fewer than 6 cards present —
         * the container shrinks to fit content with no scrollbar shown. */
        .fx-eor-grid {
            max-height: 920px;
            overflow-y: auto;
            padding-right: 6px;
            scrollbar-width: thin;
            scrollbar-color: #85C727 #F0F4E8;
        }
        .fx-eor-grid::-webkit-scrollbar { width: 8px; }
        .fx-eor-grid::-webkit-scrollbar-track { background: #F0F4E8; border-radius: 4px; }
        .fx-eor-grid::-webkit-scrollbar-thumb { background: #85C727; border-radius: 4px; }
        .fx-eor-grid::-webkit-scrollbar-thumb:hover { background: #2A6132; }
        /* v4.7.22: card is now a vertical flex container — top strip above, main row below.
         * Both cards in a grid row stretch to equal height. The top strip is present on
         * every card (same height) so cards align consistently at top and bottom. */
        /* v4.7.30: Cards mirror the billing panel structure — white body
         * (like the address field), green border (like the field border),
         * sitting inside a cream accordion (like the panel container).
         * Visual hierarchy: page mint → cream accordion → white cards.
         * v4.7.32: card border now matches address field border colour (sage). */
        /* v4.19.42 — Was `transition: all 0.15s`. James reported tick
         * "is not immediate, takes about 2 seconds" — v4.19.41 had
         * already pulled transitions off .fx-eor-check-col and
         * .fx-eor-check, but the parent card was STILL transitioning
         * border-color, background, and box-shadow over 150ms on
         * every .selected toggle. That's the green border + sage
         * fill the user sees ease in/out — visually it reads as
         * the tick "settling" slowly even though the tick glyph
         * itself flipped instantly.
         *
         * v4.19.43 — Further narrowing. v4.19.42 had `transition:
         * transform 0.15s, box-shadow 0.15s` to keep the hover
         * lift's box-shadow animating. BUT `.fx-eor-frontcard--current.selected`
         * adds `box-shadow: 0 0 0 2px #2A6132 inset` — a 2px green
         * inset that ALSO transitioned over 150ms because
         * box-shadow was in the transition list. That's the
         * remaining "tick settles slowly" feel on the current
         * product card.
         *
         * Drop box-shadow from the transition. Now ONLY transform
         * animates — the :hover lift (translateY(-1px)) still feels
         * smooth, and the hover box-shadow appears instantly (a
         * minor regression that's barely visible). The .selected
         * 2px inset flips instantly. The tick is finally instant
         * on the current product card. */
        .fx-eor-frontcard { background: #fff; border: 1px solid #85C727; border-radius: 5px; display: flex; flex-direction: column; overflow: hidden; cursor: pointer; transition: transform 0.15s; position: relative; }
        .fx-eor-frontcard:hover { border-color: #2A6132; transform: translateY(-1px); box-shadow: 0 4px 12px -6px rgba(31, 58, 36, 0.15); }
        .fx-eor-frontcard.selected { border-color: #2A6132; background: #F8FCF0; box-shadow: 0 0 0 1px #2A6132 inset; }

        /* ═══════════════════════════════════════════════════════════════
           v4.19.23 — "In Basket" indicator.

           When a product is already in the WC cart, mark its EOR card and
           slider card with a discreet "✓ In basket" badge in the top-right
           corner + a subtle sage tint. So if customer has already added a
           product on a previous page, when they navigate to a NEW product
           page where that product appears as a sibling, they immediately
           see it's already in their basket and don't accidentally re-add.

           Independent of the .selected state (which is the EOR tick/staging
           system). A card can be both — selected (about to add more) AND
           in-basket (already has one).

           v4.19.34 — Removed the "✓ IN BASKET" pill. James: "I think the
           pill is over engineered and all we need is the tick". Cards that
           are in the WC cart now simply show the same ✓ that selected
           cards show, via a CSS rule that opacity-1's the .fx-eor-check
           ::after pseudo-element. The green border is preserved as a
           subtle in-cart indicator without the noisy pill.
           ═══════════════════════════════════════════════════════════════ */
        .fx-eor-frontcard.fx-eor-in-cart {
            border-color: #85C727;
        }
        /* v4.19.34 — Show the same checkmark as ticked/staged cards when
         * a card is in the WC cart. Reuses the existing ::after content
         * (defined on .fx-eor-check below) — just makes it visible. */
        .fx-eor-frontcard.fx-eor-in-cart .fx-eor-check::after { opacity: 1; }
        /* Current product card — green outline reinforced via inset shadow. */
        .fx-eor-frontcard--current { border-color: #2A6132; }
        .fx-eor-frontcard--current.selected { border-color: #2A6132; box-shadow: 0 0 0 2px #2A6132 inset; }
        /* Top strip: v4.7.32 — mint matching the accordion body (eliminates
         * the "cream empty space" James flagged). Strip is effectively invisible
         * on non-current, non-selected cards — clean card top edge. Only the
         * current product strip stays bold dark green for the badge. */
        /* v4.7.47: Top strip hidden everywhere — "Currently viewing" badge is
         * now rendered via .fx-eor-frontcard[data-current="1"] .fx-eor-body::before
         * (see below). Strip rules retained below as no-op for safety, but the
         * display:none here is authoritative across all viewports. */
        .fx-eor-card-top { display: none; }
        .fx-eor-frontcard--current .fx-eor-card-top { background: #2A6132; border-bottom-color: #2A6132; }
        .fx-eor-frontcard.selected .fx-eor-card-top { background: #D8E8D0; border-bottom-color: #2A6132; }
        .fx-eor-frontcard--current.selected .fx-eor-card-top { background: #2A6132; }
        .fx-eor-current-badge-text { color: #fff; font-size: 9px; font-weight: 600; letter-spacing: 1px; text-transform: uppercase; font-family: 'Jost', sans-serif; }
        /* v4.7.47: Card-main is now 50/50 horizontal split everywhere.
         * Image left half, body right half. The .fx-eor-check-col is
         * display:none below and drops out of layout, leaving two grid
         * children. The padding-top:100% trick on .fx-eor-img-link
         * (further below) locks the image to a square baseline that can
         * never exceed its 1fr track width. */
        .fx-eor-card-main { display: grid; grid-template-columns: 1fr 1fr; flex: 1 1 auto; }
        /* v4.18.1 — Mobile/constrained-width fix.
         *
         * The v4.7.65 fix (min-width:0 on card-main children) was scoped only
         * to #fx-eor-top-slider .fx-eor-card-main > *. Accordion cards never
         * needed it because they always sat in a full-width product-page
         * context where image min-content fit comfortably under 1fr.
         *
         * In a constrained-width context (one-page checkout sidebar, or
         * any container narrower than ~600px in mobile portrait), the
         * image's intrinsic natural width was forcing the first 1fr grid
         * track to grow beyond 50% of card width — eating the body column
         * entirely on the worst products (large source images). Result:
         * the card rendered as just a big image with no name/pills/price
         * visible. James reported this from mobile on the live site.
         *
         * The fix is the same as v4.7.65: tell each grid child it can shrink
         * below its min-content. The 1fr/1fr distribution then wins and the
         * image's width:100% inside its track stays at exactly 50% of card. */
        .fx-eor-card-main > * {
            min-width: 0;
        }
        /* v4.7.20: Subscription card — small "subscription" hint badge bottom-right of card body. */
        .fx-eor-frontcard--sub .fx-eor-body::after { content: '↻ Subscription'; display: inline-block; font-size: 9px; color: #4A5F4F; letter-spacing: 0.4px; text-transform: uppercase; margin-top: 2px; }
        /* v4.7.29: variation pills — small selectable chips inside each card.
         * Lets customers pick stem count (or any variation axis) without
         * leaving EOR. Active pill = filled forest green; inactive = cream
         * with mint border. Pills wrap to multiple lines if many variations. */
        .fx-eor-variations { display: flex; flex-wrap: wrap; gap: 4px; margin: 4px 0 8px; }
        .fx-eor-var-pill { background: #F4F7EF; border: 1px solid #C8DEC0; color: #1F3A24; font-size: 10px; padding: 3px 9px; border-radius: 12px; cursor: pointer; font-family: 'Jost', sans-serif; letter-spacing: 0.3px; font-weight: 500; transition: all 0.12s; line-height: 1.3; }
        .fx-eor-var-pill:hover { background: #E8F7D5; border-color: #85C727; }
        .fx-eor-var-pill.active { background: #2A6132; color: #fff; border-color: #2A6132; }
        .fx-eor-var-pill.active:hover { background: #1F3A24; border-color: #1F3A24; }
        /* v4.7.48: Check column is RE-VISIBLE but absolutely positioned at the
         * card's top-right corner as an overlay tick. The existing click handler
         * (line ~2947: checkCol.addEventListener('click', ...) with stopPropagation)
         * carries the toggle — no JS changes.
         *
         * Why not a ::after pseudo (as in v4.7.47)? Pseudo-elements can't receive
         * click events. With pointer-events:none, taps on the tick fell through
         * to the .fx-eor-name-link <a> behind it (which wraps the h4 across the
         * full row including the padding-right reserved for the tick), and the
         * browser navigated to that product page instead of selecting it. */
        .fx-eor-check-col {
            position: absolute;
            top: 8px;
            right: 8px;
            width: 28px;
            height: 28px;
            background: rgba(255, 255, 255, 0.96);
            border: 1.5px solid #2A6132;
            border-bottom: 1.5px solid #2A6132;
            border-right: 1.5px solid #2A6132;
            border-radius: 4px;
            z-index: 5;
            box-shadow: 0 1px 3px rgba(0, 0, 0, 0.08);
            cursor: pointer;
            display: flex;
            align-items: center;
            justify-content: center;
            padding: 0;
            box-sizing: border-box;
            /* v4.19.41 — Was `transition: background 0.15s, border-color 0.15s`.
             * James reported tick "takes about 2 seconds" perceptually.
             * Root cause was two combined: heavy synchronous JS after the
             * click (see toggle() rAF wrap below) AND a 150ms CSS fade on
             * the box colour + glyph opacity. Combined with browser repaint
             * delay on a JS-heavy page the box flip felt sluggish. The user
             * wants the tick to feel like a native checkbox: click, pop.
             * Set to none — colour change is instant on class toggle. */
            transition: none;
        }
        .fx-eor-frontcard.selected .fx-eor-check-col {
            background: #85C727;
            border-color: #85C727;
        }
        /* Inner .fx-eor-check now fills its overlay parent (was a 19x19 nested
         * box on the old left-column design). The existing ::after rule below
         * still renders the ✓ on .selected — no change there. */
        .fx-eor-check {
            width: 100%;
            height: 100%;
            border: 0;
            background: transparent;
        }
        .fx-eor-frontcard.selected .fx-eor-check {
            background: transparent;
            border: 0;
        }
        /* v4.7.48: Original 19x19 nested-box .fx-eor-check is now no-op —
         * the inner element fills its absolutely-positioned parent (see new
         * .fx-eor-check-col styling above). The ::after ✓ checkmark logic
         * is retained: opacity 0/1 driven by .selected. Font-size bumped to
         * suit the larger 28x28 overlay. Mobile @media tightens to 13px.
         *
         * v4.19.41 — `transition: all 0.15s` removed (was fading the
         * ::after ✓ glyph opacity 0→1 over 150ms). Pseudo-elements use
         * their host's transition rules, so this declaration was the
         * direct cause of the tick fade. With it gone the glyph pops on
         * the same frame the .selected class is added. Combined with the
         * rAF defer in toggle() (search "v4.19.41 — Yield to paint")
         * the tick now feels native-checkbox responsive. */
        .fx-eor-check { width: 100%; height: 100%; border: 0; background: transparent; display: flex; align-items: center; justify-content: center; transition: none; }
        .fx-eor-frontcard.selected .fx-eor-check { background: transparent; border: 0; }
        .fx-eor-check::after { content: '\2713'; color: #1F3A24; font-size: 15px; font-weight: 700; opacity: 0; line-height: 1; font-family: 'Jost', sans-serif; }
        .fx-eor-frontcard.selected .fx-eor-check::after { opacity: 1; }
        /* v4.7.49: align-self:start — image-link sits at the TOP of its grid cell
         * and refuses to stretch when the body cell grows taller. Long titles /
         * many variations leave blank space BELOW the image inside the card. */
        /* v4.7.50: dropped padding-top:100% pseudo. The image now drives its own
         * height via natural aspect ratio: tall portrait photos (house plants)
         * render as taller boxes, square photos (peonies) stay square. The
         * placeholder div has its own aspect-ratio:1/1 fallback below. */
        .fx-eor-img-link { display: block; text-decoration: none; line-height: 0; position: relative; align-self: start; }
        /* v4.7.17: real <img> tag instead of background-image div. Bypasses WP
         * Rocket's CSS background-image lazy loader (which was stripping the
         * inline style for lazy-load and never restoring it because the
         * relocator moves the wrapper after Rocket's scan). object-fit:cover
         * replicates the previous background-size:cover/center behaviour. */
        /* v4.7.50: <img> renders at its natural aspect ratio.
         *  - width:100% pins the image to the 1fr grid track width (50% of card)
         *  - height:auto lets the browser preserve the image's intrinsic ratio
         *  - Tall portrait photos render as taller cells, square stay square,
         *    landscape photos render shorter (with blank below via align-self:start)
         * Placeholder (no image src) gets aspect-ratio:1/1 so it doesn't collapse
         * to zero height when no image is loaded. */
        img.fx-eor-img { display: block; width: 100%; height: auto; object-fit: cover; object-position: center; background-color: #F4F7EF; }
        .fx-eor-img-placeholder { display: block; width: 100%; aspect-ratio: 1 / 1; background: linear-gradient(135deg, #E8F7D5, #85C727); }
        .fx-eor-body { padding: 10px 13px; display: flex; flex-direction: column; justify-content: space-between; }
        .fx-eor-name-link { text-decoration: none; color: inherit; }
        /* v4.25.70 — the mobile-2col "View" toggle is injected into grid cards
         * by JS; hidden everywhere except the mobile 2-column view. */
        .fx-eor-mview { display: none; }
        /* v4.7.47+: padding-right reserves space for the tick overlay at the
         * card's top-right corner (the repositioned .fx-eor-check-col element
         * from v4.7.48) so the title doesn't run under it. Mobile @media
         * tightens this to 30px for the smaller tick. */
        .fx-eor-body h4 { font-family: 'Cormorant Garamond', Georgia, serif; font-weight: 500; font-size: 16px; color: #1F3A24; margin: 0 0 4px; line-height: 1.1; padding-right: 38px; }
        /* v4.25.69 — title text rules for GRID cards only (NOT the slider, which
         * is .fx-eor-top-slider-track and keeps its own h4 rule). Keeps whole
         * words: wraps at spaces, only splits a single word if it genuinely
         * cannot fit; never mid-word from the theme's global word-break. */
        .fx-eor-grid .fx-eor-body h4 { overflow-wrap: break-word; word-break: normal; hyphens: none; padding-right: 30px; }
        .fx-eor-bottom { display: flex; justify-content: space-between; align-items: center; gap: 6px; }
        .fx-eor-price { font-size: 13px; color: #1F3A24; font-weight: 500; }
        .fx-eor-price .from { color: #4A5F4F; font-size: 9px; letter-spacing: 0.5px; text-transform: uppercase; }
        .fx-eor-qty { display: flex; align-items: center; border: 1px solid #2A6132; border-radius: 3px; overflow: hidden; height: 24px; background: #fff; }
        .fx-eor-qty button { width: 22px; height: 22px; background: #F4F7EF; border: 0; cursor: pointer; color: #2A6132; font-size: 13px; font-weight: 600; padding: 0; line-height: 1; transition: background 0.12s; }
        .fx-eor-qty button:hover { background: #E8F7D5; }
        .fx-eor-qty .fx-eor-q-input { width: 24px; text-align: center; border: 0; background: #fff; color: #1F3A24; font-size: 12px; font-weight: 600; font-family: inherit; }
        .fx-eor-qty.disabled { opacity: 0.35; pointer-events: none; }
        /* v4.7.32: per-card Add to Basket button — sage green matching the
         * FBF action button style. Single-product add path: adds this card's
         * product + chosen variation + current qty (defaulting to 1 if untiсked)
         * then redirects to /basket/. Independent of the master button. */
        .fx-eor-card-add { display: block; width: 100%; margin-top: 10px; background: #85C727; color: #1F3A24; border: 0; padding: 9px 12px; font-size: 11px; font-weight: 600; letter-spacing: 1px; text-transform: uppercase; cursor: pointer; border-radius: 3px; font-family: 'Jost', sans-serif; transition: all 0.12s; }
        .fx-eor-card-add:hover { background: #95D837; }
        .fx-eor-card-add:active { background: #75B717; }
        .fx-eor-card-add:disabled { opacity: 0.55; cursor: wait; background: #6B8B6F; color: #fff; }
        /* ═══════════════════════════════════════════════════════════════
         * v4.7.48: Currently-viewing badge — rendered via ::before on the
         * body when the wrapper carries data-current="1" (attribute set in
         * HTML at line 2066). Sits at the top of the writing column above
         * the title. The image is never touched.
         *
         * The previous-version (v4.7.47) tick overlay via .fx-eor-frontcard::after
         * has been removed — pseudo-elements can't receive clicks and
         * pointer-events:none made taps fall through to the title link.
         * Tick is now the real .fx-eor-check-col element repositioned as
         * an absolute overlay (see above). */
        .fx-eor-frontcard[data-current="1"] .fx-eor-body::before {
            content: "\2605 Currently viewing";
            display: inline-block;
            background: #2A6132;
            color: #fff;
            font-size: 10px;
            letter-spacing: 1px;
            text-transform: uppercase;
            padding: 4px 10px;
            border-radius: 3px;
            font-weight: 600;
            font-family: 'Jost', sans-serif;
            align-self: flex-start;
            margin-bottom: 4px;
        }
        /* v4.19.9 — Pagination controls. Sits between the grid and the CTA.
         * Counter + Load More button + invisible IntersectionObserver sentinel. */
        .fx-eor-pagination { margin: 18px 0 14px 0; padding: 16px 20px; background: #FAFEF5; border: 1px solid #D8E8D0; border-radius: 5px; text-align: center; font-family: 'Jost', sans-serif; }
        .fx-eor-pagination[data-fx-eor-done="1"] { background: #f9fafb; border-color: #e5e7eb; }
        .fx-eor-pagination-status { font-size: 12px; color: #4A5F4F; margin-bottom: 10px; letter-spacing: 0.4px; }
        .fx-eor-pagination-status strong { color: #1F3A24; font-weight: 600; }
        .fx-eor-pagination-button { display: inline-block; background: #fff; color: #2A6132; border: 1.5px solid #2A6132; padding: 10px 24px; font-size: 12px; font-weight: 600; letter-spacing: 1.2px; text-transform: uppercase; cursor: pointer; border-radius: 4px; font-family: 'Jost', sans-serif; transition: background 0.15s, color 0.15s, transform 0.1s; }
        .fx-eor-pagination-button:hover:not(:disabled) { background: #2A6132; color: #fff; }
        .fx-eor-pagination-button:active:not(:disabled) { transform: translateY(1px); }
        .fx-eor-pagination-button:disabled { opacity: 0.55; cursor: wait; }
        .fx-eor-pagination-button.is-loading::after { content: ' ⏳'; }
        /* Sentinel: small invisible div the IntersectionObserver watches. Sized
         * so it has measurable height but isn't visible. */
        .fx-eor-pagination-sentinel { height: 1px; width: 100%; margin-top: 10px; visibility: hidden; }
        /* v0.2.53 — the toolbar waits until every batch is home (the owner:
         * "they don't need that functionality until the whole thing is
         * loaded"); the status line carries the hint meanwhile. */
        .fx-eor-toolbar.fx-eor-toolbar--pending { display: none !important; }
        .fx-eor-pagination-hint { font-weight: 400; opacity: .8; }
        /* v4.7.31: inline CTA block — appears at the bottom of the EOR section
         * itself (not just the sticky bottom bar). Matches PROCEED TO CHECKOUT
         * button style for visual consistency with the rest of FBF checkout. */
        .fx-eor-cta { margin-top: 18px; padding: 18px 20px; background: #fff; border: 1px solid #C8DEC0; border-radius: 5px; text-align: center; }
        .fx-eor-cta-summary { display: inline-flex; align-items: center; gap: 12px; margin-bottom: 14px; font-family: 'Jost', sans-serif; font-size: 13px; color: #1F3A24; }
        .fx-eor-cta-count-pill { background: #2A6132; color: #fff; min-width: 28px; height: 28px; padding: 0 8px; border-radius: 14px; display: inline-flex; align-items: center; justify-content: center; font-weight: 700; font-size: 13px; font-family: 'Jost', sans-serif; }
        .fx-eor-cta-text strong { color: #2A6132; font-weight: 600; }
        .fx-eor-cta-btn { display: block; width: 100%; background: #2A6132; color: #fff; border: 0; padding: 16px 36px; font-size: 13px; font-weight: 600; letter-spacing: 1.5px; text-transform: uppercase; cursor: pointer; border-radius: 4px; font-family: 'Jost', sans-serif; transition: background 0.15s; }
        .fx-eor-cta-btn:hover:not(:disabled) { background: #1F3A24; }
        .fx-eor-cta-btn:disabled { opacity: 0.4; cursor: not-allowed; background: #6B8B6F; }

        /* ═══════════════════════════════════════════════════════════════
           v4.19.21 — Mini-basket panel inside the inline CTA card.
           Mirrors WC cart contents with edit controls. Default-collapsed;
           toggled via "Show basket ▾" button + auto-expanded by JS on
           page load when the cart isn't empty.
           ═══════════════════════════════════════════════════════════════ */
        .fx-eor-mini-basket-toggle {
            margin-left: auto;
            background: transparent;
            border: 1px solid #C8DEC0;
            color: #2A6132;
            font-family: 'Jost', sans-serif;
            font-size: 11px;
            font-weight: 600;
            letter-spacing: 0.4px;
            padding: 5px 12px;
            border-radius: 12px;
            cursor: pointer;
            transition: all 0.15s;
        }
        .fx-eor-mini-basket-toggle:hover { background: #E8F7D5; border-color: #85C727; }
        .fx-eor-mini-basket-toggle .fx-eor-mini-basket-toggle-hide { display: none; }
        .fx-eor-mini-basket-toggle[aria-expanded="true"] .fx-eor-mini-basket-toggle-show { display: none; }
        .fx-eor-mini-basket-toggle[aria-expanded="true"] .fx-eor-mini-basket-toggle-hide { display: inline; }

        .fx-eor-mini-basket {
            background: #FAFFF8;
            border: 1px solid #D8E8D0;
            border-radius: 6px;
            padding: 4px 0 0;
            margin: 10px 0 14px;
            font-family: 'Jost', sans-serif;
        }
        .fx-eor-mini-basket[hidden] { display: none; }

        .fx-eor-mini-basket-section { padding: 8px 14px 4px; }
        .fx-eor-mini-basket-section + .fx-eor-mini-basket-section { border-top: 1px dashed #D8E8D0; padding-top: 10px; margin-top: 4px; }

        .fx-eor-mini-basket-heading {
            display: flex;
            justify-content: space-between;
            align-items: baseline;
            font-size: 10px;
            font-weight: 700;
            letter-spacing: 1.2px;
            text-transform: uppercase;
            color: #1F3A24;
            margin-bottom: 8px;
        }
        .fx-eor-mini-basket-heading-count {
            background: #2A6132;
            color: #fff;
            border-radius: 8px;
            padding: 1px 8px;
            font-size: 10px;
            font-weight: 700;
            letter-spacing: 0.5px;
        }

        .fx-eor-mini-basket-list { display: flex; flex-direction: column; gap: 8px; }

        .fx-eor-mini-basket-row {
            display: grid;
            grid-template-columns: 48px 1fr auto;
            gap: 10px;
            align-items: center;
            background: #fff;
            border: 1px solid #E8F0E0;
            border-radius: 4px;
            padding: 8px 10px;
            transition: opacity 0.18s, transform 0.18s;
        }
        .fx-eor-mini-basket-row.is-pending { background: #FFFBED; border-color: #F0E5C8; }
        .fx-eor-mini-basket-row.is-removing { opacity: 0; transform: translateX(20px); pointer-events: none; }
        .fx-eor-mini-basket-row.is-busy { opacity: 0.6; pointer-events: none; }

        .fx-eor-mini-basket-row-img {
            width: 48px;
            height: 48px;
            border-radius: 3px;
            background: #f5f5f0;
            overflow: hidden;
            display: block;
        }
        .fx-eor-mini-basket-row-img img { width: 100%; height: 100%; object-fit: cover; display: block; }

        .fx-eor-mini-basket-row-body { min-width: 0; }
        .fx-eor-mini-basket-row-name {
            display: block;
            color: #1F3A24;
            font-size: 13px;
            font-weight: 500;
            line-height: 1.25;
            text-decoration: none;
            margin-bottom: 2px;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
        }
        .fx-eor-mini-basket-row-name:hover { color: #2A6132; }
        .fx-eor-mini-basket-row-variation {
            display: block;
            color: #6B8B6F;
            font-size: 10px;
            letter-spacing: 0.3px;
            margin-bottom: 4px;
        }
        .fx-eor-mini-basket-row-controls {
            display: flex;
            align-items: center;
            gap: 8px;
        }
        .fx-eor-mini-basket-qty {
            display: inline-flex;
            align-items: center;
            border: 1px solid #C8DEC0;
            border-radius: 3px;
            background: #F4F7EF;
            overflow: hidden;
        }
        .fx-eor-mini-basket-qty button {
            background: transparent;
            border: 0;
            width: 24px;
            height: 22px;
            cursor: pointer;
            color: #1F3A24;
            font-size: 14px;
            font-weight: 700;
            line-height: 1;
            padding: 0;
            font-family: 'Jost', sans-serif;
            transition: background 0.12s;
        }
        .fx-eor-mini-basket-qty button:hover:not(:disabled) { background: #E8F7D5; }
        .fx-eor-mini-basket-qty button:disabled { opacity: 0.3; cursor: not-allowed; }
        .fx-eor-mini-basket-qty-input {
            width: 32px;
            text-align: center;
            border: 0;
            border-left: 1px solid #C8DEC0;
            border-right: 1px solid #C8DEC0;
            background: #fff;
            font-family: 'Jost', sans-serif;
            font-size: 12px;
            font-weight: 600;
            color: #1F3A24;
            padding: 4px 0;
            -moz-appearance: textfield;
        }
        .fx-eor-mini-basket-qty-input::-webkit-outer-spin-button,
        .fx-eor-mini-basket-qty-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
        .fx-eor-mini-basket-row-subtotal {
            color: #1F3A24;
            font-size: 13px;
            font-weight: 600;
            min-width: 50px;
            text-align: right;
        }
        .fx-eor-mini-basket-remove {
            background: transparent;
            border: 0;
            width: 22px;
            height: 22px;
            cursor: pointer;
            color: #C03030;
            font-size: 16px;
            font-weight: 700;
            line-height: 1;
            border-radius: 11px;
            transition: background 0.12s;
            padding: 0;
        }
        .fx-eor-mini-basket-remove:hover { background: #FFEAEA; }

        .fx-eor-mini-basket-empty {
            color: #6B8B6F;
            font-size: 12px;
            font-style: italic;
            padding: 6px 0 4px;
        }

        .fx-eor-mini-basket-footer {
            background: #F4F7EF;
            border-top: 1px solid #D8E8D0;
            padding: 10px 14px;
            display: flex;
            justify-content: space-between;
            align-items: center;
            border-radius: 0 0 5px 5px;
            margin-top: 8px;
        }
        .fx-eor-mini-basket-totals { font-size: 12px; color: #1F3A24; }
        .fx-eor-mini-basket-totals strong { font-size: 14px; margin-left: 6px; }
        .fx-eor-mini-basket-link {
            font-size: 11px;
            font-weight: 600;
            letter-spacing: 0.5px;
            color: #2A6132;
            text-decoration: none;
            text-transform: uppercase;
        }
        .fx-eor-mini-basket-link:hover { color: #1F3A24; text-decoration: underline; }
        /* v4.19.46 — Clear all button + actions wrapper. The
         * actions container holds the Clear All button + the View
         * basket link on the right side of the footer. Stacks
         * vertically on small viewports so the link doesn't get
         * crammed against the total. */
        .fx-eor-mini-basket-actions {
            display: flex;
            align-items: center;
            gap: 10px;
        }
        .fx-eor-mini-basket-clear-all {
            background: transparent;
            border: 0;
            color: #B85F5F;
            font-size: 11px;
            font-weight: 600;
            letter-spacing: 0.5px;
            text-transform: uppercase;
            cursor: pointer;
            padding: 4px 6px;
            font-family: inherit;
            border-radius: 3px;
            transition: background 0.12s, color 0.12s;
        }
        .fx-eor-mini-basket-clear-all:hover {
            background: #FFEAEA;
            color: #8B3F3F;
        }
        /* v4.19.57 F-16 — in-popup Add to Basket commit button. Uses
         * #2A6132 sage-green (brand "PAY NOW" / primary CTA colour) so
         * it visually claims the primary action role inside the popup.
         * Sized smaller than the page-level CTA at L7196 because it
         * sits inline next to Clear all + View basket links rather than
         * being the page's hero action. */
        .fx-eor-mini-basket-add {
            background: #2A6132;
            border: 0;
            color: #fff;
            font-size: 11px;
            font-weight: 600;
            letter-spacing: 0.5px;
            text-transform: uppercase;
            cursor: pointer;
            padding: 8px 14px;
            font-family: 'Jost', sans-serif;
            border-radius: 3px;
            transition: background 0.12s;
        }
        .fx-eor-mini-basket-add:hover:not(:disabled) {
            background: #1F3A24;
        }
        .fx-eor-mini-basket-add:disabled {
            opacity: 0.4;
            cursor: not-allowed;
            background: #6B8B6F;
        }
        /* v4.19.65 — Hide the small "Add to Basket →" commit button in the
         * INLINE mini-basket position (on the product page). The inline
         * basket already has a page-level CTA (#fx-eor-cta-btn at L7732)
         * that does the same job and is the visual primary action, so the
         * small button next to "Clear all / View basket" is redundant there
         * and clutters the row.
         *
         * Inside the View & Edit MODAL (#fx-eor-basket-modal-body), the
         * page-level CTA isn't visible — the modal covers it — so the small
         * button IS the user's commit action and needs to remain. The same
         * mini-basket DOM gets teleported between the two positions
         * (v4.19.35), so a location-aware CSS rule is the cleanest split:
         * one rule, no JS changes, no extra state to maintain.
         *
         * The :not([hidden]) selector honours the existing JS toggle at
         * L12275/L12282 (button hidden when there are no pending items,
         * shown when there are). So the full behaviour matrix is:
         *   - Inline + pending items     → hidden (this rule)
         *   - Inline + no pending items  → hidden (browser default for [hidden])
         *   - Modal + pending items      → visible (modal rule below)
         *   - Modal + no pending items   → hidden (browser default for [hidden]) */
        .fx-eor-mini-basket-add { display: none; }
        #fx-eor-basket-modal-body .fx-eor-mini-basket-add:not([hidden]) {
            display: inline-block;
        }
        @media (max-width: 600px) {
            .fx-eor-mini-basket-actions { flex-direction: column; align-items: flex-end; gap: 4px; }
        }

        @media (max-width: 600px) {
            .fx-eor-mini-basket-row { grid-template-columns: 40px 1fr auto; gap: 8px; padding: 6px 8px; }
            .fx-eor-mini-basket-row-img { width: 40px; height: 40px; }
            .fx-eor-mini-basket-row-name { font-size: 12px; }
            .fx-eor-mini-basket-row-subtotal { font-size: 12px; min-width: 42px; }
        }
        .fx-eor-cta-hint { font-size: 10px; color: #4A5F4F; margin-top: 10px; letter-spacing: 0.8px; text-transform: uppercase; }
        /* v4.7.33: placeholder content for sections not yet populated.
         * Clean, calm "coming soon" treatment — sets expectation without
         * looking broken. Centred icon + message + sub-text. */
        .fx-eor-placeholder { padding: 36px 24px; text-align: center; background: #fff; border: 1px dashed #85C727; border-radius: 5px; }
        .fx-eor-placeholder-icon { font-size: 18px; color: #85C727; letter-spacing: 4px; margin-bottom: 10px; }
        .fx-eor-placeholder-msg { font-family: 'Cormorant Garamond', Georgia, serif; font-size: 20px; color: #1F3A24; margin: 0 0 6px; font-weight: 500; }
        .fx-eor-placeholder-sub { font-size: 11px; color: #4A5F4F; letter-spacing: 0.5px; margin: 0; font-family: 'Jost', sans-serif; }
        /* v4.7.37: Floral Bunches variety menu — text-link navigation index.
         * Mirrors the FBF mega menu styling: 3-column grid, uppercase items
         * with letter-spacing, sage hairline dividers, dark-green hover state.
         * Mobile drops to 1 column. No images, no cards — pure navigation. */
        .fx-eor-variety-list { list-style: none; padding: 0; margin: 0; display: grid; grid-template-columns: repeat(3, 1fr); gap: 0 24px; }
        /* v4.25.51 — "Choose Your Flowers By Variety" image gallery. Overrides
         * the text-list layout when .fx-eor-variety-gallery is present: a
         * responsive tile grid (auto-fill, min 150px → ~3 cols desktop, 2 on
         * tablet, 1 on narrow mobile, matching the EOR card feel). Each tile is
         * a best-seller image + a serif (Cormorant Garamond) title in the same
         * style as product titles. Links unchanged — the whole tile is the
         * existing <a> to the correct variety URL. */
        .fx-eor-variety-gallery { grid-template-columns: repeat(auto-fill, minmax(150px, 1fr)); gap: 16px; }
        .fx-eor-variety-gallery .fx-eor-variety-item { border-bottom: 0; }
        .fx-eor-variety-gallery .fx-eor-variety-tile {
            flex-direction: column; align-items: stretch; justify-content: flex-start;
            padding: 0; gap: 0; border: 1px solid #C8DEC0; border-radius: 8px; overflow: hidden;
            background: #fff; text-transform: none; letter-spacing: normal;
        }
        .fx-eor-variety-gallery .fx-eor-variety-tile:hover { border-color: #85C727; box-shadow: 0 2px 10px rgba(31,58,36,0.10); transform: translateY(-2px); }
        .fx-eor-variety-thumb { position: relative; display: block; width: 100%; aspect-ratio: 1 / 1; background: #F4F7EF; overflow: hidden; }
        .fx-eor-variety-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
        .fx-eor-variety-thumb .fx-eor-variety-bunches-peak-badge { position: absolute; top: 6px; left: 6px; font-size: 16px; }
        .fx-eor-variety-caption { display: flex; flex-direction: column; align-items: center; justify-content: center; gap: 2px; padding: 10px 12px; text-align: center; width: 100%; }
        .fx-eor-variety-gallery .fx-eor-variety-tile,
        .fx-eor-variety-gallery .fx-eor-variety-tile:hover,
        .fx-eor-variety-gallery .fx-eor-variety-name,
        .fx-eor-variety-gallery .fx-eor-variety-link { text-decoration: none !important; }
        .fx-eor-variety-gallery .fx-eor-variety-name {
            display: block; flex: none; width: 100%;
            font-family: 'Cinzel', 'Cormorant Garamond', Georgia, serif;
            font-weight: 500; font-size: 15px;
            color: #1F3A24; line-height: 1.2;
            text-transform: uppercase; letter-spacing: 0.5px;
            text-align: center; text-decoration: none;
            /* Wrap at SPACES/word boundaries first (so "CARNATIONS - RED" breaks
             * cleanly between words, never mid-word). overflow-wrap only splits a
             * single word if it genuinely can't fit (e.g. CHRYSANTHEMUMS, which
             * has no break point). NB: do NOT use word-break:break-word or
             * hyphens here — they force ugly mid-word breaks like "CARNATI/ONS". */
            overflow-wrap: break-word;
            word-break: normal;
        }
        .fx-eor-variety-gallery .fx-eor-variety-arrow { display: none; }
        .fx-eor-variety-gallery .fx-eor-variety-returns { display: block; font-family: 'Jost', sans-serif; font-size: 10px; font-weight: 500; color: #9a6a00; letter-spacing: 0.3px; margin-top: 2px; }
        /* v4.25.51 — cap the gallery at 30 tiles for load speed (matches the
         * EOR 30-rule); tiles beyond the cap are hidden until "Show all". */
        .fx-eor-variety-gallery.is-capped .fx-eor-variety-item--beyond-cap { display: none; }
        .fx-eor-variety-pagination { margin-top: 16px; text-align: center; }
        .fx-eor-variety-item { border-bottom: 1px solid #C8DEC0; }
        .fx-eor-variety-link { display: flex; align-items: center; justify-content: space-between; padding: 11px 4px; color: #1F3A24; text-decoration: none; font-family: 'Jost', sans-serif; font-size: 12px; font-weight: 500; letter-spacing: 1.2px; text-transform: uppercase; transition: all 0.12s; gap: 8px; }
        .fx-eor-variety-link:hover { color: #2A6132; padding-left: 10px; padding-right: 10px; background: linear-gradient(90deg, transparent 0%, #E8F7D5 50%, transparent 100%); }
        .fx-eor-variety-name { flex: 1; }
        .fx-eor-variety-arrow { color: #C8DEC0; font-size: 14px; opacity: 0; transform: translateX(-4px); transition: all 0.15s; }
        .fx-eor-variety-link:hover .fx-eor-variety-arrow { opacity: 1; transform: translateX(0); color: #2A6132; }
        /* v4.7.39: colour swatch dot for the By Colour section. Small filled
         * circle with a soft mint border so even white sits visible against
         * the white card body. Sits to the left of the name. */
        .fx-eor-colour-dot {
            display: inline-block;
            width: 16px;
            height: 16px;
            border-radius: 50%;
            border: 1px solid #C8DEC0;
            box-shadow: 0 1px 2px rgba(31, 58, 36, 0.08);
            flex-shrink: 0;
            margin-right: 12px;
            vertical-align: middle;
            transition: transform 0.15s;
        }
        .fx-eor-variety-link:hover .fx-eor-colour-dot { transform: scale(1.12); border-color: #2A6132; }
        .fx-eor-basket-bar { position: fixed; bottom: 0; left: 0; right: 0; background: #1F3A24; color: #fff; padding: 12px 28px; display: flex; align-items: center; justify-content: space-between; box-shadow: 0 -8px 24px -10px rgba(0,0,0,0.2); z-index: 99999; transform: translateY(110%); transition: transform 0.3s ease-out; font-family: 'Jost', system-ui, sans-serif; }
        .fx-eor-basket-bar.visible { transform: translateY(0); }
        .fx-eor-bb-summary { display: flex; align-items: center; gap: 14px; }
        .fx-eor-bb-count { background: #85C727; color: #1F3A24; width: 32px; height: 32px; border-radius: 50%; display: flex; align-items: center; justify-content: center; font-weight: 700; font-size: 14px; }
        .fx-eor-bb-text { font-size: 13px; }
        .fx-eor-bb-text strong { color: #85C727; font-weight: 500; }
        .fx-eor-bb-text .fx-eor-bb-sub { font-size: 11px; color: rgba(255,255,255,0.65); margin-top: 2px; }
        .fx-eor-bb-actions { display: flex; gap: 10px; align-items: center; }
        .fx-eor-bb-clear { color: rgba(255,255,255,0.6); background: transparent; border: 0; cursor: pointer; font-size: 12px; padding: 6px 10px; font-family: inherit; }
        /* v4.19.35 — View & Edit button. Outline style to sit between the
         * subtle "Clear" link and the prominent green "Add to Basket". */
        .fx-eor-bb-view {
            background: transparent;
            color: #fff;
            border: 1px solid rgba(255,255,255,0.45);
            padding: 9px 16px;
            font-size: 12px;
            font-weight: 500;
            letter-spacing: 0.8px;
            text-transform: uppercase;
            cursor: pointer;
            border-radius: 3px;
            font-family: inherit;
            transition: background 0.15s, border-color 0.15s;
        }
        .fx-eor-bb-view:hover {
            background: rgba(255,255,255,0.1);
            border-color: rgba(255,255,255,0.75);
        }
        /* v4.19.35 — Basket modal (View & Edit popup). The mini-basket DOM
         * teleports in/out, so the modal panel itself only styles the
         * surround (backdrop, header, scroll container). */
        .fx-eor-basket-modal {
            position: fixed;
            inset: 0;
            z-index: 100000;
            display: flex;
            align-items: center;
            justify-content: center;
            padding: 24px;
            box-sizing: border-box;
            font-family: 'Jost', system-ui, -apple-system, sans-serif;
        }
        .fx-eor-basket-modal[hidden] { display: none; }
        .fx-eor-basket-modal-backdrop {
            position: absolute;
            inset: 0;
            background: rgba(31, 58, 36, 0.55);
            backdrop-filter: blur(2px);
            -webkit-backdrop-filter: blur(2px);
            cursor: pointer;
        }
        .fx-eor-basket-modal-panel {
            position: relative;
            background: #fff;
            border-radius: 8px;
            box-shadow: 0 12px 48px -8px rgba(0,0,0,0.35);
            width: 100%;
            max-width: 560px;
            max-height: 90vh;
            display: flex;
            flex-direction: column;
            overflow: hidden;
            animation: fx-eor-basket-modal-in 0.18s ease-out;
        }
        @keyframes fx-eor-basket-modal-in {
            from { transform: scale(0.96); opacity: 0; }
            to   { transform: scale(1);    opacity: 1; }
        }
        .fx-eor-basket-modal-header {
            display: flex;
            align-items: center;
            justify-content: space-between;
            padding: 16px 22px;
            border-bottom: 1px solid #E8F0E0;
            background: linear-gradient(180deg, #FAFFF8 0%, #F4F7EF 100%);
        }
        .fx-eor-basket-modal-title {
            font-size: 16px;
            font-weight: 600;
            color: #1F3A24;
            margin: 0;
            font-family: 'Cormorant Garamond', serif;
            letter-spacing: 0.3px;
        }
        .fx-eor-basket-modal-close {
            background: transparent;
            border: 0;
            color: #1F3A24;
            font-size: 26px;
            line-height: 1;
            cursor: pointer;
            padding: 4px 10px;
            border-radius: 3px;
            transition: background 0.15s;
        }
        .fx-eor-basket-modal-close:hover { background: #E8F7D5; }
        .fx-eor-basket-modal-body {
            padding: 16px 22px;
            overflow-y: auto;
            flex: 1;
            min-height: 0;
        }
        /* While the mini-basket is teleported into the modal, override its
         * inline-collapse styling so it shows fully expanded. */
        .fx-eor-basket-modal-body .fx-eor-mini-basket {
            border: 0;
            padding: 0;
            margin: 0;
            background: transparent;
        }
        /* Hide the show/hide toggle when mini-basket is inside the modal —
         * the modal IS the open state. */
        .fx-eor-basket-modal-body .fx-eor-mini-basket-toggle { display: none; }
        @media (max-width: 600px) {
            .fx-eor-basket-modal { padding: 12px; }
            .fx-eor-basket-modal-panel { max-height: 96vh; }
            .fx-eor-basket-modal-header { padding: 12px 16px; }
            .fx-eor-basket-modal-body { padding: 12px 16px; }
            /* Hide View button label in favour of compact icon on small screens. */
            .fx-eor-bb-view { padding: 9px 12px; font-size: 11px; letter-spacing: 0.4px; }
        }
        .fx-eor-bb-add { background: #85C727; color: #1F3A24; border: 0; padding: 12px 26px; font-size: 13px; font-weight: 600; letter-spacing: 1px; text-transform: uppercase; cursor: pointer; border-radius: 3px; font-family: inherit; }
        .fx-eor-bb-add:hover { background: #95D837; }
        .fx-eor-bb-add:disabled { opacity: 0.6; cursor: wait; }

        /* v4.7.67: close button on basket bar — dismisses the bar from view.
         * Absolute-positioned at top-right of the bar (bar is position: fixed
         * so this anchors within it). Works on both desktop and mobile
         * layouts (mobile stacks summary + actions vertically; close button
         * floats above both).
         *
         * Clicking removes .visible class from the bar → bar slides down via
         * the existing transform: translateY(110%) default state. Accordion's
         * updateBasketBar() will re-add .visible on next user interaction
         * because barCommitted is still true — so the bar comes back when
         * the user ticks/unticks something. */
        .fx-eor-bb-close {
            position: absolute;
            top: 6px;
            right: 8px;
            background: transparent;
            border: 0;
            color: rgba(255,255,255,0.55);
            font-size: 22px;
            cursor: pointer;
            width: 32px;
            height: 32px;
            display: flex;
            align-items: center;
            justify-content: center;
            line-height: 1;
            border-radius: 50%;
            transition: all 0.12s;
            padding: 0;
            font-family: inherit;
            z-index: 1;
            -webkit-tap-highlight-color: transparent;
        }
        .fx-eor-bb-close:hover {
            color: #fff;
            background: rgba(255,255,255,0.12);
        }
        .fx-eor-bb-close:active {
            background: rgba(255,255,255,0.2);
        }
        @media (max-width: 700px) {
            /* ═══════════════════════════════════════════════════════════════
             * v4.7.47: Mobile @media — the structural 50/50 redesign now
             * lives in the base styles (applied desktop + mobile alike).
             * This block only tightens sizes/spacing for narrow viewports:
             * full-width cards, smaller fonts, smaller tick + badge, denser
             * stack. Card layout itself is shared with desktop. */

            .fx-eor-wrap { padding: 0 14px; margin: 20px auto; }
            /* v4.25.70 — show the mobile 1/2 column selector at the top of the
             * EOR; hide the desktop View cluster on mobile so only one shows. */
            .fx-eor-view-wrap { display: none; }
            .fx-eor-mview-wrap {
                display: flex; align-items: center; gap: 6px;
                justify-content: flex-end; margin: 0 0 12px; font-family: inherit;
            }
            .fx-eor-mview-btn {
                background: #FAFAF8; border: 1.5px solid #D8E8D0; color: #1F3A24;
                padding: 6px 12px; border-radius: 999px; line-height: 1; cursor: pointer;
                display: inline-flex; align-items: center; justify-content: center;
                height: 28px; box-sizing: border-box; transition: background 0.12s, border-color 0.12s;
            }
            .fx-eor-mview-btn:hover { background: #E8F7D5; border-color: #85C727; }
            .fx-eor-mview-btn.is-active { background: #2A6132; border-color: #2A6132; }
            .fx-eor-mview-btn.is-active .fx-eor-view-icon--1col::before,
            .fx-eor-mview-btn.is-active .fx-eor-view-icon--2col::before { background: #fff; }
            .fx-eor-grid { grid-template-columns: 1fr; gap: 10px; max-height: 760px; }
            /* v4.25.69 — mobile column count for ALL EOR card grids, driven by
             * the operator setting (wrap class). Default 2. The slider
             * (.fx-eor-top-slider-track) is NOT a .fx-eor-grid, so it's
             * untouched. The title text rules (whole words, shrink to fit) are
             * applied for the narrower 2/3-col cards. */
            /* v4.25.70 — mobile column views: 1 or 2.
             *   mcols-1 = original layout, untouched (image left / text right).
             *   mcols-2 = vertical card: image fills the column on top, then the
             *             title + a "View" button; the variations + price + Add
             *             button stay hidden until the customer taps View
             *             (.fx-eor-mexpanded), so the grid stays tidy.
             * Scoped to .fx-eor-grid — the slider (.fx-eor-top-slider-track)
             * is never matched. The mcols-1 default keeps the proven layout. */
            .fx-eor-grid.fx-eor-mcols-1 { grid-template-columns: 1fr; }

            /* v4.25.72 — cleaner 2-col: a single thin lime-lined grid. The grid
             * background is lime and the 1px gaps show through as gridlines;
             * each card is a flush white cell with no own border/radius, so the
             * frame-wrapping + padding is gone and products slot neatly in. */
            .fx-eor-grid.fx-eor-mcols-2 {
                grid-template-columns: repeat(2, 1fr);
                gap: 1px; background: #85C727;
                border: 0; border-radius: 6px;
                overflow: hidden; padding: 1px; max-height: none;
            }
            .fx-eor-grid.fx-eor-mcols-2 .fx-eor-frontcard {
                border: 0; border-radius: 0; background: #fff;
                box-shadow: none; transition: none;
            }
            /* v4.25.76 — no green fill in empty space: if the last row has a
             * single (odd) card, span it across both columns so the lime grid
             * background never shows through an empty cell. */
            .fx-eor-grid.fx-eor-mcols-2 > .fx-eor-frontcard:last-child:nth-child(odd) { grid-column: 1 / -1; }
            .fx-eor-grid.fx-eor-mcols-2 .fx-eor-frontcard:hover { transform: none; box-shadow: none; }
            /* image on top, content stacked below (reuses the card-main grid) */
            .fx-eor-grid.fx-eor-mcols-2 .fx-eor-card-main { grid-template-columns: 1fr; }
            .fx-eor-grid.fx-eor-mcols-2 .fx-eor-img-link { display: block; width: 100%; }
            .fx-eor-grid.fx-eor-mcols-2 img.fx-eor-img { width: 100%; height: auto; aspect-ratio: 1 / 1; object-fit: cover; display: block; }
            .fx-eor-grid.fx-eor-mcols-2 .fx-eor-body { padding: 6px 8px 8px; gap: 5px; }
            .fx-eor-grid.fx-eor-mcols-2 .fx-eor-body h4 { font-size: 14px; padding-right: 4px; text-align: center; }
            /* collapsed: hide variations, price/qty row and the Add button */
            .fx-eor-grid.fx-eor-mcols-2 .fx-eor-variations,
            .fx-eor-grid.fx-eor-mcols-2 .fx-eor-bottom,
            .fx-eor-grid.fx-eor-mcols-2 .fx-eor-card-add,
            .fx-eor-grid.fx-eor-mcols-2 .fx-eor-notify-wrap { display: none; }
            /* the View toggle — mid green (midpoint of mint + lime), instant.
             * touch-action removes the mobile tap delay so it opens at once. */
            .fx-eor-grid.fx-eor-mcols-2 .fx-eor-mview {
                display: block; width: 100%; margin-top: 1px; padding: 7px 0;
                background: #B6DF7E; color: #1F3A24; border: 0;
                border-radius: 5px; font-family: 'Jost', sans-serif; font-size: 12px;
                font-weight: 600; letter-spacing: 0.4px; text-transform: uppercase;
                cursor: pointer; touch-action: manipulation; transition: none;
            }
            .fx-eor-grid.fx-eor-mcols-2 .fx-eor-mview:hover { background: #A6D466; }
            /* v4.25.73 — once expanded, the View button is gone (no Close/Hide). */
            .fx-eor-grid.fx-eor-mcols-2 .fx-eor-frontcard.fx-eor-mexpanded .fx-eor-mview { display: none; }
            /* expanded: reveal variations + price + Add (button stays mid-green) */
            .fx-eor-grid.fx-eor-mcols-2 .fx-eor-frontcard.fx-eor-mexpanded .fx-eor-variations,
            .fx-eor-grid.fx-eor-mcols-2 .fx-eor-frontcard.fx-eor-mexpanded .fx-eor-bottom,
            .fx-eor-grid.fx-eor-mcols-2 .fx-eor-frontcard.fx-eor-mexpanded .fx-eor-card-add,
            .fx-eor-grid.fx-eor-mcols-2 .fx-eor-frontcard.fx-eor-mexpanded .fx-eor-notify-wrap { display: flex; }
            .fx-eor-grid.fx-eor-mcols-2 .fx-eor-frontcard.fx-eor-mexpanded .fx-eor-variations { flex-wrap: wrap; }
            .fx-eor-grid.fx-eor-mcols-2 .fx-eor-frontcard.fx-eor-mexpanded .fx-eor-card-add { display: block; }

            /* v4.20.6 — defensive mobile overflow safety net.
             *
             * Forces the EOR wrap to never exceed its parent on mobile,
             * regardless of whether it's in standalone or --inline mode.
             * Then ensures every direct child container has box-sizing
             * border-box + max-width 100% so padded children fit. The
             * .fx-eor-acc-title gets min-width: 0 (the classic flex-item
             * overflow fix) and h3 gets overflow-wrap anywhere so the
             * long accordion headings wrap rather than push the container
             * wider than the viewport. */
            .fx-eor-wrap,
            .fx-eor-wrap--inline {
                max-width: 100%;
                box-sizing: border-box;
            }
            .fx-eor-accordion,
            .fx-eor-acc-header,
            .fx-eor-acc-body {
                max-width: 100%;
                box-sizing: border-box;
            }
            .fx-eor-acc-title {
                min-width: 0;
                flex: 1 1 auto;
            }
            /* v4.20.17 — Prefer word-boundary wraps over mid-word
             * breaks. James reported headings like "Flower Subscripti
             * ons" / "Plant Subscriptio ns" breaking mid-syllable on
             * mobile (image 2026-05-19 14:37). The pre-v4.20.17 rules
             * used `word-break: break-word` + `overflow-wrap: anywhere`
             * which let the browser break inside words AT THE FIRST
             * AVAILABLE POSITION even when the word would otherwise
             * fit on the next line. Switched to `word-break: normal`
             * (only break at word boundaries — spaces, hyphens) +
             * `overflow-wrap: break-word` (allow mid-word break ONLY
             * as a last resort when the word is wider than the
             * container). Result: "Subscriptions" wraps onto its own
             * line as a whole word instead of breaking mid-syllable. */
            .fx-eor-acc-title h3 {
                overflow-wrap: break-word;
                word-break: normal;
                hyphens: none;
            }
            .fx-eor-preview {
                overflow-wrap: break-word;
                word-break: normal;
            }
            /* v4.20.17 — Hide pills on mobile.
             *
             * James (2026-05-19 14:54): "remove those pills on mobile
             * view. They just repeat the title." Several pills are
             * identical to their adjacent heading (INDOOR PLANTS pill →
             * "Indoor Plants" h3; FLOWER SUBSCRIPTIONS pill → "Flower
             * Subscriptions" h3; PLANT SUBSCRIPTIONS pill → "Plant
             * Subscriptions" h3). Others closely paraphrase (BY COLOUR
             * pill → "Choose Your Bouquets By Colour"). On desktop the
             * pills work as visual category markers, but on mobile —
             * where everything stacks vertically — they're redundant
             * noise eating ~30% of the header row width.
             *
             * display:none !important so the heading + preview text
             * gets the full row to wrap into. Reverts cleanly on
             * desktop because the rule is inside a 700px media query.
             *
             * Supersedes the v4.20.15 wrap-the-pill rules — those
             * preserved the pill but made it narrower; James decided
             * the pill is unnecessary on mobile altogether. */
            .fx-eor-pill {
                display: none !important;
            }
            /* v4.20.5 elements — keep content within viewport. */
            .fx-eor-variety-list--inseason .fx-eor-variety-link {
                max-width: 100%;
                box-sizing: border-box;
            }
            .fx-eor-variety-list--inseason .fx-eor-variety-row {
                min-width: 0;
                max-width: 100%;
            }
            .fx-eor-variety-list--inseason .fx-eor-variety-name {
                min-width: 0;
                overflow-wrap: anywhere;
                word-break: break-word;
            }
            .fx-eor-variety-blurb,
            .fx-eor-variety-sourcing,
            .fx-eor-variety-sourcing-text {
                max-width: 100%;
                overflow-wrap: anywhere;
                word-break: break-word;
            }
            .fx-eor-also-available,
            .fx-eor-also-available-list {
                max-width: 100%;
                box-sizing: border-box;
                overflow-wrap: anywhere;
                word-break: break-word;
            }
            /* v4.20.18 — Also-available drops from 3-col (desktop) to
             * 2-col on mobile so Phalaenopsis Orchids / Chrysanthemums
             * and other long variety names breathe. Inherits all other
             * styling from the base .fx-eor-also-available-list rule. */
            .fx-eor-also-available-list {
                grid-template-columns: repeat(2, 1fr);
                gap: 4px 10px;
            }
            /* Floral Bunches: ensure peak emoji + returns pill don't push
             * the link wider than its container. */
            .fx-eor-variety-item {
                max-width: 100%;
                box-sizing: border-box;
            }
            .fx-eor-variety-item .fx-eor-variety-name {
                min-width: 0;
                overflow-wrap: anywhere;
                word-break: break-word;
            }

            /* Accordion shell — drop horizontal padding so cards run flush */
            .fx-eor-acc-header { padding: 14px 16px; gap: 10px; }
            .fx-eor-acc-title h3 { font-size: 18px; }
            .fx-eor-acc-body { padding: 16px 0 18px; }
            .fx-eor-intro { margin-left: 14px; margin-right: 14px; }

            /* Body density */
            .fx-eor-body { padding: 6px 9px 9px; gap: 4px; }
            .fx-eor-body h4 { font-size: 15px; padding-right: 30px; margin: 0 0 4px; }

            /* Smaller currently-viewing badge */
            .fx-eor-frontcard[data-current="1"] .fx-eor-body::before {
                font-size: 9px;
                padding: 3px 7px;
                margin-bottom: 3px;
            }

            /* Smaller tick overlay — targets the .fx-eor-check-col real element
             * (was the ::after pseudo before v4.7.48) */
            .fx-eor-check-col {
                top: 6px;
                right: 6px;
                width: 24px;
                height: 24px;
            }
            /* v4.7.51: invisible ::before pseudo extends the CLICK area without
             * changing the visible tick. With -12px inset on each side, the
             * tappable zone becomes 48x48 (4× the area) while the rendered
             * sage-bordered box stays 24x24. Pseudo lives inside the check-col's
             * box, so taps anywhere on the 48x48 area register on the parent
             * .fx-eor-check-col element and trigger the existing click handler
             * (line ~2964: checkCol.addEventListener with stopPropagation).
             * z-index:5 on the check-col puts the pseudo above the title's
             * <a class="fx-eor-name-link"> link beneath, so near-misses with
             * fat thumbs select the card instead of navigating to the product. */
            .fx-eor-check-col::before {
                content: "";
                position: absolute;
                top: -12px;
                right: -12px;
                bottom: -12px;
                left: -12px;
                background: transparent;
            }
            .fx-eor-check::after {
                font-size: 13px;
            }

            /* Variation pills + price row + qty stepper tightened */
            .fx-eor-variations { gap: 3px; margin: 4px 0 6px; }
            .fx-eor-var-pill { font-size: 10px; padding: 2px 7px; }
            .fx-eor-bottom { gap: 4px; margin-top: 4px; }
            .fx-eor-price { font-size: 13px; }
            .fx-eor-qty { height: 22px; }
            .fx-eor-qty button { width: 20px; height: 20px; font-size: 12px; }
            .fx-eor-qty .fx-eor-q-input { width: 22px; font-size: 11px; }
            .fx-eor-card-add { padding: 8px 6px; font-size: 10px; margin-top: 6px; }

            /* Bottom basket bar / CTA / variety list — existing mobile overrides */
            .fx-eor-basket-bar { padding: 10px 14px; flex-direction: column; gap: 8px; align-items: stretch; }
            .fx-eor-bb-add { width: 100%; }
            .fx-eor-cta { padding: 14px 14px; margin-top: 14px; }
            .fx-eor-cta-btn { padding: 14px 24px; font-size: 12px; }
            /* v4.7.37: variety list collapses to single column on mobile. */
            .fx-eor-variety-list { grid-template-columns: 1fr; gap: 0; }
            .fx-eor-variety-gallery { grid-template-columns: repeat(2, 1fr); gap: 12px; }
            .fx-eor-variety-gallery .fx-eor-variety-name { font-size: 14px; letter-spacing: 0.5px; }
            .fx-eor-variety-link { padding: 13px 4px; font-size: 13px; }
        }

        /* ═══ TOP SLIDER CSS — v4.7.64 (ACCORDION MARKUP, SCOPED OVERRIDES) ═
         *
         * The slider cards reuse accordion's `.fx-eor-frontcard` markup
         * verbatim (same class names, same structure). All accordion CSS
         * rules — card visuals, tick check column, variation pills, qty
         * stepper, Add to Basket button — apply unchanged.
         *
         * This block defines only:
         *   1. The slider's OUTER container (wrap, header, track) — these
         *      have unique `.fx-eor-top-slider-*` classes, no conflict with
         *      accordion.
         *   2. Slider-specific OVERRIDES of card behaviour via
         *      `#fx-eor-top-slider .fx-eor-frontcard` selectors. These add:
         *        - card width clamping (calc(100vw - 56px))
         *        - scroll-snap alignment
         *        - card-main aspect-ratio (locks height to image's natural aspect)
         *        - body overflow-y: auto (vertical scroll for many-variation cards)
         *        - hide empty card-top on non-current cards (no white space)
         *      These overrides cascade only over cards INSIDE the slider —
         *      accordion cards remain untouched.
         *
         * v4.7.58 width disaster is prevented by:
         *   .fx-eor-top-slider-wrap { width: 100%; overflow: hidden; padding: 0 14px; }
         *   #fx-eor-top-slider .fx-eor-frontcard { flex: 0 0 calc(100vw - 56px); }
         * The wrap clamps; cards use viewport-anchored widths; the track scrolls
         * horizontally within the clamped wrap. */
        .fx-eor-top-slider-wrap {
            display: none;
            width: 100%;
            box-sizing: border-box;
            margin: 14px 0 18px;
            padding: 0 10px;
            overflow: hidden;
            font-family: 'Jost', system-ui, -apple-system, sans-serif;
            color: #1A1A1A;
        }
        .fx-eor-top-slider-header {
            display: flex;
            align-items: baseline;
            justify-content: space-between;
            padding: 0 0 8px;
        }
        .fx-eor-top-slider-title {
            font-family: 'Cormorant Garamond', Georgia, serif;
            font-weight: 500;
            font-size: 18px;
            color: #1F3A24;
        }
        .fx-eor-top-slider-hint {
            font-size: 10px;
            color: #4A5F4F;
            letter-spacing: 0.8px;
            text-transform: uppercase;
            font-family: 'Jost', sans-serif;
        }
        .fx-eor-top-slider-track {
            display: flex;
            gap: 10px;
            overflow-x: auto;
            overflow-y: hidden;
            scroll-snap-type: x mandatory;
            -webkit-overflow-scrolling: touch;
            scrollbar-width: none;
            padding: 6px 0 14px;
            align-items: stretch;
            /* v4.16.1 — touch-action: pan-x tells the browser horizontal
             * motion on the track is for scrolling. Without this, taps
             * landing on variation pills / qty buttons / Add-to-Basket
             * inside cards were consumed as taps before the browser
             * recognised the horizontal pan gesture, so users could only
             * swipe by starting their finger on the image area. With
             * pan-x set on the parent, the browser commits to "this
             * touch is a scroll" once horizontal motion exceeds its
             * native threshold (~5px on iOS/Android), cancelling any
             * pending tap on the child. Stationary touches still fire
             * clicks normally so buttons remain tappable. */
            touch-action: pan-x;
            /* v4.7.68: scroll-padding ensures snap landing position respects
             * the wrap's outer padding. Without this, cards snap with their
             * left edge at the track's content-box left (which is at the
             * wrap's padding-left position) but subpixel rounding can
             * leave the card's right edge slightly past the visible area,
             * chopping a few pixels off. Setting scroll-padding-left: 0
             * explicitly anchors the snap point. */
            scroll-padding: 0;
        }
        .fx-eor-top-slider-track::-webkit-scrollbar { display: none; }

        /* ── Scoped overrides for cards inside the slider only ──────────── */

        /* v4.7.65/68: Card sizing with HARD CONSTRAINTS to prevent the flex
         * item (the card) from growing past its flex-basis due to content
         * min-content. Without `min-width: 0`, flex items default to
         * `min-width: auto` which means content min-content size becomes the
         * floor — long product titles or wide variation pills pushed the
         * card wider than calc(...), causing badges/buttons/pills to
         * overflow off the right of the viewport.
         *
         * v4.7.68 — card width tightened from `calc(100vw - 40px)` to
         * `calc(100vw - 60px)`. The previous width left the card with
         * subpixel margin to spare (335px card in 355px track on a 375
         * viewport, with 10px gap), and iOS Safari's scroll-snap was rounding
         * the snap position so the right edge of the card was chopping a
         * few pixels at the viewport edge. The extra 20px breathing room
         * means snap landing is robust even with subpixel rounding, and
         * gives a comfortable peek of the next card on the right.
         *
         * `scroll-snap-stop: always` enforces one-card-at-a-time gestures
         * so fast swipes can't overshoot and land mid-card. */
        #fx-eor-top-slider .fx-eor-frontcard {
            flex: 0 0 calc(100vw - 60px);
            width: calc(100vw - 60px);
            max-width: calc(100vw - 60px);
            min-width: 0;
            scroll-snap-align: start;
            scroll-snap-stop: always;
            box-sizing: border-box;
        }

        /* Card-main: EXPLICIT height (not aspect-ratio which is only preferred).
         *
         * v4.7.66: changed from `aspect-ratio: 2/1` to explicit
         * `height: calc((100vw - 40px) / 2)`. The aspect-ratio approach was
         * only a PREFERRED ratio — when content (image at natural aspect ratio
         * via accordion's `img.fx-eor-img { height: auto }`, or body with
         * many variations) exceeded the calculated height, the container grew
         * to fit. Resulted in cards being 400-450px tall instead of the
         * intended ~167px, and body scroll never engaged because there was
         * always enough height for all content.
         *
         * The explicit `height` + `max-height` + `grid-template-rows` triple
         * locks the row to exactly half the card width regardless of content.
         * `flex: 0 0 auto` overrides accordion's `flex: 1 1 auto` (which
         * would otherwise let card-main grow to fill card height).
         *
         * v4.7.68: aspect changed from 2:1 (square cells = card-main width / 2)
         * to 3:2 (portrait cells = card-main width × 2/3). FBF product photos
         * are tall portrait (typical bouquet photography aspect ~3:4 or 4:5).
         * With square cells, `object-fit: cover` aggressively cropped the
         * top/bottom of each photo. With 3:4 portrait cells (card-main width
         * × 2/3) the photos display naturally without heavy cropping.
         *
         * Math: card_width = 100vw - 60. card-main width = card_width.
         * card-main height = card_width × 2/3 (= 3:2 W:H card-main aspect).
         * Each cell is (card_width / 2) × (card_width × 2/3) = 3:4 portrait. */
        #fx-eor-top-slider .fx-eor-card-main {
            display: grid;
            grid-template-columns: 1fr 1fr;
            grid-template-rows: calc((100vw - 60px) * 2 / 3);
            height: calc((100vw - 60px) * 2 / 3);
            max-height: calc((100vw - 60px) * 2 / 3);
            width: 100%;
            max-width: 100%;
            min-width: 0;
            flex: 0 0 auto;
            box-sizing: border-box;
            overflow: hidden;
        }

        /* v4.7.66: image must fill its grid cell, NOT take natural aspect.
         * Accordion's `img.fx-eor-img` has `height: auto` for the accordion's
         * intentional behaviour (tall photos render as tall cells). In the
         * slider we want uniform card height regardless of photo aspect, so
         * we override the image to fill the constrained cell with cover. */
        #fx-eor-top-slider .fx-eor-img-link {
            display: block;
            width: 100%;
            height: 100%;
            overflow: hidden;
            align-self: stretch;
        }
        #fx-eor-top-slider img.fx-eor-img {
            width: 100%;
            height: 100%;
            object-fit: cover;
            display: block;
        }
        #fx-eor-top-slider .fx-eor-img-placeholder {
            width: 100%;
            height: 100%;
            aspect-ratio: auto;
        }

        /* v4.7.65: CRITICAL — force grid cells to respect 1fr / 1fr distribution.
         * Without `min-width: 0` on grid items, CSS Grid defaults to
         * `min-width: auto` and a column can't shrink below its content's
         * min-content width. Long product titles ("Subscription Flowers -
         * Peonies Pinks & Lavender") have wide min-content that pushes the
         * body grid cell past 50% of card width, forcing the card to expand
         * (or content to overflow). Setting `min-width: 0` allows the grid to
         * enforce the 1fr/1fr distribution and any overflow within body is
         * then clipped/scrolled by the body's own overflow rules. */
        #fx-eor-top-slider .fx-eor-card-main > * {
            min-width: 0;
        }

        /* Hide the empty card-top strip on non-current cards in the slider
         * (per James: "too much white because of the selected product
         * element bar"). The strip stays on the current product card to
         * show the "Currently viewing" badge. */
        #fx-eor-top-slider .fx-eor-frontcard:not(.fx-eor-frontcard--current) .fx-eor-card-top {
            display: none;
        }

        /* v4.7.65: Pad the title in slider so it doesn't run into the
         * absolute-positioned `.fx-eor-check-col` overlay in the top-right
         * corner. Accordion cards are full-width on mobile so titles have
         * room before reaching the corner; slider cards are narrower so we
         * pad explicitly. */
        #fx-eor-top-slider .fx-eor-name-link {
            padding-right: 32px;
            box-sizing: border-box;
            display: block;
        }

        /* v4.7.70: title line-clamp + word-break for the slider only.
         *
         * Problem: long product titles like "Subscription Flowers - Peonies
         * Pinks & Lavender" wrap to 3-5 lines in the narrow slider body
         * cell, eating vertical space and creating inconsistent layouts.
         *
         * Solution: clamp at 2 lines with "..." ellipsis. `overflow-wrap:
         * break-word` ensures ultra-long single words break cleanly rather
         * than overflowing. `text-wrap: balance` (modern browsers — Chrome
         * 114+, Firefox 121+, Safari 17.4+) distributes line breaks evenly
         * so a 3-word title splits like "Lovely / Spring Subscription"
         * rather than "Lovely Spring / Subscription" — falls back
         * gracefully on older browsers.
         *
         * Why not shrink-to-fit:
         *  - Requires JS measurement per card (adds layout cost)
         *  - Long titles shrunk to one line become illegible at ~7-8px
         *  - Variable font sizes across cards look chaotic
         *  - Line-clamp is the e-commerce industry standard
         *    (Amazon/Etsy/ASOS/Wayfair all use this pattern)
         *
         * Scoped to slider only — accordion titles unchanged.
         *
         * v4.20.43 — Clamp REMOVED per James (2026-05-19): "allow the
         * title to show in full". The clamp was hiding the full product
         * name on the slider. Title now wraps to as many lines as the
         * name needs; the body's overflow-y:auto handles vertical
         * scrolling so the user can roll the title and variations up
         * the card if they exceed the visible area. */
        #fx-eor-top-slider .fx-eor-body h4 {
            display: block;
            -webkit-line-clamp: unset;
            -webkit-box-orient: unset;
            overflow: visible;
            text-overflow: clip;
            overflow-wrap: break-word;
            text-wrap: balance;
        }

        /* v4.20.43 — Slider footer: position:sticky pins the price/qty
         * row + the Add-to-Basket button to the bottom of the body
         * while the upper content (title + variations) scrolls behind
         * it. James (2026-05-19): "Fix the add to basket button to the
         * bottom of the box with 2px of white between the button and
         * the border. Then fix the quantity row above that then the
         * slider element that is there rolls the full title and
         * variations up the page".
         *
         * Layout mechanism:
         *  - Body is the scroll container (overflow-y:auto, defined
         *    height from grid-template-rows on card-main, see line
         *    11800-ish).
         *  - .fx-eor-slider-footer is the LAST child of the body.
         *    position:sticky + bottom:2px sticks it 2px from the
         *    body's visible bottom edge as the user scrolls.
         *  - .fx-eor-bottom (price + qty) sits at the top of the
         *    footer; .fx-eor-card-add sits below it. Both pinned
         *    together at the bottom.
         *  - background:#fff ensures scrolling content above renders
         *    cleanly behind the footer (no see-through).
         *  - margin-top:auto pushes the footer to the bottom of the
         *    flex column even when content is short (so footer doesn't
         *    sit awkwardly mid-card).
         *
         * Why position:sticky not flex margin-top:auto alone: when
         * content exceeds body height, the body scrolls. Without
         * sticky, the footer scrolls out of view along with content.
         * Sticky pins it visually at the bottom regardless of scroll.
         *
         * Why the v4.7.71 sticky-removal doesn't apply here: that
         * removal was tied to padding-bottom interaction. The current
         * body has no padding-bottom (replaced by the ::after spacer
         * in v4.7.72). The footer wrapper sticks cleanly.
         *
         * The 2px white gap to the card border comes from:
         *  - bottom: 2px (footer leaves 2px below itself)
         *  - The body's ::after spacer (14px) provides scroll buffer
         *    above the footer, not below — body extends to the card
         *    edge, so the 2px below the footer IS the gap to the
         *    card's outer border. */
        /* v4.20.43 — Slider two-zone flex layout per James (2026-05-19):
         * "WHEN YOU SCROLL UP THE BUTTON IS NOT FIXED TO THE BOTTOM AND YOU
         * CAN SEE IN THE GAP THE VARIATIONS SCROLLING. IT JUMPS UP".
         *
         * v4.20.33's approach: body has overflow-y:auto, footer has
         * position:sticky bottom:2px with a mask gradient.
         * Two problems with that:
         *   (a) The mask gradient (linear-gradient transparent → black
         *       over 4px) made the top 4px of the footer SEE-THROUGH.
         *       Variations scrolling past in the body showed through
         *       that fade — exactly the "you can see in the gap"
         *       behaviour James reported.
         *   (b) position:sticky inside an overflow-y:auto flex item on
         *       iOS Safari is the same scenario v4.7.71 abandoned. The
         *       footer doesn't reliably pin; it can "jump" as scroll
         *       momentum settles.
         *
         * v4.20.43 fix: body is now the flex container with
         * overflow:hidden (no scrolling on the body itself). A new inner
         * wrapper .fx-eor-slider-scroll holds title + variations and
         * IS the scroll container. .fx-eor-slider-footer is a natural
         * flex-item at the bottom (flex: 0 0 auto, NO sticky, NO mask,
         * fully opaque white). The 2px white gap to the card border
         * comes from padding-bottom: 2px on the footer.
         *
         * This is the v4.7.71 "natural flex-flow positioning" idea —
         * but applied at the right level. The body no longer scrolls;
         * only the upper zone does. The footer can never "jump" because
         * it isn't part of any scrolling context. The iOS Safari sticky
         * quirks don't apply. The ::after spacer that v4.7.72 added for
         * iOS scroll buffer moves from the body to the scroll wrapper.
         *
         * Working logic preserved:
         *   - v4.20.34 qty stepper unconditional enable
         *   - v4.20.34 visual qty fallback in addSingleCard
         *   - v4.20.34 bulletproofed slider redirect to /basket/
         *   - v4.20.33 .fx-eor-slider-footer wrapper (CSS approach changed)
         *   - v4.7.72 ::after scroll buffer (moved to scroll wrapper)
         *   - All horizontal swipe gesture handling on the track
         *
         * Working logic DELETED: none. Only the v4.20.33 sticky+mask
         * approach (which never worked) is removed. */
        #fx-eor-top-slider .fx-eor-slider-scroll {
            flex: 1 1 auto;
            min-height: 0;
            overflow-y: auto;
            overflow-x: hidden;
            overscroll-behavior: contain;
            -webkit-overflow-scrolling: touch;
            scrollbar-width: thin;
            scrollbar-color: #85C727 transparent;
            /* v4.20.43 — Was pan-y. James (2026-05-20) reported swipe
             * gestures on the right side of the card didn't trigger
             * slider scroll. pan-y BLOCKED horizontal pan; the !important
             * rule below also covers this, but updating inline keeps
             * the declarations consistent. */
            touch-action: pan-y pan-x;
            display: flex;
            flex-direction: column;
        }
        #fx-eor-top-slider .fx-eor-slider-scroll::after {
            content: '';
            display: block;
            height: 6px;
            flex-shrink: 0;
        }
        #fx-eor-top-slider .fx-eor-slider-scroll::-webkit-scrollbar { width: 4px; }
        #fx-eor-top-slider .fx-eor-slider-scroll::-webkit-scrollbar-track { background: transparent; }
        #fx-eor-top-slider .fx-eor-slider-scroll::-webkit-scrollbar-thumb { background: #85C727; border-radius: 2px; }

        #fx-eor-top-slider .fx-eor-slider-footer {
            flex: 0 0 auto;
            background: #ffffff;
            display: flex;
            flex-direction: column;
            gap: 6px;
            padding-top: 6px;
            padding-bottom: 2px;
            /* 2px padding-bottom = the white gap between the Add button
             * and the card's outer border (the box edge) — exactly
             * what James asked for. */
        }
        #fx-eor-top-slider .fx-eor-slider-footer .fx-eor-bottom {
            margin: 0;
        }
        #fx-eor-top-slider .fx-eor-slider-footer .fx-eor-card-add {
            margin: 0;
        }

        /* v4.20.43 — CURRENTLY VIEWING strip slides up with the title.
         * James (2026-05-20): "make the currently viewing strip slide up
         * when you slide that mini product. The problem with that scroll
         * is when the top of the frame doesn't move up it gets right in
         * the way of selecting the pills".
         *
         * Background: the strip is rendered via CSS ::before pseudo on
         * .fx-eor-body (line 10942) so the same render works for grid /
         * accordion / slider WITHOUT needing the strip in HTML.
         * v4.20.35 moved the slider's scrollable content into a NEW
         * inner wrapper .fx-eor-slider-scroll. The ::before sits on the
         * BODY, not the scroll wrapper — so it stayed pinned at the top
         * while title + variations scrolled away. Result: when the user
         * scrolled to reach lower pills, the strip remained right where
         * the pills used to be — exactly the obstruction James reported.
         *
         * Fix: slider-only override — hide the original body::before
         * for slider cards, and emit the SAME strip as a ::before on
         * .fx-eor-slider-scroll instead. Since the scroll wrapper IS
         * the vertical scroll container, its ::before becomes the first
         * flex-item inside the scrolling region and scrolls up with the
         * rest of the upper content.
         *
         * Grid + accordion render paths are UNTOUCHED — they don't have
         * .fx-eor-slider-scroll, so the original body::before continues
         * to fire there. Only #fx-eor-top-slider scope changes. */
        #fx-eor-top-slider .fx-eor-frontcard[data-current="1"] .fx-eor-body::before {
            display: none !important;
        }
        #fx-eor-top-slider .fx-eor-frontcard[data-current="1"] .fx-eor-slider-scroll::before {
            content: "\2605 Currently viewing";
            display: inline-block;
            background: #2A6132;
            color: #fff;
            font-size: 10px;
            letter-spacing: 1px;
            text-transform: uppercase;
            padding: 4px 10px;
            border-radius: 3px;
            font-weight: 600;
            font-family: 'Jost', sans-serif;
            align-self: flex-start;
            margin-bottom: 6px;
            flex-shrink: 0;
        }

        /* v4.20.43 — Swipe detection on the full right side of slider
         * cards. James (2026-05-20): "add slide detection onto the whole
         * of the righthand side of the slider product so that it detects
         * slide when you are focussed on the pills titles or add to cart
         * button or quantity. So that side of the pane needs to have
         * both slide and click detection on mobile".
         *
         * Background: .fx-eor-slider-scroll had touch-action: pan-y
         * (v4.20.35) and .fx-eor-body had touch-action: pan-y (v4.7.72).
         * Both EXPLICITLY BLOCK horizontal pan gestures. When the user's
         * finger lands on the inner wrapper or any of its descendants
         * (pills, qty buttons, Add button, qty input, title), the
         * browser sees pan-y and DROPS the horizontal pan entirely —
         * even though the parent track has overflow-x:auto + pan-x and
         * could have scrolled.
         *
         * Native browser scroll-chaining requires touch-action on EACH
         * intermediate element to ALLOW the gesture direction. pan-y
         * means "only vertical" — horizontal motion is forbidden.
         *
         * Fix: change pan-y to "pan-y pan-x" on both .fx-eor-body and
         * .fx-eor-slider-scroll. The browser routes by DOMINANT motion
         * direction: vertical-dominant pan → inner scroll wrapper
         * (overflow-y:auto wins); horizontal-dominant pan → bubbles up
         * to the track (overflow-x:auto wins). Stationary taps still
         * fire click events on pills/buttons normally — the threshold
         * is ~5px of movement before the browser commits to "scroll".
         *
         * Also explicit pan-y pan-x on interactive children. Buttons
         * default to touch-action: auto which already allows panning,
         * but being explicit removes any ambiguity from older browsers
         * (or future CSS resets that might set touch-action: manipulation
         * on buttons globally). */
        #fx-eor-top-slider .fx-eor-body,
        #fx-eor-top-slider .fx-eor-slider-scroll,
        #fx-eor-top-slider .fx-eor-slider-footer,
        #fx-eor-top-slider .fx-eor-var-pill,
        #fx-eor-top-slider .fx-eor-card-add,
        #fx-eor-top-slider .fx-eor-q-up,
        #fx-eor-top-slider .fx-eor-q-down,
        #fx-eor-top-slider .fx-eor-q-input,
        #fx-eor-top-slider .fx-eor-name-link,
        #fx-eor-top-slider .fx-eor-name-link h4,
        #fx-eor-top-slider .fx-eor-variations,
        #fx-eor-top-slider .fx-eor-bottom,
        #fx-eor-top-slider .fx-eor-price,
        #fx-eor-top-slider .fx-eor-qty {
            touch-action: pan-y pan-x !important;
        }

        /* Title full-width on slider — v4.20.24 retired the multi-select
         * tick column (.fx-eor-check-col display:none), so the
         * padding-right:38px (global, line 10907) + padding-right:32px
         * (slider scope below, line 11877) that reserved space for the
         * absolute-positioned tick overlay are obsolete. James
         * (2026-05-19): "THE TEXT IS WRAPPPED EARLY IN THE TITLE AND IT
         * SHOULD BE FULL WIDTH NOW THE TICK BOX HAS BEEN REMOVED". Zero
         * both for the slider so the title gets the full body width to
         * wrap into. Grid + accordion scopes untouched — those layouts
         * may want the right padding for their own reasons. */
        #fx-eor-top-slider .fx-eor-body h4 {
            padding-right: 0 !important;
        }
        #fx-eor-top-slider .fx-eor-name-link {
            padding-right: 0 !important;
        }

        /* v4.7.65: Tighten "ADD TO BASKET" button so the text fits inside the
         * narrower slider body cell. Accordion's button has generous padding
         * suitable for full-width body; slider body is ~half width so we
         * reduce padding + font slightly. white-space: nowrap + width: 100%
         * keep the button predictable.
         *
         * v4.7.71: position: sticky removed (was added in v4.7.69, removed
         * per James). On iOS Safari the sticky bottom: 0 anchor interacted
         * badly with the body's padding-bottom (content-box bottom anchor
         * vs padding-box rendering caused the bottom half of the button
         * to be visually clipped). Reverted to natural flex-flow positioning
         * — button is the last child in the body, stacks at the bottom of
         * content for short cards, scrolls naturally into view at max-scroll
         * for long-content cards. */
        #fx-eor-top-slider .fx-eor-card-add {
            padding: 10px 6px;
            font-size: 10px;
            letter-spacing: 0.5px;
            width: 100%;
            box-sizing: border-box;
            white-space: nowrap;
            overflow: hidden;
            text-overflow: ellipsis;
        }

        /* v4.7.65: Tighten the "★ CURRENTLY VIEWING" badge font so it fits in
         * the narrower slider card-top strip alongside the check-col overlay. */
        #fx-eor-top-slider .fx-eor-current-badge-text {
            font-size: 9px;
            letter-spacing: 0.4px;
        }

        /* Body becomes a vertical scroll viewport — content above the
         * fold is name + variations + price/qty, with Add to Basket
         * appearing as the user scrolls the body up (for products with
         * many variations). min-width: 0 lets the body grid cell shrink
         * to its 1fr allocation regardless of content min-content.
         *
         * v4.7.66: added `height: 100%` + `max-height: 100%` so the body
         * fills the grid cell (now explicitly sized in card-main) and
         * scroll engages when content > cell height. Previously the body
         * was content-sized which meant scroll never triggered.
         *
         * v4.7.68: `justify-content: flex-start` overrides accordion's
         * `justify-content: space-between` (set on `.fx-eor-body` in the
         * shared CSS at line ~2700). Space-between distributes empty
         * vertical space between children — for SHORT body content (simple
         * product with no variations) the title pinned to the top + button
         * pinned to the bottom of the cell looked unbalanced with a huge
         * gap in the middle. Flex-start stacks title/pills/price/button
         * with consistent 4px gap (from mobile media query rule), button
         * sits right below the price row. Cleaner, more predictable. */
        #fx-eor-top-slider .fx-eor-body {
            /* v4.20.43 — Body is now the non-scrolling flex container.
             * Scroll moved to inner .fx-eor-slider-scroll wrapper (see
             * v4.20.43 block above). The body's job is to host two
             * vertically stacked flex items: the scroll wrapper (flex:1)
             * and the footer (flex:0 0 auto). The previous
             * overflow-y:auto on the body is GONE — that role is now the
             * inner wrapper's. This eliminates the v4.20.33 sticky+mask
             * problems described in detail in the v4.20.43 comment
             * above; the footer can never "jump" because it isn't in any
             * scrolling context, and there's no mask gradient leaking
             * variations behind the footer.
             *
             * touch-action: pan-y retained — the inner scroll wrapper
             * also has touch-action:pan-y; nesting them is fine and
             * doubly ensures iOS Safari commits vertical gestures to
             * the scroll wrapper rather than the parent horizontal
             * track. */
            overflow: hidden;
            min-height: 0;
            min-width: 0;
            height: 100%;
            max-height: 100%;
            box-sizing: border-box;
            justify-content: flex-start;
            /* v4.20.43 — Was pan-y. See v4.20.43 swipe-detection comment
             * above the touch-action !important rule for full reasoning.
             * pan-y pan-x lets the browser route both vertical (to the
             * inner scroll wrapper) and horizontal (to the parent
             * track) pan gestures depending on dominant motion direction. */
            touch-action: pan-y pan-x;
        }
        /* v4.20.43 — body::after spacer REMOVED. The 14px scroll buffer
         * that v4.7.72 added for the iOS Safari padding-bottom bug now
         * lives on .fx-eor-slider-scroll::after (6px is enough since
         * the footer is fully visible and no longer needs to be scrolled
         * past). The body itself doesn't scroll anymore so it doesn't
         * need the buffer. */
        /* Slider body's own webkit-scrollbar rules removed — no scroll
         * on the body now. Scrollbar styling moved to
         * .fx-eor-slider-scroll above. */

        /* Mobile-only display (slider hidden on desktop where accordion is enough) */
        @media (max-width: 700px) {
            .fx-eor-top-slider-wrap { display: block; }
        }

        /* v4.19.32 — REVERTED v4.19.22 mobile slider tap-to-expand.
         * The collapse-to-image-only + tap-to-reveal pattern was breaking
         * the mobile experience (controls invisible, expand didn't restore
         * them reliably). Restored the pre-v4.19.22 behaviour where slider
         * cards show every control by default — matching how accordion
         * cards render. setupCard() wires the same behaviours for both
         * slider and accordion cards, so the slider gets tick / pills /
         * qty / Add automatically without needing the expand mechanism.
         * The variation-sync multi-current-card fix from v4.19.22 is
         * preserved (that lives in applyVariationToAllCurrentCards). */
        