/* =============================================================
   Bundle Boss — Frontend Styles
   ============================================================= */

/* Theme tokens. Override these (e.g. in Customizer → Additional CSS)
   to brand the whole configurator — no need to touch rules below.
   The five that matter most for branding: --bb-accent, --bb-ink,
   --bb-surface, --bb-border, --bb-muted. :root, not the form, because
   a few pieces (cart links, price tweaks) render outside the form. */
:root {
    --bb-ink: #111827;            /* headline text + the summary bar */
    --bb-ink-soft: #1f2937;
    --bb-text: #374151;
    --bb-muted: #6b7280;
    --bb-muted-light: #9ca3af;
    --bb-surface: #fff;           /* cards */
    --bb-surface-faint: #f9fafb;
    --bb-surface-alt: #f3f4f6;
    --bb-summary-bg: #f3f4f6;     /* sticky summary bar — light; rebrandable */
    --bb-border: #e5e7eb;
    --bb-border-strong: #d1d5db;
    --bb-accent: #7c3aed;         /* stepper pills, active highlights */
    --bb-accent-bg: #f5f3ff;
    --bb-accent-deep: #4c1d95;
    --bb-link: #2271b1;           /* inline links + focus outlines */
    --bb-success: #166534;        /* "chosen" confirmations */
    --bb-success-bg: #f0fdf4;
    --bb-success-border: #bbf7d0;
    --bb-success-bright: #00a32a; /* completed-step markers */
    --bb-warn: #b45309;           /* low-stock badges */
    --bb-warn-bg: #fffbeb;
    --bb-warn-bg-strong: #fef3c7;
    --bb-warn-border: #fde68a;
    --bb-warn-accent: #fcd34d;
    --bb-warn-deep: #92400e;
    --bb-error: #b91c1c;
}

.bundle-boss-form {
    margin-top: 2em;
    font-family: inherit;
    color: var(--bb-ink);
}

/* The hidden attribute must always win inside the configurator. Our
   own display rules (flex/grid) outrank the browser's [hidden] default,
   and only some themes (e.g. Storefront) re-assert it with !important —
   on Astra/Blocksy/TT25 the pager and chooser grid leaked through. */
.bundle-boss-form [hidden] {
    display: none !important;
}

/* ── Discount tier pills ─────────────────────────────────────── */

.bb-tiers {
    display: flex;
    flex-wrap: wrap;
    align-items: center;
    gap: 8px;
    margin-bottom: 1.75em;
}

.bb-tier-pill {
    display: inline-block;
    padding: 5px 14px;
    background: var(--bb-success-bg);
    color: #15803d;
    border: 1px solid var(--bb-success-border);
    border-radius: 100px;
    font-size: 0.8em;
    font-weight: 600;
    white-space: nowrap;
    letter-spacing: 0.01em;
}

/* ── Item list / grid container ──────────────────────────────── */

.bundle-items {
    display: flex;
    flex-direction: column;
    gap: 16px;
    /* Clearance for the sticky summary bar: the last item rests above the
       bar's docked position instead of flush against it. */
    margin-bottom: 16px;
    padding-bottom: 18px;
}

/* So keyboard / anchored scrolling parks an item above the pinned summary
   bar rather than tucked behind it. */
.bundle-item {
    scroll-margin-bottom: 92px;
}

/* Grid must size against its CONTAINER, not the viewport — themes put
   product content in columns of wildly different widths. The form is
   the query container; the rules below adapt the grid to it. */
.bundle-boss-form {
    container-type: inline-size;
}

.bundle-items.bundle-layout-grid {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(240px, 1fr));
    gap: 16px;
    /* Each card sizes to its own content; without this, grid's default
       align-items:stretch pads a short card (e.g. an optional simple item) to
       match the tallest card in its row, leaving a large empty area below it. */
    align-items: start;
}

/* ── Item card ───────────────────────────────────────────────── */

.bundle-item {
    background: var(--bb-surface);
    border: 1px solid var(--bb-border);
    border-radius: 16px;
    /* No overflow:hidden — it would disable the sticky card header.
       Corner rounding is applied to the children instead. */
    box-shadow: 0 1px 3px rgba(0,0,0,0.06), 0 4px 12px rgba(0,0,0,0.04);
    transition: box-shadow 0.2s, border-color 0.2s;
}

.bundle-item > :first-child {
    border-radius: 15px 15px 0 0;
}

.bundle-item > :last-child {
    border-radius: 0 0 15px 15px;
}

.bundle-item--collapsed > .bb-card-header {
    border-radius: 15px;
}

.bundle-item:hover {
    border-color: var(--bb-border-strong);
    box-shadow: 0 4px 24px rgba(0,0,0,0.1);
}

.bundle-item.bundle-item--done {
    border-color: #a7f3d0;
    box-shadow: 0 1px 3px rgba(0,0,0,0.06), 0 4px 12px rgba(16,185,129,0.08);
}

/* ── Card header ─────────────────────────────────────────────── */

.bb-card-header {
    display: flex;
    align-items: center;
    flex-wrap: wrap;
    row-gap: 6px;
    gap: 12px;
    padding: 14px 18px;
    /* Sticky: the collapse control stays in reach while scrolling a tall
       open card — no scrolling back up to fold it. */
    position: sticky;
    top: 0;
    z-index: 5;
    background: var(--bb-surface);
    border-bottom: 1px solid var(--bb-surface-alt);
    cursor: pointer;
    user-select: none;
}

/* Sticky needs an unclipped ancestor chain. Themes commonly clearfix the
   product container with overflow:hidden, and wrap the page in an
   overflow-x:hidden element (which silently becomes a scroll container and
   swallows position:sticky). Unclip both, scoped to bundle product pages,
   and replace the clearfix the safe way. */
.single-product div.product.product-type-bundle {
    overflow: visible !important;
}

.single-product div.product.product-type-bundle::after {
    content: '';
    display: block;
    clear: both;
}

.single-product .site,
.single-product #page {
    overflow: visible !important;
}

/* Clear the WP admin bar for logged-in users. */
body.admin-bar .bb-card-header {
    top: 32px;
}

@media screen and (max-width: 782px) {
    body.admin-bar .bb-card-header {
        top: 46px;
    }
}

.bb-card-header-left {
    display: flex;
    align-items: center;
    gap: 10px;
    /* Keep a sensible width so, in a narrow card (Grid page layout), the
       Optional toggle wraps to its own line below the name instead of crushing
       it. In a normal-width card everything stays on one line. */
    flex: 1 1 11rem;
    min-width: 11rem;
}

/* The title/meta column must be allowed to shrink, so a long item name
   truncates (ellipsis) instead of overflowing onto the Optional toggle in a
   narrow card (the Grid page layout puts items in half-width columns). */
.bb-card-header-left > div {
    min-width: 0;
}

/* Chevron arrow */
.bb-card-chevron {
    flex-shrink: 0;
    width: 0;
    height: 0;
    border-left: 4.5px solid transparent;
    border-right: 4.5px solid transparent;
    border-top: 5.5px solid var(--bb-border-strong);
    transition: transform 0.18s, border-color 0.15s;
}

.bb-card-header:hover .bb-card-chevron {
    border-top-color: var(--bb-muted-light);
}

.bundle-item:not(.bundle-item--collapsed) .bb-card-chevron {
    transform: rotate(180deg);
}

.bb-step {
    flex-shrink: 0;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 26px;
    height: 26px;
    background: var(--bb-ink);
    color: var(--bb-surface);
    border-radius: 50%;
    font-size: 0.75em;
    font-weight: 800;
    letter-spacing: -0.01em;
    transition: background 0.2s;
}

.bundle-item--done .bb-step {
    background: #10b981;
}

.bb-item-name {
    font-size: 0.95em;
    font-weight: 700;
    color: var(--bb-ink);
    overflow-wrap: break-word;
    max-width: 280px;
    line-height: 1.3;
}

/* In the accordion card header the title has the whole row to use, so let a
   long slot name spread across the available width and split evenly instead of
   wrapping early into a ragged orphan line ("...you / would like"). The 280px
   cap on the base rule is for the narrow Grid tile, not this full-width header.
   text-wrap: balance is progressive — older browsers just fall back to normal
   wrapping. */
.bb-card-header .bb-item-name {
    max-width: none;
    text-wrap: balance;
}

.bb-item-meta {
    font-size: 0.75em;
    color: var(--bb-muted-light);
    font-weight: 400;
    margin-top: 2px;
}

/* ── Optional toggle ─────────────────────────────────────────── */

.bb-toggle {
    display: inline-flex;
    align-items: center;
    gap: 7px;
    cursor: pointer;
    user-select: none;
    flex-shrink: 0;
}

.bb-toggle input[type="checkbox"] {
    position: absolute;
    opacity: 0;
    width: 0;
    height: 0;
}

.bb-toggle-track {
    position: relative;
    width: 36px;
    height: 20px;
    background: var(--bb-border-strong);
    border-radius: 10px;
    flex-shrink: 0;
    transition: background 0.2s;
}

.bb-toggle input:checked ~ .bb-toggle-track {
    background: #10b981;
}

.bb-toggle-thumb {
    position: absolute;
    top: 3px;
    left: 3px;
    width: 14px;
    height: 14px;
    background: var(--bb-surface);
    border-radius: 50%;
    box-shadow: 0 1px 3px rgba(0,0,0,0.2);
    transition: transform 0.18s;
}

.bb-toggle input:checked ~ .bb-toggle-track .bb-toggle-thumb {
    transform: translateX(16px);
}

.bb-toggle-text {
    font-size: 0.75em;
    font-weight: 600;
    color: var(--bb-muted-light);
    text-transform: uppercase;
    letter-spacing: 0.07em;
}

/* ── Card body ───────────────────────────────────────────────── */

.bb-card-body.bundle-item-inner {
    display: flex;
    align-items: stretch;
    gap: 0;
    padding: 0;
}

.bundle-layout-grid .bb-card-body.bundle-item-inner {
    flex-direction: column;
}

/* ── Product image ───────────────────────────────────────────── */

.bb-product-image.bundle-item-image {
    flex-shrink: 0;
    width: 116px;
    align-self: stretch;      /* container fills card height for the border */
    border-right: 1px solid var(--bb-surface-alt);
    background: var(--bb-surface-faint);      /* fills the gap below the fixed-height image */
    overflow: hidden;
}

.bb-product-image.bundle-item-image img {
    display: block;
    width: 116px;
    height: 116px;            /* fixed — no longer stretches with card */
    object-fit: cover;
    object-position: center top;
    border-radius: 0;
}

/* A chosen design that carries a full-size image invites a click to enlarge
   (the thumbnail is small / cropped). A quiet magnifier badge signposts it. */
.bb-choice-thumb-img[data-full] {
    cursor: zoom-in;
}
/* The magnifier badge is pinned to the image's TOP-right — the image is
   top-aligned in its container, which can stretch far taller than the image
   on a tall item, so a bottom-anchored badge would float off in the gap. It
   is revealed on hover / focus (so it isn't ever-present on every thumbnail),
   and stays visible on touch where there is no hover to reveal it. */
.bb-product-image:has( .bb-choice-thumb-img[data-full] ) {
    position: relative;
}
.bb-product-image:has( .bb-choice-thumb-img[data-full] )::after {
    content: "";
    position: absolute;
    top: 6px;
    right: 6px;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background: rgba( 17, 24, 39, 0.7 ) center / 11px 11px no-repeat
        url( "data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='white' stroke-width='2.4' stroke-linecap='round'%3E%3Ccircle cx='10.5' cy='10.5' r='6.5'/%3E%3Cline x1='15.5' y1='15.5' x2='21' y2='21'/%3E%3C/svg%3E" );
    pointer-events: none;
    opacity: 0;
    transition: opacity 0.12s;
}
.bb-product-image:has( .bb-choice-thumb-img[data-full] ):hover::after,
.bb-product-image:has( .bb-choice-thumb-img[data-full]:focus-visible )::after {
    opacity: 0.92;
}
@media ( hover: none ) {
    .bb-product-image:has( .bb-choice-thumb-img[data-full] )::after {
        opacity: 0.92;
    }
}
.bb-choice-thumb-img[data-full]:hover {
    filter: brightness( 0.94 );
}
.bb-choice-thumb-img[data-full]:focus-visible {
    outline: 2px solid var(--bb-accent);
    outline-offset: 2px;
}

/* Design lightbox: a dismissable, focus-trapped overlay for the full image. */
.bb-lightbox {
    position: fixed;
    inset: 0;
    z-index: 100000;
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 24px;
    background: rgba( 17, 24, 39, 0.82 );
}
.bb-lightbox[hidden] {
    display: none;
}
.bb-lightbox-inner {
    position: relative;
    max-width: min( 92vw, 900px );
    max-height: 90vh;
}
.bb-lightbox-img {
    display: block;
    max-width: 100%;
    max-height: 90vh;
    width: auto;
    height: auto;
    border-radius: 8px;
    background: #fff;
    box-shadow: 0 12px 48px rgba( 0, 0, 0, 0.5 );
}
.bb-lightbox-close {
    position: absolute;
    top: -14px;
    right: -14px;
    width: 36px;
    height: 36px;
    padding: 0;
    border: none;
    border-radius: 50%;
    background: #fff;
    color: #111827;
    font-size: 22px;
    line-height: 36px;
    text-align: center;
    cursor: pointer;
    box-shadow: 0 2px 8px rgba( 0, 0, 0, 0.3 );
}
.bb-lightbox-close:hover {
    background: #f3f4f6;
}
body.bb-lightbox-open {
    overflow: hidden;
}
@media ( prefers-reduced-motion: no-preference ) {
    .bb-lightbox-img {
        animation: bb-lightbox-in 0.15s ease;
    }
    @keyframes bb-lightbox-in {
        from { transform: scale( 0.96 ); opacity: 0; }
        to   { transform: scale( 1 ); opacity: 1; }
    }
}

.bundle-layout-grid .bb-product-image.bundle-item-image {
    width: 100%;
    border-right: none;
    border-bottom: 1px solid var(--bb-surface-alt);
    /* The PANEL owns the shape; the image just fills it. One source of
       truth means every card in a row shares one image height. */
    aspect-ratio: 5 / 3;
    height: auto;
    max-height: 200px;
}

.bundle-layout-grid .bb-product-image.bundle-item-image img {
    width: 100%;
    height: 100%;
    aspect-ratio: auto;
    max-height: none;
    object-fit: cover;
    border-radius: 0;
}

/* Narrow container (theme sweep finding: Storefront/Astra product
   columns): a one-column "grid" of tall cards looks broken — degrade
   each card to the list presentation instead. Container queries are
   2023-baseline; older browsers just keep the capped-image cards. */
@container (max-width: 499px) {
    .bundle-items.bundle-layout-grid {
        grid-template-columns: 1fr;
    }
    .bundle-layout-grid .bb-card-body.bundle-item-inner {
        flex-direction: row;
    }
    .bundle-layout-grid .bb-product-image.bundle-item-image {
        width: 116px;
        border-right: 1px solid var(--bb-surface-alt);
        border-bottom: none;
    }
    .bundle-layout-grid .bb-product-image.bundle-item-image img {
        width: 116px;
        height: 116px;
        aspect-ratio: auto;
        max-height: none;
    }
}

/* A quantity grid needs the full card width to lay its colour tiles out as a
   wrapping block. In the list layout the image sits in a fixed 116px left
   column, which squeezes the tile grid to ~1 per row on a phone. When the form
   is narrow, stack the image on top so the grid gets the whole width (2–3
   tiles per row instead of one). Scoped with :has() so simple list items keep
   their compact thumbnail-beside-controls layout. */
@container (max-width: 499px) {
    .bb-card-body.bundle-item-inner:has( .bb-quantity-grid ) {
        flex-direction: column;
    }
    .bb-card-body.bundle-item-inner:has( .bb-quantity-grid ) .bb-product-image.bundle-item-image {
        width: 100%;
        border-right: none;
        border-bottom: 1px solid var(--bb-surface-alt);
    }
    .bb-card-body.bundle-item-inner:has( .bb-quantity-grid ) .bb-product-image.bundle-item-image img {
        width: 100%;
        height: auto;
        aspect-ratio: 5 / 3;
        max-height: 180px;
    }
}

/* ── Controls area ───────────────────────────────────────────── */

.bb-controls.bundle-item-details {
    flex: 1;
    min-width: 0;
    padding: 16px 18px;
}

/* ── Qty stepper ─────────────────────────────────────────────── */

.bb-qty-row.bundle-item-qty-control {
    display: flex;
    align-items: center;
    gap: 10px;
    margin-bottom: 14px;
}

.bb-qty-label {
    font-size: 0.75em;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.06em;
    color: var(--bb-muted-light);
}

.bb-stepper {
    display: inline-flex;
    align-items: stretch;
    border: 1px solid var(--bb-border);
    border-radius: 8px;
    overflow: hidden;
    background: var(--bb-surface);
}

.bb-qty-btn {
    width: 30px;
    background: none;
    border: none;
    cursor: pointer;
    font-size: 1em;
    font-weight: 600;
    color: var(--bb-muted);
    display: flex;
    align-items: center;
    justify-content: center;
    padding: 0;
    transition: background 0.1s, color 0.1s;
    line-height: 1;
}

.bb-qty-btn:hover {
    background: var(--bb-surface-alt);
    color: var(--bb-ink);
}

.bundle-item-qty-spinner {
    width: 36px !important;
    text-align: center !important;
    border: none !important;
    border-left: 1px solid var(--bb-border) !important;
    border-right: 1px solid var(--bb-border) !important;
    border-radius: 0 !important;
    padding: 5px 2px !important;
    font-size: 0.88em !important;
    font-weight: 700 !important;
    box-shadow: none !important;
    -moz-appearance: textfield !important;
    outline: none !important;
    background: var(--bb-surface) !important;
    color: var(--bb-ink) !important;
}

.bundle-item-qty-spinner::-webkit-inner-spin-button,
.bundle-item-qty-spinner::-webkit-outer-spin-button {
    -webkit-appearance: none;
    margin: 0;
}

/* Blanket guard: every BundleBoss stepper ships its own +/- buttons, so the
   browser's native number-input arrows must never appear alongside them, on
   any layout (per-unit, build-a-box, quantity grid, configurator). */
.bundle-boss-form input[type="number"]::-webkit-inner-spin-button,
.bundle-boss-form input[type="number"]::-webkit-outer-spin-button,
.bb-box input[type="number"]::-webkit-inner-spin-button,
.bb-box input[type="number"]::-webkit-outer-spin-button,
.bb-quantity-grid input[type="number"]::-webkit-inner-spin-button,
.bb-quantity-grid input[type="number"]::-webkit-outer-spin-button {
    -webkit-appearance: none;
    margin: 0;
}
.bundle-boss-form input[type="number"],
.bb-box input[type="number"],
.bb-quantity-grid input[type="number"] {
    -moz-appearance: textfield;
    appearance: textfield;
}

/* ── "All the same" toggle row ───────────────────────────────── */

.bb-same-all-row {
    display: flex;
    align-items: center;
    margin-bottom: 14px;
}

.bb-same-all-toggle {
    gap: 8px !important;
}

.bb-same-all-text {
    font-size: 0.8em;
    font-weight: 600;
    color: var(--bb-muted);
}

.bb-same-all-toggle input:checked ~ .bb-toggle-track {
    background: var(--bb-ink);
}

.bb-same-all-toggle input:checked ~ * ~ .bb-same-all-text {
    color: var(--bb-ink);
}

/* Synced units — collapsed visually but present in DOM for form submission */
.bundle-item-unit.bb-unit-synced {
    height: 0 !important;
    overflow: hidden !important;
    padding-top: 0 !important;
    margin-top: 0 !important;
    border-top: none !important;
    pointer-events: none;
}

/* ── Unit rows (variable products) ──────────────────────────── */

.bundle-item-units {
    display: flex;
    flex-direction: column;
    gap: 0;
}

.bundle-item-unit {
    padding: 0;
    background: transparent;
    border: none;
    border-top: 1px solid var(--bb-surface-alt);
    padding-top: 14px;
    margin-top: 14px;
}

.bundle-item-unit:first-child {
    border-top: none;
    padding-top: 0;
    margin-top: 0;
}

.bb-unit-label {
    font-size: 0.7em;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: #c4c9d4;
    margin-bottom: 10px;
}

/* ── Attribute group ─────────────────────────────────────────── */

.bb-attr-group.bundle-item-attribute {
    margin-bottom: 12px;
}

.bb-attr-group.bundle-item-attribute:last-of-type {
    margin-bottom: 0;
}

.bb-attr-name {
    display: block;
    font-size: 0.7em;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--bb-muted-light);
    margin-bottom: 7px;
}

/* Hidden native selects (pills mode) */
.bundle-attr-select {
    display: none !important;
}

/* Visible dropdown (dropdown mode) */
.bb-select-wrap {
    position: relative;
    display: inline-block;
}

.bb-select-wrap::after {
    content: '';
    position: absolute;
    right: 10px;
    top: 50%;
    transform: translateY(-50%);
    width: 0;
    height: 0;
    border-left: 4px solid transparent;
    border-right: 4px solid transparent;
    border-top: 5px solid var(--bb-muted-light);
    pointer-events: none;
}

.bb-select-wrap .bundle-attr-select.bb-dropdown {
    display: block !important;
    appearance: none;
    -webkit-appearance: none;
    padding: 7px 30px 7px 11px !important;
    background: var(--bb-surface) !important;
    border: 1px solid var(--bb-border) !important;
    border-radius: 8px !important;
    font-size: 0.88em !important;
    font-weight: 500 !important;
    color: var(--bb-ink) !important;
    cursor: pointer;
    transition: border-color 0.15s, box-shadow 0.15s;
    min-width: 140px;
    box-shadow: none !important;
    outline: none;
}

.bb-select-wrap .bundle-attr-select.bb-dropdown:focus {
    border-color: var(--bb-ink) !important;
    box-shadow: 0 0 0 3px rgba(17,24,39,0.08) !important;
}

/* ── Chip buttons ────────────────────────────────────────────── */

.bb-chips {
    display: flex;
    flex-wrap: wrap;
    gap: 6px;
}

.bb-chip {
    display: inline-block;
    padding: 5px 14px;
    background: var(--bb-surface);
    border: 1px solid var(--bb-border);
    border-radius: 100px;
    font-size: 0.82em;
    font-weight: 500;
    color: var(--bb-text);
    cursor: pointer;
    transition: border-color 0.12s, background 0.12s, color 0.12s;
    white-space: nowrap;
    line-height: 1.5;
    outline: none;
}

.bb-chip:hover {
    border-color: var(--bb-muted-light);
    color: var(--bb-ink);
    background: var(--bb-surface-faint);
}

.bb-chip.is-selected {
    background: var(--bb-ink);
    border-color: var(--bb-ink);
    color: var(--bb-surface);
    font-weight: 600;
}

.bb-chip.is-selected:hover {
    background: var(--bb-ink-soft);
    border-color: var(--bb-ink-soft);
}

/* The missing states: a chip is a control, so it carries the full set.
   Focus draws its own ring (themes love `outline: none` on buttons), and
   disabled reads as present-but-unavailable instead of vanishing. */
.bb-chip:focus-visible {
    outline: 2px solid var(--bb-accent, #ff6a53);
    outline-offset: 2px;
}

.bb-chip:disabled,
.bb-chip[aria-disabled="true"] {
    opacity: 0.45;
    cursor: not-allowed;
    pointer-events: none;
}

/* ── Colour swatch chips ──────────────────────────────────────────
   The gallery anatomy: the swatch VISUAL with the name (and the price
   where one is carried) standing beneath it, all one button. The dot
   owns the frame, the selected ring and the tick; the button is the
   whole tap target. The old hover tooltip is retired: a name that only
   exists on hover does not exist on a touch screen. */

.bb-chip--swatch {
    position: relative;
    display: inline-flex;
    flex-direction: column;
    align-items: center;
    gap: 5px;
    width: auto;
    padding: 0;
    border: 0;
    background: none;
    text-align: center;
    flex-shrink: 0;
}

.bb-swatch-dot {
    position: relative;
    display: block;
    width: 40px;
    height: 40px;
    border-radius: 50%;
    /* subtle inner edge so white/very light swatches stay visible */
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.12), 0 0 0 1px var(--bb-border);
    pointer-events: none;
}

.bb-chip--swatch:hover {
    background: none;
    border: 0;
}

.bb-chip--swatch:hover .bb-swatch-dot {
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.12), 0 0 0 1px var(--bb-muted-light);
}

.bb-chip--swatch.is-selected,
.bb-chip--swatch.is-selected:hover {
    background: none;
    border: 0;
}

/* the gallery's double ring: a gap of surface, then the ink */
.bb-chip--swatch.is-selected .bb-swatch-dot {
    box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.12), 0 0 0 2px var(--bb-surface), 0 0 0 4px var(--bb-ink);
}

/* the visible words beneath the swatch */
.bb-sw-name {
    display: block;
    font-size: 12px;
    font-weight: 600;
    line-height: 1.2;
    color: var(--bb-text);
}

.bb-chip--swatch.is-selected .bb-sw-name {
    color: var(--bb-ink);
    font-weight: 700;
}

.bb-chip--swatch .bb-chip-delta {
    display: block;
    font-size: 11.5px;
    color: var(--bb-muted);
}

/* checkmark badge on the selected swatch */
.bb-chip--swatch.is-selected .bb-swatch-dot::after {
    content: '';
    position: absolute;
    right: -3px;
    bottom: -3px;
    width: 13px;
    height: 13px;
    background: var(--bb-ink) url('data:image/svg+xml;utf8,<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 16 16"><path fill="none" stroke="white" stroke-width="2.4" stroke-linecap="round" stroke-linejoin="round" d="M3.5 8.5l3 3 6-7"/></svg>') center / 9px no-repeat;
    border: 2px solid var(--bb-surface);
    border-radius: 50%;
}

/* ── Chip theme guard. The same doctrine as the stepper guard: restate the
      geometry with !important inside our own selectors, so a Flatsome-grade
      global button rule (min-widths, tall heights, trailing margins, pill
      shadows, uppercase) cannot reshape a chip. Colour stays negotiable;
      geometry does not. The selected-swatch padding is re-pinned last so the
      guard cannot flatten the 2px selected inset. ─────────────────────────── */
.bb-chips .bb-chip {
    width: auto !important;
    min-width: 0 !important;
    height: auto !important;
    min-height: 0 !important;
    margin: 0 !important;
    padding: 5px 14px !important;
    border-radius: 100px !important;
    box-shadow: none !important;
    line-height: 1.5 !important;
    font-size: 0.82em !important;
    text-transform: none !important;
    letter-spacing: normal !important;
}

.bb-chips .bb-chip--swatch {
    width: auto !important;
    height: auto !important;
    padding: 0 !important;
    border: 0 !important;
    border-radius: 0 !important;
    background: none !important;
    box-shadow: none !important;
}

.bb-chips .bb-chip--swatch .bb-swatch-dot {
    width: 40px !important;
    height: 40px !important;
}

/* ── Swatch rows read as a grid of colours ────────────────────────
   Each swatch chip used to be as wide as its own name, so a long name
   ("Moonstone Blue") shoved its dot out of line with the row above and the
   dots read ragged. Equal-width columns instead: every dot centres on a fixed
   grid, and a long name wraps under its dot rather than stretching the chip.
   Scoped with :has() to the dot rows, so text and card rows keep their flow;
   an ancient browser without :has() simply keeps the old flow.

   NAMED dots only, which is the case this was written for. A swatch drawn
   without its name, as a bundle draws one, has nothing to wrap and nothing to
   line up, so the column stretched a 40px dot's chip to the full 105px cell.
   The dot stayed put and the SELECTED state grew to the cell, which read as
   one highlight covering two colours. Nameless dots keep the plain flow and
   hug themselves. */
.bb-chips:has(.bb-chip--swatch .bb-sw-name) {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(86px, 1fr));
    gap: 16px 6px;
    align-items: start;
}
.bb-chips:has(.bb-chip--swatch .bb-sw-name) .bb-chip--swatch {
    width: 100% !important;
}
.bb-chips:has(.bb-chip--swatch .bb-sw-name) .bb-sw-name {
    overflow-wrap: anywhere;
}

/* ── Photo-card rows read as an even grid too ─────────────────────
   Same story as the swatch dots: a card used to be as wide as its own name, so
   "Orchard Ember" made a wider box than "Golden Hour" and the row wrapped ragged
   (five then three). Equal-width columns instead: every card fills its cell, so
   they are one size and split into aligned columns. Scoped with :has() to card
   rows only; the photo already fills the card at a fixed height, so it follows. */
.bb-chips:has(.bb-chip--card) {
    display: grid;
    grid-template-columns: repeat(auto-fill, minmax(104px, 1fr));
    gap: 12px 10px;
    align-items: start;
}
.bb-chips:has(.bb-chip--card) .bb-chip--card {
    width: 100% !important;
}
.bb-chips:has(.bb-chip--card) .bb-cc-name {
    overflow-wrap: anywhere;
}

/* ── Unit footer (variation image + price + stock) ───────────── */

.bb-unit-footer {
    display: flex;
    align-items: center;
    gap: 8px;
    margin-top: 10px;
    min-height: 20px;
}

.bb-unit-actions { margin-top: 10px; }



/* In the cards layout the actions row spans the card width. */
.bb-units-cards .bb-unit-actions { flex: 0 0 100%; margin-top: 6px; }

.bb-var-thumb.bundle-unit-thumb img {
    width: 32px;
    height: 32px;
    object-fit: cover;
    border-radius: 6px;
    border: 1px solid var(--bb-border);
}

.bb-unit-price.bundle-item-price {
    font-size: 0.92em;
    font-weight: 700;
    color: var(--bb-ink);
    margin-left: auto;
}

/* ── Variant Quantity Grid (per-unit table layout) ───────────────
   Opt-in via the bundle's "Multiple items: Table" setting. Purely a
   re-layout of the same per-unit controls: the inputs, the classes the
   JS hooks onto, and the POSTed payload are identical to the stacked
   layout above, so the configurator JS and the cart/order flow are
   unaffected. */

.bundle-item-units.bb-units-table {
    display: table;
    /* Size columns to their content and only fill the column when there is
       room (min-width). With a plain width:100% the table compressed to the
       column and clipped the rightmost cells (Price/personalisation) once the
       attributes + columns got wide; now it overflows into the scroll wrap
       instead. Mobile reflows this to blocks, where min-width:100% is inert. */
    width: auto;
    min-width: 100%;
    border-collapse: collapse;
    margin: 4px 0 0;
}

.bb-units-table thead th {
    text-align: left;
    font-size: 0.7em;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--bb-muted);
    padding: 0 12px 8px;
    border-bottom: 2px solid var(--bb-border);
    white-space: nowrap;
}

.bb-units-table thead .bb-col-num { width: 1%; text-align: center; }
.bb-units-table thead .bb-col-thumb { width: 44px; }
.bb-units-table thead .bb-col-price { text-align: right; }

/* Neutralise the stacked-stack spacing; rows separate via cell borders. */
.bundle-item-units.bb-units-table .bundle-item-unit {
    border-top: none;
    padding-top: 0;
    margin-top: 0;
}

.bb-units-table tbody td {
    padding: 12px;
    vertical-align: middle;
    border-top: 1px solid var(--bb-surface-alt);
}

.bb-units-table tbody tr:first-child td { border-top: none; }

.bb-units-table .bb-col-num {
    color: var(--bb-muted);
    font-weight: 700;
    font-size: 0.85em;
    text-align: center;
    white-space: nowrap;
}

.bb-units-table .bb-cell-attr .bb-chips { margin: 0; }
.bb-units-table .bb-cell-attr .bb-attr-name { display: none; } /* the label lives in the header */

.bb-units-table .bb-col-thumb { text-align: center; }
.bb-units-table .bb-col-thumb .bb-var-thumb.bundle-unit-thumb img { margin: 0 auto; }

.bb-units-table .bb-col-price { text-align: right; white-space: nowrap; }
.bb-units-table .bb-col-price .bb-unit-price.bundle-item-price { margin-left: 0; }
.bb-units-table .bb-col-price .bb-stock-badge { margin-top: 4px; }

/* "All the same" collapses the duplicate rows (they still submit). */
.bundle-item-units.bb-units-table .bundle-item-unit.bb-unit-synced {
    display: none !important;
}

/* Many-column (personalisation) tables: scroll sideways rather than crush cells. */
.bb-units-table-wrap { overflow-x: auto; }

/* Personalisation columns: label in the header, bare input in each cell. */
.bb-units-table .bb-col-addon { min-width: 7.5rem; }
.bb-units-table .bb-col-fee { font-weight: 600; color: var(--bb-muted); white-space: nowrap; }
.bb-units-table .bb-cell-addon { vertical-align: middle; }
.bb-units-table .bb-cell-addon .bb-addon-input { width: 100%; min-width: 6.5rem; box-sizing: border-box; }
.bb-units-table .bb-cell-addon .bb-addon-upload { display: flex; align-items: center; gap: 6px; min-width: 9rem; }
.bb-units-table .bb-cell-attr .bb-select-wrap { min-width: 7rem; }

/* Mobile: reflow the table back to the stacked cards used everywhere else,
   so small screens keep the proven layout. */
@media (max-width: 640px) {
    .bundle-item-units.bb-units-table,
    .bb-units-table tbody,
    .bb-units-table tr,
    .bb-units-table td {
        display: block;
        width: auto;
    }
    .bb-units-table thead { display: none; }
    .bb-units-table tbody tr.bundle-item-unit {
        border: 1px solid var(--bb-border);
        border-radius: 10px;
        padding: 12px 14px;
        margin-top: 12px;
    }
    .bb-units-table tbody tr.bundle-item-unit:first-child { margin-top: 0; }
    .bb-units-table tbody td {
        border-top: none;
        padding: 6px 0;
        text-align: left;
    }
    .bb-units-table td[data-label]:not([data-label=""])::before {
        content: attr(data-label);
        display: block;
        font-size: 0.7em;
        font-weight: 700;
        text-transform: uppercase;
        letter-spacing: 0.08em;
        color: var(--bb-muted);
        margin-bottom: 4px;
    }
    .bb-units-table .bb-col-num {
        text-align: left;
        color: var(--bb-ink);
        border-bottom: 1px solid var(--bb-surface-alt);
        padding-bottom: 8px !important;
    }
    .bb-units-table .bb-col-num::before { content: "Item "; }
    .bb-units-table .bb-col-thumb,
    .bb-units-table .bb-col-price { text-align: left; }
}

/* Same reflow, keyed to the CONTAINER width instead of the viewport. A narrow
   theme product column on desktop (Storefront, ColorMag, boxed/block themes)
   would otherwise overflow the per-unit table into a horizontal scroll and
   clip the Size/Price columns. Below ~510px of form width, stack each unit
   into a labelled card (same markup, same data-label ::before headers). The
   form is the query container (container-type above); wide columns keep the
   table. Breakpoint sits above the table's intrinsic min-width (~470px) and
   below typical wide columns (measured: Storefront 416 / OceanWP 367 reflow;
   Astra 552 / Kadence 595 keep the table). */
@container (max-width: 510px) {
    .bundle-item-units.bb-units-table,
    .bb-units-table tbody,
    .bb-units-table tr,
    .bb-units-table td {
        display: block;
        width: auto;
    }
    .bb-units-table thead { display: none; }
    .bb-units-table tbody tr.bundle-item-unit {
        border: 1px solid var(--bb-border);
        border-radius: 10px;
        padding: 12px 14px;
        margin-top: 12px;
    }
    .bb-units-table tbody tr.bundle-item-unit:first-child { margin-top: 0; }
    .bb-units-table tbody td {
        border-top: none;
        padding: 6px 0;
        text-align: left;
    }
    .bb-units-table td[data-label]:not([data-label=""])::before {
        content: attr(data-label);
        display: block;
        font-size: 0.7em;
        font-weight: 700;
        text-transform: uppercase;
        letter-spacing: 0.08em;
        color: var(--bb-muted);
        margin-bottom: 4px;
    }
    .bb-units-table .bb-col-num {
        text-align: left;
        color: var(--bb-ink);
        border-bottom: 1px solid var(--bb-surface-alt);
        padding-bottom: 8px !important;
    }
    .bb-units-table .bb-col-num::before { content: "Item "; }
    .bb-units-table .bb-col-thumb,
    .bb-units-table .bb-col-price { text-align: left; }
}

/* ── Compact cards (per-unit) ─────────────────────────────────────
   One card per unit; the attributes and per-unit personalisation fields flow
   side by side and wrap to the next line, so there is never a horizontal
   scroll and each card stays short. Swatches are kept. Same inputs/payload as
   every other layout. */

.bundle-item-units.bb-units-cards {
    display: flex;
    flex-direction: column;
    gap: 14px;
}

.bb-units-cards .bundle-item-unit {
    display: flex;
    flex-wrap: wrap;
    align-items: flex-start;
    gap: 12px 18px;
    border: 1px solid var(--bb-border);
    border-radius: 12px;
    padding: 14px 16px;
    margin: 0;
}

.bb-units-cards .bb-unit-label {
    flex: 0 0 100%;
    margin: 0;
}

.bb-units-cards .bundle-item-attribute.bb-attr-group {
    flex: 1 1 8rem;
    min-width: 7rem;
    margin: 0;
}

/* The personalisation collapsible sits on its own full-width row and stays an
   optional, collapsed-by-default "Personalise this item" toggle (not forced
   open on every card). */
.bb-units-cards .bb-personalise { flex: 0 0 100%; margin: 4px 0 0; }

.bb-units-cards .bb-unit-footer {
    flex: 0 0 100%;
    margin: 2px 0 0;
}

/* "All the same" collapses the duplicate cards (they still submit). */
.bundle-item-units.bb-units-cards .bundle-item-unit.bb-unit-synced {
    display: none !important;
}

@media (max-width: 560px) {
    .bb-units-cards .bundle-item-attribute.bb-attr-group { flex-basis: 100%; }
}

.bb-stock-badge.bundle-unit-stock {
    display: inline-block;
    padding: 2px 9px;
    background: var(--bb-warn-bg);
    color: var(--bb-warn);
    border: 1px solid var(--bb-warn-border);
    border-radius: 100px;
    font-size: 0.7em;
    font-weight: 700;
    white-space: nowrap;
}

/* ── Variant Quantity Grid ─────────────────────────────── */

/* The 80px bottom clearance served the pre-4.9 in-flow sticky bar; the order
   tray sits in the normal flow now, so the grid keeps a normal footing. */
.bb-quantity-grid { margin: 6px 0 2px; padding-bottom: 8px; }

/* Bounded runs get a visual fill under the "Choose a total of N" head; it
   mirrors the tier bar and turns green when the run is met. */
.bb-grid-bar {
    height: 8px;
    margin: 2px 0 12px;
    border-radius: 5px;
    background: var(--bb-summary-bg, #f3f4f6);
    overflow: hidden;
}
.bb-grid-bar-fill {
    display: block;
    height: 100%;
    width: 0;
    border-radius: 5px;
    background: var(--bb-accent, #7c3aed);
    transition: width 0.3s ease;
}
.bb-grid-ok .bb-grid-bar-fill { background: var(--bb-success-bright, #00a32a); }

/* The grid replaces the per-unit stepper and the "all the same" toggle. */
.bundle-item:has( .bb-quantity-grid ) .bb-qty-row,
.bundle-item:has( .bb-quantity-grid ) .bb-same-all-row { display: none; }

.bb-grid-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
    flex-wrap: wrap;
    margin-bottom: 8px;
}

.bb-grid-instruction { font-weight: 700; color: var(--bb-ink); }

.bb-grid-progress {
    font-size: 0.85em;
    font-weight: 700;
    color: var(--bb-ink-soft, #6b6256);
    padding: 2px 10px;
    border: 1px solid var(--bb-border);
    border-radius: 100px;
    white-space: nowrap;
}

.bb-quantity-grid.bb-grid-ok .bb-grid-progress {
    color: var(--bb-success, #2e7d32);
    border-color: var(--bb-success, #2e7d32);
}

.bb-quantity-grid.bb-grid-over .bb-grid-progress {
    color: var(--bb-warn, #b45309);
    border-color: var(--bb-warn-border, #e7cba0);
}

/* Colour tiles: a compact, wrapping grid; tap one to reveal its sizes inline,
   so 15 colours stay a tidy block with everything in front of you. */
.bb-grid-tiles {
    display: grid;
    grid-template-columns: repeat( auto-fill, minmax( 92px, 1fr ) );
    gap: 8px;
}

.bb-grid-tile {
    position: relative;
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 5px;
    padding: 9px 6px;
    border: 1px solid var(--bb-border);
    border-radius: 10px;
    background: var(--bb-surface, #fff);
    color: var(--bb-ink);
    font: inherit;
    line-height: 1.2;
    text-align: center;
    cursor: pointer;
}

.bb-grid-tile:hover { border-color: var(--bb-accent); }

/* Theme-proof: themes fill <button> backgrounds on hover/focus/active; hold the
   tile's own surface + ink so the colour name stays readable (the candidate-hover
   lesson). The accent shows via the border / open ring, not a fill. */
.bb-grid-tile,
.bb-grid-tile:hover,
.bb-grid-tile:focus,
.bb-grid-tile:focus-visible,
.bb-grid-tile:active {
    background: var(--bb-surface, #fff) !important;
    color: var(--bb-ink) !important;
}

.bb-grid-tile.is-open {
    border-color: var(--bb-accent);
    box-shadow: inset 0 0 0 1px var(--bb-accent);
}

/* Exactly the single-pick swatch's treatment, at grid size. It used to carry a
   real 1px border, which draws a ring standing off the colour, while the
   single-pick dot uses a box-shadow pair: an inner edge so a white or very pale
   swatch is still visible, and a hairline outside it. Two surfaces showing the
   same colour should not look like two different controls, and the border was
   only ever here because the grid CSS was written apart from the chips. */
.bb-grid-tile-swatch {
    width: 24px;
    height: 24px;
    border-radius: 50%;
    box-shadow: inset 0 0 0 1px rgba( 0, 0, 0, 0.12 ), 0 0 0 1px var(--bb-border);
}

/* The set's own swatch shape, on the grid as well as the single-pick page.
   Round was hardcoded here, so a merchant who chose Square watched the setting
   do nothing to the surface they were actually selling from. Only an explicit
   choice emits the class (see addon_boss_grid_shape_class), so a grid nobody
   has made a decision about keeps the round swatches it has today. */
.bb-quantity-grid.bb-shape-square .bb-grid-tile-swatch { border-radius: 6px; }
.bb-quantity-grid.bb-shape-circle .bb-grid-tile-swatch { border-radius: 50%; }
.bb-quantity-grid.bb-shape-square .bb-matrix-swatch { border-radius: 4px; }
.bb-quantity-grid.bb-shape-circle .bb-matrix-swatch { border-radius: 50%; }

.bb-grid-tile-img {
    width: 42px;
    height: 42px;
    object-fit: cover;
    border-radius: 7px;
    border: 1px solid rgba( 0, 0, 0, 0.1 );
}

.bb-grid-tile-name { font-size: 0.8em; font-weight: 600; }

.bb-grid-tile-count {
    position: absolute;
    top: -7px;
    right: -7px;
    min-width: 20px;
    height: 20px;
    padding: 0 5px;
    border-radius: 100px;
    background: var(--bb-accent);
    color: #fff;
    font-size: 0.72em;
    font-weight: 700;
    line-height: 20px;
}

.bb-grid-tile:not(.bb-grid-tile-filled) .bb-grid-tile-count { display: none; }

.bb-grid-panels {
    margin-top: 6px;
}

.bb-grid-panel {
    margin: 2px 0 4px;
    padding: 10px 12px;
    border: 1px solid var(--bb-border);
    border-radius: 10px;
    background: var(--bb-summary-bg);
}

.bb-grid-panel[hidden] { display: none; }

/* Wrapping cell grid: fills the width and wraps to the next line, so every
   option stays visible at any column width or count. Never scrolls sideways. */
.bb-grid-cells {
    display: grid;
    grid-template-columns: repeat( auto-fill, minmax( 100px, 1fr ) );
    gap: 8px;
}

.bb-grid-cell {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 3px;
    padding: 6px 4px;
    border: 1px solid var(--bb-border);
    border-radius: 9px;
    background: var(--bb-surface, #fff);
}

.bb-grid-cell:focus-within { border-color: var(--bb-accent); }

.bb-grid-cell-label {
    font-size: 0.78em;
    font-weight: 600;
    color: var(--bb-ink-soft, #6b6256);
    white-space: nowrap;
}

/* Photo cells: a single-attribute grid with the "Photos" tile style shows
   each variation's own photo, so a flavour/style run reads as picture cards.
   Two-attribute grids keep photos on the colour tiles instead. Columns follow
   the balanced --bb-grid-cols (even rows, never 3+1); phones drop to two. */
.bb-grid-cells-photo { grid-template-columns: repeat( var( --bb-grid-cols, 4 ), minmax( 0, 1fr ) ); }
@media ( max-width: 480px ) {
    .bb-grid-cells-photo { grid-template-columns: repeat( 2, minmax( 0, 1fr ) ); }
}
.bb-grid-cell-photo {
    padding: 8px 8px 7px;
    gap: 6px;
    cursor: pointer;
    transition: border-color 0.15s ease;
}
.bb-grid-cell-photo:hover { border-color: var(--bb-accent, #7c3aed); }
.bb-grid-cell-photo .bb-grid-cell-label {
    font-size: 0.85em;
    color: var(--bb-ink, #1d2025);
}
.bb-grid-cell-img {
    width: 100%;
    aspect-ratio: 1 / 1;
    object-fit: cover;
    border-radius: 7px;
}
.bb-grid-cell-photo .bb-grid-cell-label {
    white-space: normal;
    text-align: center;
    line-height: 1.25;
}
.bb-grid-cell-price { font-size: 0.8em; color: var(--bb-text); }
.bb-grid-cell-filled {
    border-color: var(--bb-accent);
    box-shadow: inset 0 0 0 1px var(--bb-accent);
}

/* ── The boarding-card strip (his pick from the grid mockups) ──
   Standard size cells join into ONE strip: shared hairlines instead of
   floating boxes, and a filled size tints and underlines in the accent so
   a size run reads at a glance (nothing, nothing, 4, 8, 2). The hairlines
   are child box-shadows, clipped by the frame's overflow, so wrapped rows
   form a clean lattice with no doubled edges. Photo cards keep their own
   card look (the :not scope). */
.bb-grid-cells:not(.bb-grid-cells-photo) {
    /* auto-fit, not auto-fill: with few sizes the cells share the full
       width, so the strip never shows an empty pane past the last size. */
    grid-template-columns: repeat( auto-fit, minmax( 100px, 1fr ) );
    gap: 0;
    border: 1.5px solid var(--bb-border);
    border-radius: 11px;
    overflow: hidden;
    background: var(--bb-surface, #fff);
}
.bb-grid-cells:not(.bb-grid-cells-photo) .bb-grid-cell {
    border: 0;
    border-radius: 0;
    margin: 0;
    padding: 10px 6px 11px;
    gap: 2px;
    box-shadow: 1px 0 0 var(--bb-border-soft, #ececed), 0 1px 0 var(--bb-border-soft, #ececed);
    transition: background 0.15s ease;
}
.bb-grid-cells:not(.bb-grid-cells-photo) .bb-grid-cell-label { font-size: 0.85em; }
.bb-grid-cells:not(.bb-grid-cells-photo) .bb-grid-cell:focus-within { border: 0; box-shadow: 1px 0 0 var(--bb-border-soft, #ececed), 0 1px 0 var(--bb-border-soft, #ececed), inset 0 0 0 1.5px var(--bb-accent); }
.bb-grid-cells:not(.bb-grid-cells-photo) .bb-grid-cell-filled {
    border: 0;
    background: var(--bb-summary-bg, #f6f7f7);
    background: color-mix( in srgb, var(--bb-accent) 8%, var(--bb-surface, #fff) );
    box-shadow: 1px 0 0 var(--bb-border-soft, #ececed), 0 1px 0 var(--bb-border-soft, #ececed), inset 0 -2.5px 0 var(--bb-accent);
}
.bb-grid-cells:not(.bb-grid-cells-photo) .bb-grid-cell-filled .bb-grid-cell-label { color: var(--bb-accent); font-weight: 700; }

/* The strip's stepper: one joined control (flush ends, the count owns the
   middle) instead of three floating pieces. */
.bb-grid-cells:not(.bb-grid-cells-photo) .bb-grid-stepper {
    display: inline-flex;
    align-items: center;
    margin-top: 4px;
    border: 1px solid var(--bb-border);
    border-radius: 8px;
    overflow: hidden;
    background: var(--bb-surface, #fff);
}
.bb-grid-cells:not(.bb-grid-cells-photo) .bb-grid-step {
    width: 28px;
    min-width: 28px;
    min-height: 30px;
    border: 0 !important;
    border-radius: 0 !important;
    background: var(--bb-summary-bg, #f6f7f7) !important;
    color: var(--bb-ink-soft, #6b6256) !important;
    font-size: 1em;
}
.bb-grid-cells:not(.bb-grid-cells-photo) .bb-grid-step:hover {
    background: var(--bb-summary-bg, #f6f7f7) !important;
    color: var(--bb-accent) !important;
}
.bb-grid-cells:not(.bb-grid-cells-photo) .bb-grid-qty {
    width: 34px;
    max-width: none;
    min-height: 30px;
    padding: 0 2px;
    border: 0;
    border-radius: 0;
    font-weight: 700;
}
.bb-grid-cells:not(.bb-grid-cells-photo) .bb-grid-cell-filled .bb-grid-stepper { border-color: var(--bb-accent); }

/* A card at rest offers ONE action: Add. The stepper only exists once a
   quantity does, so there is never a minus to hover at rest and no click
   can mean "remove" by accident. */
.bb-grid-cell-photo .bb-grid-add {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    gap: 5px;
    width: 100%;
    min-height: 40px;
    margin-top: auto; /* pin to the card's bottom edge so a two-line name never staggers the row of pills */
    border: 1px solid var(--bb-accent, #ff6a53);
    border-radius: 999px;
    background: var(--bb-surface, #fff);
    color: var(--bb-accent, #ff6a53);
    font-size: 0.95em;
    font-weight: 600;
    line-height: 1;
    cursor: pointer;
    transition: background-color 0.15s ease, color 0.15s ease;
}
.bb-grid-cell-photo:hover .bb-grid-add,
.bb-grid-cell-photo .bb-grid-add:focus-visible {
    background: var(--bb-accent, #ff6a53);
    color: #fff;
}
.bb-grid-cell-filled .bb-grid-add { display: none; }
.bb-grid-cell-photo:not(.bb-grid-cell-filled) .bb-grid-stepper { display: none; }

.bb-grid-cell-photo .bb-grid-stepper {
    gap: 0;
    width: 100%; /* same footprint as the Add pill it replaces; without it the span shrink-wraps and squeezes the count out at narrow cards */
    margin-top: auto; /* bottom-pinned, exactly like the Add pill it morphs from */
    border: 1px solid var(--bb-border, #e0e0e0);
    border-radius: 999px;
    overflow: hidden;
    background: var(--bb-surface, #fff);
}
/* The - and + are compact ends of the pill; the count owns the middle.
   Card taps land on "add one more", never on "remove". On narrow cards
   (a four-column grid in a slim theme column) the buttons give way
   first, so the count always has room to paint. */
.bb-quantity-grid .bb-grid-cell-photo .bb-grid-step {
    width: auto;
    flex: 0 1 40px;
    min-width: 28px;
    min-height: 40px;
    border: 0;
    border-radius: 0;
    font-size: 1.1em;
    /* the minus and plus wear the accent, same family as the Add pill's
       glyphs; the count between them stays ink for legibility */
    color: var(--bb-accent, #ff6a53) !important;
}
.bb-quantity-grid .bb-grid-cell-photo .bb-grid-step:hover {
    background-color: var(--bb-summary-bg, #f3f4f6) !important;
    color: var(--bb-accent, #ff6a53) !important;
}
.bb-grid-cell-photo .bb-grid-stepper .bb-grid-qty {
    width: auto;
    max-width: none;
    min-width: 1.6em; /* the count never collapses; number inputs refuse to flex-shrink without an explicit minimum */
    flex: 1 1 auto;
    padding-left: 0;
    padding-right: 0;
    border: 0;
    border-radius: 0;
    font-size: 1.05em;
    font-weight: 700;
}
.bb-grid-cell-filled .bb-grid-stepper { border-color: var(--bb-accent); }

.bb-grid-qty {
    width: 100%;
    max-width: 3.6em;
    text-align: center;
    padding: 5px 4px;
    border: 1px solid var(--bb-border);
    border-radius: 7px;
    background: var(--bb-surface, #fff);
    color: var(--bb-ink);
    font-size: 0.95em;
    -moz-appearance: textfield;
}

.bb-grid-qty:focus {
    outline: 2px solid var(--bb-accent);
    outline-offset: 0;
    border-color: var(--bb-accent);
}

/* Clickable − / + steppers on each grid cell (so it isn't type-only). */
.bb-grid-stepper {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 4px;
    width: 100%;
}
.bb-quantity-grid .bb-grid-step {
    flex: 0 0 auto;
    width: 24px;
    height: 30px;
    padding: 0;
    border: 1px solid var(--bb-border, #e0e0e0);
    border-radius: 7px;
    background-color: #fff !important;
    color: var(--bb-ink, #1d2025) !important;
    font-size: 1.15em;
    line-height: 1;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    user-select: none;
    box-shadow: none !important;
}
.bb-quantity-grid .bb-grid-step:hover {
    border-color: var(--bb-accent) !important;
    color: var(--bb-accent) !important;
    background-color: #fff !important;
}
.bb-quantity-grid .bb-grid-step:active {
    background-color: var(--bb-soft, #f3f4f6) !important;
}
.bb-grid-stepper .bb-grid-qty { width: 2em; max-width: 2em; flex: 0 1 auto; }

/* Subtle cue when switching colours: the opened size panel fades up, and the
   selected tile's border eases in. Kept gentle ("nothing too much"). */
@keyframes bbGridFadeIn {
    from { opacity: 0; transform: translateY(5px); }
    to   { opacity: 1; transform: translateY(0); }
}
.bb-grid-panel.is-open { animation: bbGridFadeIn 0.22s ease both; }
.bb-grid-tile { transition: border-color 0.15s ease, box-shadow 0.15s ease; }
@media (prefers-reduced-motion: reduce) {
    .bb-grid-panel.is-open { animation: none; }
}

.bb-grid-cell-oos { opacity: 0.55; justify-content: center; }
.bb-grid-oos-note { font-size: 0.72em; color: var(--bb-ink-soft, #6b6256); }

/* ── Simple product price ────────────────────────────────────── */

.bb-simple-price.bundle-item-price {
    font-size: 0.92em;
    font-weight: 700;
    color: var(--bb-ink);
    margin: 4px 0 0;
}

/* ── Optional excluded state ─────────────────────────────────── */

.bundle-item-excluded .bb-card-body {
    opacity: 0.35;
    pointer-events: none;
    transition: opacity 0.2s;
}

/* ── Out-of-stock item ───────────────────────────────────────── */

.bundle-item-oos .bb-card-body {
    opacity: 0.35;
    pointer-events: none;
}

.bundle-item-oos .bb-item-name {
    opacity: 0.6;
}

.bb-oos-badge {
    font-size: 0.78em;
    font-weight: 600;
    color: var(--bb-danger, #b32d2e);
    background: rgba(179, 45, 46, 0.08);
    border-radius: 999px;
    padding: 3px 11px;
    white-space: nowrap;
}

/* ── Summary bar ─────────────────────────────────────────────── */

/* ── Order tray: ONE card, two states (building → added). The tier bar,
      total and CTA are single instances that re-render, so nothing can render
      twice. Vertical card in M1; M2 makes it a sticky right-rail on desktop
      and a fixed bottom bar on mobile. ────────────────────────────────────── */
.bb-order-tray {
    display: flex;
    flex-direction: column;
    gap: 13px;
    padding: 16px 18px;
    background: var(--bb-surface, #fff);
    border: 1px solid var(--bb-border);
    border-radius: 14px;
    margin-top: 10px;
    box-shadow: 0 6px 20px rgba(15, 23, 42, 0.06);
}
.bb-order-tray .bb-total-row { justify-content: space-between; width: 100%; }

/* Confirmation strip (added state). */
.bb-tray-confirm {
    display: flex;
    align-items: center;
    gap: 9px;
    font-weight: 700;
    font-size: 1.02em;
    color: var(--bb-ink);
}
.bb-tray-check {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    flex: 0 0 20px;
    width: 20px;
    height: 20px;
    border-radius: 50%;
    background: var(--bb-success, #166534);
    color: #fff;
}

/* Tier bar — a single instance shared by both states. */
.bb-tray-tier { display: flex; flex-direction: column; gap: 6px; }
.bb-tier-bar {
    height: 8px;
    border-radius: 5px;
    background: var(--bb-summary-bg, #f3f4f6);
    overflow: hidden;
}
.bb-tier-bar-fill {
    display: block;
    height: 100%;
    width: 0;
    border-radius: 5px;
    background: var(--bb-accent, #7c3aed);
    transition: width 0.4s ease;
}
.bb-tray-tier.is-unlocked .bb-tier-bar-fill { background: var(--bb-success, #166534); }
.bb-tier-line {
    margin: 0;
    font-weight: 700;
    font-size: 0.9em;
    color: var(--bb-accent-deep, #4c1d95);
}
.bb-tray-tier.is-unlocked .bb-tier-line { color: var(--bb-success, #166534); }
.bb-tier-sub { margin: 0; font-size: 0.82em; font-weight: 600; color: var(--bb-muted); }

/* In your box — count, thumbnails, and the adaptive grouped readback. */
.bb-tray-box { display: flex; flex-direction: column; gap: 8px; }
.bb-box-head {
    display: flex;
    align-items: baseline;
    justify-content: space-between;
    gap: 10px;
}
.bb-box-lbl { font-weight: 600; color: var(--bb-ink); }
.bb-box-count { font-size: 0.9em; color: var(--bb-muted); font-weight: 600; }
.bb-box-thumbs { display: flex; flex-wrap: wrap; gap: 5px; }
.bb-box-thumbs:empty { display: none; }
.bb-box-thumbs img {
    width: 34px;
    height: 34px;
    border-radius: 7px;
    border: 1px solid var(--bb-border);
    object-fit: cover;
    background: var(--bb-summary-bg);
}

/* Added-state basket restatement + its saving pill. */
.bb-tray-basket { margin: 0; font-weight: 700; color: var(--bb-ink); font-size: 1.05em; }
.bb-tray-saving {
    margin: -6px 0 0;
    align-self: flex-start;
    font-size: 0.78em;
    font-weight: 600;
    color: var(--bb-success);
    background: var(--bb-success-bg, #ecfdf3);
    border: 1px solid var(--bb-success-border, #bbe5c8);
    padding: 2px 10px;
    border-radius: 999px;
}
.bb-tray-saving[hidden] { display: none; }
/* Content-owned visibility, state-scoped display: only meaningful once added. */
.bb-order-tray[data-state="building"] .bb-tray-saving { display: none; }

/* "+N" overflow chip at the end of the thumbnail strip. */
.bb-box-thumbs .bb-box-thumb-more {
    width: 34px;
    height: 34px;
    border-radius: 7px;
    border: 1px solid var(--bb-border);
    background: var(--bb-summary-bg, #f3f4f6);
    display: inline-flex;
    align-items: center;
    justify-content: center;
    font-size: 12px;
    font-weight: 700;
    color: var(--bb-muted);
}

/* Action row. */
.bb-tray-cta { display: flex; flex-direction: column; gap: 9px; }
.bb-tray-cta > .button { width: 100%; text-align: center; }
/* Filled, not outline — the same lesson as .bb-chooser-change below. An
   outline button is only readable while its text colour survives, and Divi's
   Theme Builder ships an id-level ".button { color: #fff !important }" that a
   class selector cannot outrank. Our transparent background won (the theme's
   is not !important) but our ink text lost, so the label went white on the
   tray's white card: invisible until hover, when Divi's own background painted
   and the words appeared out of nowhere. Reported from a live Divi store.
   A dark fill reads with white text OR ours, so the label survives whichever
   colour the theme forces. Fallbacks in var() too: a token that fails to
   resolve takes the whole declaration with it and hands the button back. */
.bundle-boss-form .bb-tray-cta .bb-build-new {
    background: var(--bb-ink, #111827) !important;
    color: #fff !important;
    border: 1px solid var(--bb-ink, #111827) !important;
    box-shadow: none !important;
}
.bundle-boss-form .bb-tray-cta .bb-build-new:hover {
    background: var(--bb-text, #374151) !important;
    border-color: var(--bb-text, #374151) !important;
    color: #fff !important;
}
.bb-tray-links {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 18px;
    margin-top: 2px;
}
.bb-tray-links a {
    font-weight: 600;
    font-size: 0.86em;
    color: var(--bb-link, #2271b1);
    text-decoration: none;
}
.bb-tray-links a:hover { text-decoration: underline; }

/* ── "Your boxes" — the editable running order (added state). Each box is a
      row you can edit, re-quantify or remove; add and remove are symmetric. ── */
.bb-your-boxes { display: flex; flex-direction: column; gap: 11px; }
.bb-yb-head {
    font-size: 0.72em;
    font-weight: 700;
    letter-spacing: 0.09em;
    text-transform: uppercase;
    color: var(--bb-muted);
}
.bb-yb-row {
    display: grid;
    grid-template-columns: 44px 1fr;
    gap: 11px;
    align-items: start;
    transition: opacity 0.15s;
}
.bb-yb-row.bb-yb-busy { opacity: 0.45; pointer-events: none; }
.bb-yb-thumb {
    width: 44px;
    height: 44px;
    border-radius: 9px;
    border: 1px solid var(--bb-border);
    background-size: cover;
    background-position: center;
    background-color: var(--bb-summary-bg);
}
/* No image? The row drops the thumbnail entirely and becomes a single column,
   so the name/qty/price keep their normal layout — never a blank grey tile. */
.bb-yb-row-noimg { grid-template-columns: 1fr; }
.bb-yb-main { min-width: 0; }
.bb-yb-name {
    font-weight: 700;
    font-size: 0.94em;
    line-height: 1.25;
    overflow: hidden;
    text-overflow: ellipsis;
    white-space: nowrap;
}
.bb-yb-meta { font-size: 0.8em; color: var(--bb-muted); margin-top: 1px; }
/* What tells two boxes of the same bundle apart: "250g · STEFANOS". */
.bb-yb-detail { font-size: 0.8em; color: var(--bb-text); margin-top: 1px; }
.bb-yb-actions { display: flex; align-items: center; gap: 10px; margin-top: 8px; flex-wrap: wrap; }
.bb-yb-edit { font-size: 0.82em; font-weight: 600; color: var(--bb-accent-deep, #4c1d95); text-decoration: none; }
.bb-yb-edit:hover { text-decoration: underline; }
.bb-yb-stepper { display: inline-flex; border: 1px solid var(--bb-border); border-radius: 8px; overflow: hidden; }
.bb-yb-stepper button,
.bb-yb-qty {
    width: 28px;
    height: 26px;
    display: flex;
    align-items: center;
    justify-content: center;
    background: var(--bb-surface);
    border: 0;
    font-size: 14px;
    font-weight: 700;
    color: var(--bb-ink);
}
.bb-yb-stepper button { cursor: pointer; padding: 0; }
.bb-yb-stepper button:disabled { color: var(--bb-border-strong, #cbd0d6); cursor: default; }
.bb-yb-qty { border-left: 1px solid var(--bb-border); border-right: 1px solid var(--bb-border); font-size: 13px; }
.bb-yb-remove {
    width: 26px;
    height: 26px;
    border: 1px solid var(--bb-border);
    border-radius: 8px;
    background: var(--bb-surface);
    color: var(--bb-muted);
    font-size: 15px;
    line-height: 1;
    cursor: pointer;
    display: inline-flex;
    align-items: center;
    justify-content: center;
    padding: 0;
}
.bb-yb-remove:hover { color: var(--bb-accent-deep, #d4381f); border-color: currentColor; }
/* The box currently open in the builder (edit mode). */
.bb-yb-badge {
    font-size: 0.68em;
    font-weight: 700;
    letter-spacing: 0.07em;
    text-transform: uppercase;
    padding: 2px 9px;
    border-radius: 12px;
    background: var(--bb-accent-bg, var(--bb-summary-bg, #f3f4f6));
    color: var(--bb-accent-deep, #4c1d95);
}
.bb-yb-price { margin-left: auto; font-weight: 800; font-size: 0.96em; color: var(--bb-ink); }

/* The two-column shell stays transparent (display:contents) — BundleBoss's
   builder lives inside WooCommerce's ~50% summary column, so a right-rail would
   crush the item cards. Discoverability is handled by the sticky peek bar below
   instead. The wrappers are kept (harmless) so a future full-width-builder mode
   could switch this to a real grid. */
.bb-form-cols,
.bb-build-col,
.bb-tray-col { display: contents; }

/* ── Sticky peek bar (M2). A slim bar that appears fixed at the bottom of the
      viewport ONLY while the full tray is scrolled out of view, so the shopper
      always has the running total + action to hand without the tray covering
      the builder. Hidden whenever the real tray is on screen. ──────────────── */
.bb-tray-peek {
    position: fixed;
    left: 0;
    right: 0;
    bottom: 0;
    z-index: 60;
    display: flex;
    flex-direction: column;
    background: var(--bb-surface, #fff);
    border-top: 1px solid var(--bb-border);
    box-shadow: 0 -6px 22px rgba(15, 23, 42, 0.14);
    transform: translateY(100%);
    transition: transform 0.22s ease;
}
.bb-tray-peek.is-shown { transform: none; }
@media (prefers-reduced-motion: reduce) { .bb-tray-peek { transition: none; } }
.bb-peek-tier { height: 4px; background: var(--bb-summary-bg, #f3f4f6); }
.bb-peek-tier > i { display: block; height: 100%; width: 0; background: var(--bb-accent, #7c3aed); transition: width 0.4s ease; }
.bb-tray-peek.is-unlocked .bb-peek-tier > i { background: var(--bb-success, #166534); }
.bb-peek-row {
    display: flex;
    align-items: center;
    gap: 14px;
    max-width: 1100px;
    margin: 0 auto;
    width: 100%;
    padding: 10px 16px;
    padding-bottom: calc(10px + env(safe-area-inset-bottom, 0px));
}
.bb-peek-info { flex: 1; min-width: 0; font-weight: 700; color: var(--bb-ink); font-size: 0.98em; }
.bb-peek-info .bb-peek-sub { display: block; font-weight: 600; font-size: 0.78em; color: var(--bb-accent-deep, #4c1d95); }
.bb-tray-peek.is-unlocked .bb-peek-info .bb-peek-sub { color: var(--bb-success, #166534); }
.bb-peek-cta { flex: 0 0 auto; white-space: nowrap; }

/* When docked inside the Configurator's side panel, the tray drops its own card
   chrome so it blends into the panel instead of reading as a card-in-a-card. */
.bb-config-tray {
    border: 0;
    box-shadow: none;
    background: transparent;
    padding: 0;
    margin-top: 12px;
    max-height: none;
    overflow: visible;
}

.bb-summary-totals {
    display: flex;
    flex-direction: column;
    gap: 2px;
}

/* ── Build recap (full-width line above the totals) ─────────────── */

.bb-build-recap {
    flex-basis: 100%;
    margin: 0;
}

.bb-build-recap > summary {
    cursor: pointer;
    font-size: 0.75em;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--bb-ink-soft);
}

.bb-build-recap > summary:hover,
.bb-build-recap[open] > summary {
    color: var(--bb-accent-deep);
}

.bb-recap-list {
    list-style: none;
    margin: 10px 0 4px;
    padding: 0;
    max-height: 180px;
    overflow-y: auto;
}

.bb-recap-list li {
    display: flex;
    justify-content: space-between;
    gap: 16px;
    padding: 3px 0;
    font-size: 0.82em;
}

.bb-recap-name {
    color: var(--bb-text);
}

.bb-recap-price {
    color: var(--bb-ink);
    white-space: nowrap;
}

.bb-total-row {
    display: flex;
    align-items: baseline;
    gap: 10px;
}

.bundle-total-label {
    font-size: 0.75em;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--bb-muted);
}

.bundle-total-amount {
    font-size: 1.8em;
    font-weight: 800;
    color: var(--bb-ink);
    line-height: 1;
    letter-spacing: -0.02em;
}

.bundle-original-total {
    font-size: 0.85em;
    color: #4b5563;
    text-decoration: line-through;
    font-weight: 400;
}

.bundle-savings {
    font-size: 0.78em;
    font-weight: 600;
    color: var(--bb-success);
    background: var(--bb-success-bg);
    border: 1px solid var(--bb-success-border);
    display: inline-block;
    padding: 2px 10px;
    border-radius: 100px;
}

.bundle-add-to-cart,
.bb-add-another {
    flex-shrink: 0;
    padding: 13px 32px !important;
    font-size: 0.97em !important;
    font-weight: 700 !important;
    letter-spacing: 0.02em;
    border-radius: 10px !important;
    transition: opacity 0.15s, transform 0.12s, box-shadow 0.15s !important;
    min-width: 180px;
    text-align: center;
    background: var(--bb-accent) !important;
    color: #fff !important;
    border-color: var(--bb-accent) !important;
    box-shadow: none !important;
}

.bundle-add-to-cart:not(:disabled):hover,
.bb-add-another:hover {
    background: var(--bb-accent-deep) !important;
    border-color: var(--bb-accent-deep) !important;
    transform: translateY(-1px);
    box-shadow: 0 4px 14px rgba(60,40,20,0.22) !important;
}

/* The form prefix lifts this above theme/custom button rules like
   ".woocommerce button.button { background: ... !important }", which
   otherwise repaint a disabled Add at full strength. */
.bundle-boss-form .bundle-add-to-cart:disabled {
    background: var(--bb-border-strong) !important;
    color: var(--bb-muted) !important;
    border-color: var(--bb-border-strong) !important;
    opacity: 1 !important;
    cursor: not-allowed !important;
    transform: none !important;
}

/* Theme guard: the tray CTA button. Divi's WooCommerce add-to-cart module ships
   "body #page-container .et_pb_section .et_pb_wc_add_to_cart_0_tb_body .button
   { margin-left: 90px }", which shoved the full-width Add button right and off
   the tray's edge on a customer's Divi store. That rule carries no !important,
   so ours wins regardless of its id-level specificity; pin the tray CTA buttons
   flush to the tray column. */
.bundle-boss-form .bb-tray-cta > .button {
    box-sizing: border-box !important;
    width: 100% !important;
    max-width: 100% !important;
    margin: 0 !important;
    float: none !important;
}

/* ── Theme guard: small controls ─────────────────────────────────
   Global theme form/button styles outrank our single-class rules
   (input[type="number"] is 0-1-1, .bb-yb-qty is 0-1-0) — measured
   live under Flatsome, which inflates inputs and reshapes buttons
   with its own margins and heights. Restate the geometry of every
   small control with the form prefix and !important, mirroring the
   base rules; behavioural state (hover, disabled) stays with the
   base rules except where repeated here to keep it alive. */

.bundle-boss-form .bb-yb-stepper {
    display: inline-flex !important;
    width: auto !important;
    height: auto !important;
    margin: 0 !important;
    padding: 0 !important;
    border: 1px solid var(--bb-border) !important;
    border-radius: 8px !important;
    overflow: hidden !important;
}

.bundle-boss-form .bb-yb-stepper button,
.bundle-boss-form .bb-yb-qty {
    box-sizing: border-box !important;
    display: flex !important;
    align-items: center !important;
    justify-content: center !important;
    width: 28px !important;
    height: 26px !important;
    min-width: 0 !important;
    min-height: 0 !important;
    max-width: none !important;
    margin: 0 !important;
    padding: 0 !important;
    border: 0 !important;
    border-radius: 0 !important;
    box-shadow: none !important;
    text-shadow: none !important;
    text-transform: none !important;
    letter-spacing: normal !important;
    font-size: 14px !important;
    font-weight: 700 !important;
    line-height: 1 !important;
    background: var(--bb-surface) !important;
    color: var(--bb-ink) !important;
}

.bundle-boss-form .bb-yb-qty {
    border-left: 1px solid var(--bb-border) !important;
    border-right: 1px solid var(--bb-border) !important;
    font-size: 13px !important;
}

/* Repeated because the guard pins colour above the base rules. */
.bundle-boss-form .bb-yb-stepper button:disabled {
    color: var(--bb-border-strong, #cbd0d6) !important;
}

.bundle-boss-form .bb-yb-remove {
    box-sizing: border-box !important;
    display: inline-flex !important;
    align-items: center !important;
    justify-content: center !important;
    width: 26px !important;
    height: 26px !important;
    min-width: 0 !important;
    min-height: 0 !important;
    margin: 0 !important;
    padding: 0 !important;
    border: 1px solid var(--bb-border) !important;
    border-radius: 8px !important;
    box-shadow: none !important;
    font-size: 15px !important;
    line-height: 1 !important;
    background: var(--bb-surface) !important;
    color: var(--bb-muted) !important;
}

.bundle-boss-form .bb-yb-remove:hover {
    color: var(--bb-accent-deep, #d4381f) !important;
    border-color: currentColor !important;
}

.bundle-boss-form .bb-chooser-search-input {
    box-sizing: border-box !important;
    width: 100% !important;
    height: auto !important;
    min-height: 0 !important;
    margin: 0 !important;
    padding: 8px 12px !important;
    border: 1.5px solid var(--bb-border) !important;
    border-radius: 8px !important;
    box-shadow: none !important;
    font-size: 0.85em !important;
    line-height: normal !important;
}

.bundle-boss-form .bb-chooser-pager button {
    box-sizing: border-box !important;
    width: auto !important;
    height: auto !important;
    min-width: 0 !important;
    min-height: 0 !important;
    margin: 0 !important;
    padding: 3px 10px !important;
    border: 1px solid var(--bb-border) !important;
    border-radius: 6px !important;
    box-shadow: none !important;
    text-transform: none !important;
    letter-spacing: normal !important;
    font-size: 1.05em !important;
    line-height: 1 !important;
    background: var(--bb-surface) !important;
    color: var(--bb-text) !important;
}

.bundle-boss-form .bb-chooser-pager button:hover:not(:disabled) {
    color: var(--bb-ink) !important;
    border-color: var(--bb-muted) !important;
}

/* Hygiene-only pins (no fixed geometry of their own to restate):
   themes' global input margins/shadows must not leak in. */
.bundle-boss-form .bb-addon-input,
.bundle-boss-form .bb-quantity-grid input[type="number"] {
    margin: 0 !important;
    min-height: 0 !important;
    box-shadow: none !important;
}

/* The Order Builder matrix cell is a borderless number in a bordered cell (the
   .bb-quantity-grid guard above already pins its margin/min-height/box-shadow).
   Pin border + radius too, with the form prefix, so an id-scoped theme input
   border can't turn each cell into a rounded pill. Scoped to .bb-matrix only —
   the standalone grid input keeps its own border and 7px radius. */
.bundle-boss-form .bb-matrix .bb-grid-qty {
    border: 0 !important;
    border-radius: 0 !important;
}

.bundle-boss-form .bb-select-wrap .bundle-attr-select.bb-dropdown {
    height: auto !important;
    min-height: 0 !important;
    margin: 0 !important;
    line-height: normal !important;
}

/* ── Notices ─────────────────────────────────────────────────── */

.bundle-stock-notice,
.bundle-boss-notices,
.bundle-boss-success-notice {
    margin-top: 12px;
    padding: 12px 16px;
    border-radius: 10px;
    font-size: 0.88em;
    font-weight: 500;
    line-height: 1.5;
}

.bundle-stock-notice {
    background: var(--bb-warn-bg);
    border: 1px solid var(--bb-warn-border);
    color: var(--bb-warn-deep);
}

/* Builder-failed-to-load notice (revealed by init-guard.js, or shown inside
   <noscript> when JavaScript is off). No `display` rule here on purpose, so the
   `hidden` attribute keeps it hidden until the guard removes it. */
.bundle-boss-init-error {
    margin-top: 12px;
    padding: 12px 16px;
    border-radius: 10px;
    font-size: 0.88em;
    line-height: 1.5;
    background: #fef2f2;
    border: 1px solid #fecaca;
    color: #991b1b;
}

.bundle-boss-notices {
    background: #fef2f2;
    border: 1px solid #fecaca;
    color: var(--bb-error);
}

.bundle-boss-success-notice {
    background: var(--bb-success-bg);
    border: 1px solid var(--bb-success-border);
    color: #15803d;
}

.bundle-boss-success-notice a {
    font-weight: 700;
    text-decoration: underline;
    color: inherit;
}

/* ── Sale Ending countdown ───────────────────────────────────── */

.bb-sale-ending {
    margin-bottom: 1.5em;
    padding: 14px 18px;
    background: var(--bb-surface);
    border: 1px solid var(--bb-border);
    border-radius: 12px;
    box-shadow: 0 1px 3px rgba(0,0,0,0.05);
}

.bb-sale-label {
    font-size: 0.72em;
    font-weight: 700;
    text-transform: uppercase;
    letter-spacing: 0.1em;
    color: var(--bb-muted);
    margin-bottom: 8px;
}

.bb-sale-bar {
    height: 5px;
    background: var(--bb-surface-alt);
    border-radius: 100px;
    overflow: hidden;
    margin-bottom: 12px;
}

.bb-sale-bar-fill {
    height: 100%;
    background: linear-gradient(90deg, #ef4444 0%, #f97316 100%);
    border-radius: 100px;
    transition: width 1s linear;
}

.bb-countdown {
    display: flex;
    align-items: center;
    gap: 6px;
}

.bb-cd-seg {
    display: flex;
    flex-direction: column;
    align-items: center;
    min-width: 44px;
}

.bb-cd-val {
    font-size: 1.6em;
    font-weight: 800;
    color: var(--bb-ink);
    letter-spacing: -0.02em;
    line-height: 1;
    font-variant-numeric: tabular-nums;
}

.bb-cd-unit {
    font-size: 0.62em;
    font-weight: 600;
    text-transform: uppercase;
    letter-spacing: 0.08em;
    color: var(--bb-muted-light);
    margin-top: 3px;
}

.bb-cd-sep {
    font-size: 1.4em;
    font-weight: 800;
    color: var(--bb-border-strong);
    line-height: 1;
    margin-bottom: 12px;
    align-self: flex-start;
    margin-top: 4px;
}

/* ── Related bundles carousel ────────────────────────────────── */

.bb-related {
    margin-top: 2em;
    border-top: 1px solid var(--bb-surface-alt);
    padding-top: 1.5em;
}

.bb-related-header {
    display: flex;
    align-items: center;
    justify-content: space-between;
    margin-bottom: 14px;
}

.bb-related-title {
    font-size: 1em !important;
    font-weight: 700 !important;
    color: var(--bb-ink) !important;
    margin: 0 !important;
    padding: 0 !important;
    border: none !important;
}

.bb-related-nav {
    display: flex;
    gap: 6px;
}

.bb-related-prev,
.bb-related-next {
    width: 32px;
    height: 32px;
    border-radius: 50%;
    border: 1px solid var(--bb-border);
    background: var(--bb-surface);
    cursor: pointer;
    font-size: 1em;
    color: var(--bb-text);
    display: flex;
    align-items: center;
    justify-content: center;
    transition: background 0.12s, border-color 0.12s, color 0.12s;
    line-height: 1;
}

.bb-related-prev:hover,
.bb-related-next:hover {
    background: var(--bb-ink);
    border-color: var(--bb-ink);
    color: var(--bb-surface);
}

.bb-related-prev:disabled,
.bb-related-next:disabled {
    opacity: 0.3;
    cursor: not-allowed;
}

.bb-related-viewport {
    overflow: hidden;
}

.bb-related-track {
    display: flex;
    gap: 14px;
    transition: transform 0.3s ease;
}

.bb-related-card {
    flex: 0 0 200px;
    background: var(--bb-surface);
    border: 1px solid var(--bb-border);
    border-radius: 12px;
    overflow: hidden;
    text-decoration: none !important;
    color: inherit !important;
    display: flex;
    flex-direction: column;
    transition: box-shadow 0.18s, border-color 0.18s;
    box-shadow: 0 1px 3px rgba(0,0,0,0.04);
}

.bb-related-card:hover {
    border-color: var(--bb-border-strong);
    box-shadow: 0 4px 16px rgba(0,0,0,0.09);
}

.bb-related-img img {
    display: block;
    width: 100%;
    height: 130px;
    object-fit: cover;
    border-bottom: 1px solid var(--bb-surface-alt);
}

.bb-related-info {
    padding: 10px 12px 6px;
    flex: 1;
}

.bb-related-name {
    font-size: 0.82em;
    font-weight: 700;
    color: var(--bb-ink);
    line-height: 1.3;
    margin-bottom: 4px;
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

.bb-related-price {
    font-size: 0.78em;
    font-weight: 600;
    color: var(--bb-text);
}

.bb-related-cta {
    display: block;
    padding: 8px 12px;
    font-size: 0.75em;
    font-weight: 700;
    color: var(--bb-ink);
    text-align: right;
    border-top: 1px solid var(--bb-surface-alt);
    letter-spacing: 0.02em;
    transition: color 0.12s;
}

.bb-related-card:hover .bb-related-cta {
    color: var(--bb-text);
}

/* ── Shop / archive badge ────────────────────────────────────── */

.bundle-boss-type-badge {
    display: inline-block;
    padding: 3px 9px;
    background: var(--bb-ink);
    color: var(--bb-surface);
    font-size: 0.7em;
    font-weight: 700;
    border-radius: 4px;
    margin-bottom: 6px;
    letter-spacing: 0.06em;
    text-transform: uppercase;
    vertical-align: middle;
}

.bundle-boss-from-price {
    font-weight: 700;
}

/* ── Cart / order group header ───────────────────────────────── */

.bundle-boss-cart-group-header {
    display: block;
    margin-bottom: 0.4em;
    padding-bottom: 0.4em;
    border-bottom: 1px solid var(--bb-border);
    font-size: 0.88em;
    color: var(--bb-muted);
}

a.bundle-boss-edit-bundle   { color: var(--bb-link); font-size: 0.8em; font-weight: 600; white-space: nowrap; }
a.bundle-boss-remove-bundle { color: #b32d2e; font-size: 0.8em; font-weight: 600; white-space: nowrap; }

/* ── Accordion collapsed state ───────────────────────────── */

.bundle-item--collapsed .bb-card-header {
    border-bottom: none;
}

.bundle-item--collapsed .bb-card-body {
    display: none;
}

.bb-card-summary {
    display: none;
    padding: 0 18px 14px calc(18px + 26px + 10px);
    font-size: 0.82em;
    color: var(--bb-muted);
    font-weight: 500;
    flex-wrap: wrap;
    gap: 5px;
    align-items: center;
    line-height: 1.4;
}

.bundle-item--collapsed .bb-card-summary {
    display: flex;
}

.bundle-item--done.bundle-item--collapsed .bb-card-summary {
    color: #059669;
    font-weight: 600;
}

/* ── Bundle product page: badge, title, price ────────────── */

.bundle-boss-product-badge {
    display: inline-block;
    background: var(--bb-ink);
    color: var(--bb-surface);
    font-size: 0.68em;
    font-weight: 800;
    letter-spacing: 0.12em;
    text-transform: uppercase;
    padding: 4px 13px;
    border-radius: 100px;
    margin-bottom: 10px;
    line-height: 1.5;
}

.product-type-bundle h1.product_title {
    font-size: 2.3em !important;
    font-weight: 800 !important;
    letter-spacing: -0.025em !important;
    line-height: 1.15 !important;
    color: var(--bb-ink) !important;
    margin-bottom: 6px !important;
}

.product-type-bundle .summary .price,
.product-type-bundle .entry-summary .price {
    font-size: 1.45em !important;
    font-weight: 700 !important;
    color: var(--bb-ink) !important;
    display: block;
    margin-bottom: 0.5em !important;
}

/* ── Keyboard focus visibility ─────────────────────────────────── */

.bb-card-header:focus-visible,
.bb-chip:focus-visible,
.bb-qty-btn:focus-visible,
.bundle-add-to-cart:focus-visible,
.bb-toggle input:focus-visible + .bb-toggle-track {
    outline: 2px solid var(--bb-link);
    outline-offset: 2px;
}

/* Swatch chips: the outline alone can vanish against the dot colour —
   add a ring as well. */
.bb-chip--swatch:focus-visible {
    box-shadow: 0 0 0 3px rgba(34, 113, 177, 0.35);
}

/* Scroll-back target after collapsing a card: keep clear of the admin bar. */
.bundle-item {
    scroll-margin-top: 12px;
}

body.admin-bar .bundle-item {
    scroll-margin-top: 44px;
}

/* ── Builder: choice slot chooser ─────────────────────────────── */

/* Options fill the row they land on. Two things conspired to leave a slot with
   few options looking half-empty:
   1. "auto-fill" reserves a track for every 150px of width whether or not an
      option lands in it, so a 700px column reserved four tracks and a 900px one
      reserved five — when no slot had more than three options. "auto-fit" drops
      the tracks nothing uses.
   2. A candidate is a <button>, and a button does not stretch into its grid
      track the way a div does: it sizes to its content (~210px) and stops. So
      even a full-width track left the card stranded at 210px, and the layout
      only ever looked filled because auto-fill's tracks happened to be narrower
      than that. width:100% is what makes a card take the track it was given.
   The cap keeps a lone option a card rather than a banner. It only bites where
   one card would get more than 340px, and the card then centres in its track.
   The configurator pins its own auto-fill grid at a higher specificity, so its
   tracks stay ~150-220px and the width below is a no-op there. */
.bb-chooser-grid {
    display: grid;
    grid-template-columns: repeat(auto-fit, minmax(150px, 1fr));
    gap: 10px;
}

.bb-chooser-grid .bb-candidate {
    width: 100%;
    max-width: 340px;
    justify-self: center;
}

.bb-chooser-loading,
.bb-chooser-empty {
    color: var(--bb-muted-light);
    font-size: 0.85em;
    padding: 12px 0;
}

.bb-candidate {
    position: relative; /* badge anchor */
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 6px;
    padding: 12px 10px;
    background: var(--bb-surface);
    border: 1.5px solid var(--bb-border);
    border-radius: 12px;
    cursor: pointer;
    text-align: center;
    transition: border-color 0.12s, box-shadow 0.12s, transform 0.12s;
}

.bb-candidate:hover:not(:disabled) {
    background: var(--bb-surface);
    border-color: var(--bb-ink);
    box-shadow: 0 4px 14px rgba(0,0,0,0.08);
    transform: translateY(-1px);
}

/* Candidates are <button>s. Some themes (Kadence, etc.) give buttons a
   coloured background on hover/focus/active that is more specific than our
   base rule, which buries the name/price. Hold our own surface + ink. */
.bb-candidate:focus,
.bb-candidate:active {
    background: var(--bb-surface);
    color: var(--bb-ink);
    border-color: var(--bb-ink);
}

.bb-candidate:focus-visible {
    outline: 2px solid var(--bb-link);
    outline-offset: 2px;
}

.bb-candidate.is-oos {
    opacity: 0.5;
    cursor: not-allowed;
}

.bb-candidate-img {
    width: 72px;
    height: 72px;
    object-fit: cover;
    border-radius: 8px;
}

.bb-candidate-name {
    font-size: 0.82em;
    font-weight: 600;
    color: var(--bb-ink);
    line-height: 1.3;
}

.bb-candidate-price {
    font-size: 0.8em;
    color: var(--bb-text);
}

.bb-candidate-oos {
    font-size: 0.7em;
    font-weight: 700;
    color: var(--bb-error);
    text-transform: uppercase;
    letter-spacing: 0.05em;
}

/* Deal surfacing on candidate cards: sale badge top-left, featured
   ("Popular") ribbon top-right. WC's price_html already renders the
   strikethrough; the rules below just keep it tidy at card size. */

.bb-candidate-sale,
.bb-candidate-featured {
    position: absolute;
    top: 8px;
    padding: 2px 8px;
    border-radius: 9px;
    font-size: 0.62em;
    font-weight: 700;
    line-height: 1.7;
    text-transform: uppercase;
    letter-spacing: 0.05em;
}

.bb-candidate-sale {
    left: 8px;
    background: var(--bb-error);
    color: #fff;
}

.bb-candidate-featured {
    right: 8px;
    background: var(--bb-accent-bg);
    color: var(--bb-accent-deep);
    border: 1px solid var(--bb-accent);
}

.bb-candidate-price del {
    color: var(--bb-muted-light);
    margin-right: 4px;
}

.bb-candidate-price ins {
    text-decoration: none;
    font-weight: 600;
}

/* Search + pager */

.bb-chooser-search {
    margin-bottom: 10px;
}

.bb-chooser-search-input {
    width: 100%;
    padding: 8px 12px;
    border: 1.5px solid var(--bb-border);
    border-radius: 8px;
    font-size: 0.85em;
}

.bb-chooser-pager {
    display: flex;
    align-items: center;
    justify-content: center;
    gap: 12px;
    margin-top: 10px;
    font-size: 0.8em;
    color: var(--bb-muted);
}

.bb-chooser-pager button {
    border: 1px solid var(--bb-border);
    background: var(--bb-surface);
    /* Explicit text colour: themes that paint button text white (Kadence et al.)
       left the arrows white-on-white, invisible until hover repainted them. */
    color: var(--bb-text);
    border-radius: 6px;
    padding: 3px 10px;
    font-size: 1.05em;
    line-height: 1;
    cursor: pointer;
}

.bb-chooser-pager button:hover:not(:disabled) {
    color: var(--bb-ink);
    border-color: var(--bb-muted);
}

.bb-chooser-pager button:disabled {
    opacity: 0.4;
    cursor: not-allowed;
}

/* Per-item price hiding (item row More options). The price elements stay in
   the DOM so the totals JS keeps working from data; only the display goes.
   The cart lines respect the same flag server-side. */
.bundle-item.bb-item-price-hidden .bundle-item-price {
    display: none !important;
}

/* Failed-load retry: rendered in the grid instead of an eternal spinner. */
.bb-chooser-retry {
    margin-left: 8px;
    padding: 3px 12px;
    border: 1px solid var(--bb-border);
    border-radius: 6px;
    background: var(--bb-surface);
    color: var(--bb-text);
    font-size: 0.85em;
    line-height: 1.4;
    cursor: pointer;
}

.bb-chooser-retry:hover {
    border-color: var(--bb-muted);
}

/* Chosen bar */

.bb-chosen-bar {
    display: flex;
    align-items: center;
    flex-wrap: wrap;      /* price + Change drop to their own line when tight */
    gap: 8px 12px;
    padding: 10px 14px;
    background: var(--bb-success-bg);
    border: 1px solid var(--bb-success-border);
    border-radius: 10px;
    margin-bottom: 12px;
    /* Hug the content (name + price + Change) instead of stretching the full
       column width — a short name left a big empty gap with the Change button
       floating on the far right. */
    width: fit-content;
    max-width: 100%;
}

.bb-chosen-bar[hidden] {
    display: none;
}

/* The card's main image already swaps to the chosen product —
   a second thumbnail inside the bar just steals width. */
.bb-chosen-thumb {
    display: none;
}

.bb-chosen-name {
    font-weight: 700;
    font-size: 0.88em;
    color: #14532d;
    flex: 0 1 auto;
    min-width: 0;
    line-height: 1.35;
    /* Never split words — wrap whole words only. */
    word-break: normal;
    overflow-wrap: normal;
    hyphens: none;
}

.bb-chosen-price {
    font-size: 0.85em;
    color: var(--bb-success);
    white-space: nowrap;
}

/* A chosen simple product's config block is only its price —
   redundant right under the bar's price. (Data attributes stay
   in the DOM for the total calculation.) */
.bb-chosen-config .bundle-simple-item {
    display: none;
}

/* Filled, not outline. An outline (green text on white) reads only while the
   text stays green: Divi's Theme Builder ships an id-level ".button { color:
   #fff !important }" that a class selector cannot outrank, which turned the
   text white and left it white-on-white — invisible until hover, when the
   background alone went green. A solid green fill reads with white OR green
   text, so the label survives whatever colour a theme forces on it. */
.bundle-boss-form .bb-chosen-bar .bb-chooser-change {
    flex-shrink: 0;
    margin-left: auto;
    display: inline-flex;
    align-items: center;
    gap: 6px;
    background: var(--bb-success, #166534) !important;
    color: #fff !important;
    border: 1.5px solid var(--bb-success, #166534) !important;
    border-radius: 100px !important;
    padding: 5px 16px !important;
    font-size: 0.78em !important;
    font-weight: 600 !important;
    line-height: 1.4 !important;
    text-transform: none !important;
    letter-spacing: 0 !important;
    box-shadow: none !important;
    cursor: pointer;
    transition: background 0.15s, color 0.15s, border-color 0.15s, transform 0.12s;
}

.bundle-boss-form .bb-chosen-bar .bb-chooser-change:hover {
    background: #0f4a29 !important;
    color: #fff !important;
    border-color: #0f4a29 !important;
    transform: translateY(-1px);
}

.bundle-boss-form .bb-chosen-bar .bb-chooser-change:active {
    transform: translateY(0);
}

/* ── Builder: compatibility warnings ──────────────────────────── */

.bb-candidate.has-warning {
    border-color: var(--bb-warn-accent);
}

.bb-candidate-warn {
    font-size: 0.7em;
    font-weight: 700;
    color: var(--bb-warn);
}

.bb-compat-warn {
    font-size: 0.75em;
    font-weight: 700;
    color: var(--bb-warn);
    background: var(--bb-warn-bg-strong);
    border: 1px solid var(--bb-warn-accent);
    border-radius: 100px;
    padding: 2px 10px;
    white-space: nowrap;
}

.bb-compat-warn[hidden] {
    display: none;
}

.bb-chooser-notice {
    margin-bottom: 10px;
    padding: 9px 12px;
    background: var(--bb-warn-bg-strong);
    border: 1px solid var(--bb-warn-accent);
    border-radius: 8px;
    font-size: 0.82em;
    color: var(--bb-warn-deep);
}

.bb-chooser-notice[hidden] {
    display: none;
}

/* Quantity-rule cap on a slot's stepper, e.g. "Limited to 2 by your
   Motherboard choice". */
.bb-qty-cap-note {
    margin: -4px 0 10px;
    font-size: 0.78em;
    color: var(--bb-warn-deep);
}

.bb-qty-cap-note::before {
    content: "\26A0\FE0E\00A0"; /* warning sign */
}

/* ── Share this build ─────────────────────────────────────────── */

.bb-share-row {
    margin-top: 10px;
    text-align: center;
}

.bb-share-row[hidden] {
    display: none;
}

.bb-share-build {
    display: inline-flex;
    align-items: center;
    gap: 6px;
    padding: 7px 16px;
    border: 1px solid #c3c4c7;
    border-radius: 100px;
    background: transparent;
    color: #50575e;
    font-size: 0.85em;
    cursor: pointer;
    transition: border-color 0.15s, color 0.15s, background 0.15s;
}

.bb-share-build::before {
    content: "\1F517"; /* link symbol */
    font-size: 0.95em;
}

.bb-share-build:hover {
    border-color: #50575e;
    color: #1d2327;
}

.bb-share-build.is-copied {
    border-color: var(--bb-success-bright);
    color: var(--bb-success-bright);
    background: #edfaef;
}

/* ── Choice slot with no chosen product and no slot image: hide the preview
      entirely. If there is no picture, show nothing rather than an empty "+"
      tile, and let the chooser take the space. ────────────────────────── */
.bb-product-image:has(.bb-thumb-empty) {
	display: none;
}
/* The empty-state placeholder image must never render as a grey "no image"
   box, in ANY layout. The Configurator restructures this thumbnail into its
   own container, so the old `.bb-product-image.bundle-item-image` scoping let
   the placeholder leak there. Target the image itself and force it down so it
   is hidden whatever container it ends up in; the dashed "+" tile above still
   shows because `:has(.bb-thumb-empty)` matches the (hidden) image in the DOM. */
img.bb-thumb-empty { display: none !important; }

/* ── Personalisation add-on fields ─────────────────────────────────── */
.bb-personalise {
	/* A small left indent + accent left edge so the panel reads as nested under
	   the item it personalises (the same language the cart line uses), not as a
	   separate thing. Units-cards resets the indent below (flex-basis 100%). */
	margin: 10px 0 2px 14px;
	background: var(--bb-surface-faint);
	border: 1px solid var(--bb-border);
	border-left: 3px solid var(--bb-accent);
	border-radius: 10px;
	overflow: hidden;
}
.bb-personalise-item { background: var(--bb-accent-bg); }
.bb-personalise-toggle {
	cursor: pointer;
	list-style: none;
	display: flex;
	align-items: center;
	justify-content: space-between;
	gap: 9px;
	padding: 11px 14px;
	font-size: 13px;
	font-weight: 600;
	color: var(--bb-accent-deep);
}
.bb-personalise-toggle::-webkit-details-marker { display: none; }
.bb-personalise-toggle::marker { content: ''; }
.bb-personalise-toggle:hover { color: var(--bb-accent); }
/* Chevron: the most recognised expand/collapse signifier. Points down when
   collapsed, rotates up when open — never reads as a close (×). */
.bb-personalise-caret {
	flex: none;
	width: 8px;
	height: 8px;
	border-right: 2px solid currentColor;
	border-bottom: 2px solid currentColor;
	transform: rotate(45deg);
	transition: transform 0.2s ease;
	margin-bottom: 3px;
}
.bb-personalise[open] .bb-personalise-caret { transform: rotate(-135deg); }
.bb-personalise-fields {
	display: grid;
	gap: 10px;
	padding: 2px 12px 12px;
}
/* Personalisation tabbed in under the line it belongs to, in the recap. */
.bb-recap-addon-row .bb-recap-name {
	padding-left: 22px;
	font-size: 12px;
	font-style: italic;
	color: var(--bb-muted);
	position: relative;
}
.bb-recap-addon-row .bb-recap-name::before {
	content: '\21B3'; /* ↳ sub-item marker */
	position: absolute;
	left: 8px;
	font-style: normal;
	color: var(--bb-muted-light);
}
.bb-recap-addon-row .bb-recap-price {
	font-size: 12px;
	color: var(--bb-success);
}
/* A bundle-wide offer/fee (BOGO saving, gift wrap): its own full-width row,
   not indented under the item above it. */
.bb-recap-offer-row {
	margin-top: 3px;
	padding-top: 6px !important;
	border-top: 1px dashed var(--bb-border, #e5e7eb);
}
.bb-recap-offer-row .bb-recap-name {
	font-weight: 600;
	display: inline-flex;
	align-items: center;
	gap: 6px;
}
.bb-recap-offer-row .bb-recap-price {
	font-weight: 600;
	white-space: nowrap;
}
/* A saving (BOGO): green, with a tick. A fee (gift wrap) stays neutral so a
   plus-charge never reads as a discount. */
.bb-recap-offer-saving .bb-recap-name,
.bb-recap-offer-saving .bb-recap-price {
	color: var(--bb-success);
}
.bb-recap-offer-saving .bb-recap-name::before {
	content: '\2713'; /* ✓ */
	font-size: 0.9em;
}
.bb-addon-field { display: grid; gap: 4px; align-items: start; }
.bb-addon-label {
	font-size: 13px;
	font-weight: 600;
	color: var(--bb-text);
}
/* The word "Required" beside a label: the accent-soft pill, small caps,
   everywhere the word appears (field labels, set heads, section rows), so
   the whole panel wears one marking grammar. It replaced an aria-hidden red
   asterisk first and plain red text second. The literal ground declares
   before the color-mix ground, so an older browser keeps a readable pill. */
.bb-addon-req { display: inline-block; color: var(--bb-accent); font-size: 10.5px; font-weight: 700;
	letter-spacing: 0.05em; background: #fff1ee; background: color-mix(in srgb, currentColor 9%, transparent);
	border-radius: 99px; padding: 2px 9px; line-height: 1.5; text-transform: none;
	font-variant-caps: all-small-caps; white-space: nowrap; vertical-align: middle; }
.bb-addon-input {
	width: 100%;
	padding: 7px 10px;
	border: 1px solid var(--bb-border-strong);
	border-radius: 8px;
	font-size: 14px;
	background: var(--bb-surface);
	color: var(--bb-ink);
	box-sizing: border-box;
}
.bb-addon-input:focus {
	outline: none;
	border-color: var(--bb-accent);
	box-shadow: 0 0 0 2px var(--bb-accent-bg);
}
textarea.bb-addon-input { min-height: 56px; resize: vertical; }
.bb-addon-check {
	display: flex;
	align-items: center;
	gap: 8px;
	font-size: 14px;
	color: var(--bb-text);
	cursor: pointer;
}
.bb-addon-note {
	font-size: 13px;
	font-weight: 600;
	color: var(--bb-ink-soft);
	border-bottom: 1px solid var(--bb-border);
	padding-bottom: 4px;
}
/* What an option adds, stated quietly.
   It used to be a filled green pill, and inside a flex column an inline-block
   child stretches, so it drew a full-width bar under every field: louder than
   the input it prices, and on an empty optional field it read as a charge that
   had already been applied when nothing was being charged at all.
   No real implementation does this. The convention is a small "+£5.00" beside
   the field at label size, with the running total carrying the emphasis, so
   that is what this is now. align-self stops the flex parent stretching it. */
.bb-addon-fee {
	display: inline-block;
	align-self: flex-start;
	font-size: 12px;
	font-weight: 600;
	color: var(--bb-muted);
	background: none;
	border: 0;
	padding: 0;
	white-space: nowrap;
}
/* An EMPTY optional priced field must not advertise its fee as if it were
   already being charged. While the group is unfilled the figure reads as a
   quiet rate (its "per character" style suffix already says what it is a
   rate OF), firming to the charged colour the moment the field is actually
   filled. The is-filled class is the hook the field polish script already
   keeps; hiding the price entirely would be the opposite failure, a charge
   disclosed only after the shopper has typed. */
.bb-field-group .bb-addon-fee { color: var(--bb-muted-light); font-weight: 500; transition: color 0.15s ease; }
.bb-field-group.is-filled .bb-addon-fee { color: var(--bb-ink); font-weight: 600; }

/* The typeface and colour rows under a placement field: the same chip anatomy
   the option sets use (a font chip wears its own typeface, a colour is the
   swatch dot), so the shopper meets one control language on the whole page. */
.bb-style-pick { display: grid; gap: 8px; margin-top: 8px; }
.bb-style-row { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.bb-style-lab { font-size: 12px; font-weight: 600; color: var(--bb-text); min-width: 62px; }

.bb-addon-upload { display: flex; align-items: center; gap: 10px; flex-wrap: wrap; }
.bb-addon-file-btn {
	display: inline-flex;
	align-items: center;
	gap: 6px;
	padding: 7px 13px;
	border: 1px dashed var(--bb-border-strong);
	border-radius: 8px;
	background: var(--bb-surface);
	color: var(--bb-text);
	font-size: 13px;
	font-weight: 600;
	cursor: pointer;
}
.bb-addon-file-btn:hover { border-color: var(--bb-accent); color: var(--bb-accent); }
.bb-addon-file-input { position: absolute; width: 1px; height: 1px; opacity: 0; overflow: hidden; }
.bb-addon-file-status { font-size: 13px; color: var(--bb-muted); display: inline-flex; align-items: center; gap: 6px; }
.bb-addon-file-name { color: var(--bb-text); font-weight: 600; }
.bb-addon-file-remove { border: 0; background: none; cursor: pointer; color: var(--bb-error); font-size: 16px; line-height: 1; padding: 0 2px; }
.bb-addon-upload.is-uploading .bb-addon-file-status { color: var(--bb-accent); }
/* Drag-and-drop: the whole upload row is a drop target. `outline` (not border)
   highlights it without nudging the layout, and the Choose file button firms up
   from dashed to a solid coral so the drop point reads clearly. */
.bb-addon-drop-hint { font-size: 12px; color: var(--bb-muted); }
.bb-addon-upload.is-dragover { outline: 2px dashed var(--bb-accent); outline-offset: 4px; border-radius: 10px; }
.bb-addon-upload.is-dragover .bb-addon-file-btn { border-style: solid; border-color: var(--bb-accent); background: var(--bb-surface-faint); color: var(--bb-accent); }
/* Determinate upload progress: a thin coral fill on its own line under the
   button, so a large print file shows real movement instead of a frozen dot. */
.bb-addon-file-bar { display: block; flex-basis: 100%; height: 5px; border-radius: 3px; background: var(--bb-surface-alt); overflow: hidden; }
.bb-addon-file-bar > i { display: block; height: 100%; width: 0; background: var(--bb-accent); border-radius: 3px; transition: width .15s ease; }
/* These class rules set their own `display`, which beats the UA [hidden] rule,
   so the hidden attribute has to be honoured explicitly or the button lingers
   behind the chip and the empty bar shows before an upload starts. */
.bb-addon-file-btn[hidden], .bb-addon-drop-hint[hidden], .bb-addon-file-bar[hidden] { display: none; }
/* The artwork verdict: an advisory beside the file, amber rather than red,
   because the seller allowed the sale to continue. A refusal never renders
   here (the upload itself failed with the reason). */
.bb-addon-art-note { flex-basis: 100%; margin: 2px 0 0; padding: 9px 12px; border: 1px solid #e6b34d; border-left-width: 3px; border-radius: 8px; background: #fdf6e6; }
.bb-addon-art-note p { margin: 0; font-size: 12.5px; line-height: 1.5; color: #6b4e12; }
.bb-addon-art-note p + p { margin-top: 5px; }

/* ── Main image: hide mode ─────────────────────────────────────────
   The merchant chose to drop the big gallery image on this bundle's page (the
   configurator items carry their own photos). Hide the gallery and let the
   summary, which holds the configurator, take the full width. Targets the
   common WooCommerce theme structure; harmless where a theme differs. */
body.bb-hide-main-image div.product .woocommerce-product-gallery {
	display: none !important;
}
body.bb-hide-main-image div.product .summary,
body.bb-hide-main-image div.product .entry-summary {
	width: 100% !important;
	max-width: none !important;
	float: none !important;
	margin-left: 0 !important;
}

/* BOGO / Buy X Get Y offer pill (Pro) — at the top of the configurator, by the
   pricing pills, so the deal is seen before the customer configures. */
.bb-bogo-badge {
	display: inline-block;
	margin: 0 0 14px;
	padding: 6px 15px;
	border-radius: 999px;
	background: var(--bb-success-bg, #e7f6ec);
	color: var(--bb-success, #1a7f37);
	font-size: 13.5px;
	font-weight: 700;
}

/* ============================================================
   Layout consistency pass (2026-07)
   Cards align, long names clamp, imageless products get a quiet
   tile, and secondary sections read as sections.
   ============================================================ */

/* Option/candidate cards: clamp long names to two lines, reserve the
   space either way, and pin the price row to the bottom so a row of
   cards keeps one baseline no matter how names wrap. */
.bb-candidate-name {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    min-height: calc( 1.3em * 2 );
}

.bb-candidate-price { margin-top: auto; }

/* Candidates keep one card width everywhere (the configurator's
   sections otherwise stretch them to different sizes per row). */
.bb-candidate { max-width: 210px; }

/* Box cards: the same discipline. */
.bb-box-card { display: flex; flex-direction: column; }

.bb-box-card-name {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
    min-height: calc( 1.35em * 2 );
}

.bb-box-card-price { margin-top: auto; }

/* ===== Order builder: matrix mode (accordion rows + colour x size grid) ===== */
/* A box in matrix mode is a single column of full-width rows; each design's
   header toggles its own colour x size grid open (one at a time). The grid is a
   real table so sizes are columns (not an endless vertical stack). */
.bb-box-grid.bb-box-grid-matrix { display: flex; flex-direction: column; gap: 10px; }

.bb-box-grid-matrix .bb-box-card-matrix {
    display: flex; flex-direction: row; flex-wrap: wrap; align-items: center; gap: 8px 12px;
    padding: 12px 14px; cursor: pointer;
    border: 1px solid var(--bb-line, #e4e8ef); border-radius: 12px; background: var(--bb-card, #fff);
    transition: border-color .15s ease;
}
.bb-box-card-matrix.bb-box-card-chosen { border-color: color-mix( in srgb, var(--bb-accent, #3466e6) 45%, var(--bb-line, #e4e8ef) ); }
.bb-box-card-matrix.is-open { border-color: var(--bb-accent, #3466e6); }
.bb-box-card-matrix > .bb-box-card-img,
.bb-box-card-matrix > .bb-noimg { width: 50px; height: 50px; min-height: 0; border-radius: 8px; object-fit: cover; flex: none; }
.bb-box-card-matrix > .bb-box-card-name { flex: 1 1 140px; min-height: 0; display: block; -webkit-line-clamp: unset; overflow: visible; font-weight: 600; }
.bb-box-card-matrix > .bb-box-card-price { margin: 0; flex: none; color: var(--bb-muted, #5c6780); font-size: .9em; }
.bb-box-card-picks { flex-basis: 100%; order: 50; display: flex; flex-wrap: wrap; gap: 4px 10px; font-size: .8em; color: var(--bb-muted, #5c6780); }
.bb-box-card-picks:empty { display: none; }
.bb-box-card-picks .bb-mpick { display: inline-flex; align-items: center; gap: 5px; }
.bb-box-card-picks .bb-mpick .bb-cd { width: 10px; height: 10px; border-radius: 50%; box-shadow: inset 0 0 0 1px rgba(0,0,0,.2); }
.bb-box-card-picks .bb-mpick b { color: var(--bb-ink, #1a2036); font-weight: 700; }
.bb-box-card-total { flex: none; font-weight: 700; font-variant-numeric: tabular-nums; color: var(--bb-green, #12805a); }
.bb-box-card-total:empty { display: none; }
.bb-box-card-chev { width: 20px; height: 20px; flex: none; color: var(--bb-muted, #8b96ad); transition: transform .2s ease; }
.bb-box-card-matrix.is-open .bb-box-card-chev { transform: rotate(180deg); color: var(--bb-accent, #3466e6); }
/* Smooth open: the grid eases from 0 to its natural height (the grid-template-rows
   0fr -> 1fr trick, which animates to auto height) with a gentle fade, instead of
   snapping the matrix in. */
.bb-box-card-matrix > .bb-card-matrix { flex-basis: 100%; order: 99; margin-top: 2px; cursor: default; display: grid; grid-template-rows: 0fr; opacity: 0; overflow: hidden; transition: grid-template-rows .28s ease, opacity .2s ease, margin-top .28s ease; margin-top: 0; }
.bb-box-card-matrix.is-open > .bb-card-matrix { grid-template-rows: 1fr; opacity: 1; margin-top: 6px; }
.bb-box-card-matrix > .bb-card-matrix > .bb-matrix-scroll { min-height: 0; }
@media ( prefers-reduced-motion: reduce ) { .bb-box-card-matrix > .bb-card-matrix { transition: none; } }

/* The card and the grid wrapper are flex/grid items; without min-width:0 they
   refuse to shrink below the table's intrinsic width, so the whole card stretches
   past the viewport and overflow-x never engages. min-width:0 lets the scroll box
   constrain to the card and scroll the table inside it (colour column stays pinned). */
.bb-box-grid-matrix .bb-box-card-matrix { min-width: 0; }
.bb-card-matrix { min-width: 0; max-width: 100%; position: relative; }
.bb-card-matrix .bb-matrix-scroll { overflow-x: auto; max-width: 100%; -webkit-overflow-scrolling: touch; border: 1px solid var(--bb-line, #e4e8ef); border-radius: 8px; background: var(--bb-card, #fff); }
/* Frozen panes for a wide size run: the colour column pins left and the subtotal
   pins right, so scrolling the sizes in the middle never loses the row's identity
   or its running total. Soft edge shadows show content slides under each — the
   standard, and far calmer than reading a 10-wide table end to end. */
.bb-matrix tbody th.bb-matrix-colour, .bb-matrix thead th.bb-matrix-corner { box-shadow: 7px 0 8px -8px rgba( 15, 23, 42, .3 ); }
.bb-matrix thead th.bb-matrix-sub, .bb-matrix td.bb-matrix-rowsub { position: sticky; right: 0; background: var( --bb-card, #fff ); box-shadow: -7px 0 8px -8px rgba( 15, 23, 42, .3 ); }
.bb-matrix thead th.bb-matrix-sub { z-index: 3; }
.bb-matrix td.bb-matrix-rowsub { z-index: 1; }
.bb-matrix { border-collapse: separate; border-spacing: 0; width: 100%; font-variant-numeric: tabular-nums; }
.bb-matrix th, .bb-matrix td { padding: 0; text-align: center; }
.bb-matrix thead th { position: sticky; top: 0; z-index: 3; background: var(--bb-card, #fff); color: var(--bb-muted, #5c6780); font-size: 11px; font-weight: 700; letter-spacing: .05em; text-transform: uppercase; padding: 9px 6px; border-bottom: 1px solid var(--bb-line-strong, #d3d9e4); white-space: nowrap; }
.bb-matrix thead th.bb-matrix-size { cursor: pointer; min-width: 58px; }
.bb-matrix thead th.bb-matrix-size:hover { color: var(--bb-accent, #3466e6); }
.bb-matrix thead th.bb-matrix-corner { left: 0; z-index: 4; text-align: left; padding-left: 12px; min-width: 118px; }
.bb-matrix thead th.bb-matrix-sub { text-align: right; padding-right: 12px; }
.bb-matrix tbody th.bb-matrix-colour { position: sticky; left: 0; z-index: 2; background: var(--bb-card, #fff); text-align: left; padding: 8px 10px 8px 12px; border-bottom: 1px solid var(--bb-line, #e4e8ef); font-weight: 500; font-size: 13.5px; white-space: nowrap; }
.bb-matrix tbody tr:last-child th.bb-matrix-colour, .bb-matrix tbody tr:last-child td { border-bottom: 0; }
/* Same treatment as the single-pick dot and the tile swatch, at row size. */
.bb-matrix-swatch { display: inline-block; width: 14px; height: 14px; border-radius: 50%; margin-right: 8px; vertical-align: -2px; box-shadow: inset 0 0 0 1px rgba(0,0,0,.12), 0 0 0 1px var(--bb-border); }
.bb-matrix-ccount { display: inline-flex; min-width: 20px; height: 18px; padding: 0 6px; margin-left: 9px; align-items: center; justify-content: center; border-radius: 999px; font-size: 11px; font-weight: 800; background: var(--bb-accent, #3466e6); color: #fff; vertical-align: 1px; }
.bb-matrix-ccount.bb-zero { background: transparent; color: var(--bb-muted, #8b96ad); box-shadow: inset 0 0 0 1px var(--bb-line-strong, #d3d9e4); }
.bb-matrix td.bb-matrix-cell { border-bottom: 1px solid var(--bb-line, #e4e8ef); border-left: 1px solid var(--bb-line, #e4e8ef); }
.bb-matrix td.bb-matrix-cell.bb-has-qty { background: color-mix( in srgb, var(--bb-accent, #3466e6) 8%, transparent ); }
.bb-matrix td.bb-matrix-gap { border-bottom: 1px solid var(--bb-line, #e4e8ef); border-left: 1px solid var(--bb-line, #e4e8ef); background: repeating-linear-gradient( 45deg, transparent, transparent 4px, rgba(0,0,0,.03) 4px, rgba(0,0,0,.03) 8px ); }
.bb-matrix td.bb-matrix-oos { border-bottom: 1px solid var(--bb-line, #e4e8ef); border-left: 1px solid var(--bb-line, #e4e8ef); color: var(--bb-muted, #8b96ad); }
.bb-matrix td.bb-matrix-rowsub { border-bottom: 1px solid var(--bb-line, #e4e8ef); text-align: right; padding-right: 12px; color: var(--bb-muted, #5c6780); font-size: 13px; min-width: 58px; }
.bb-matrix td.bb-matrix-rowsub b { color: var(--bb-ink, #1a2036); font-weight: 700; }
.bb-matrix .bb-grid-qty { width: 52px; height: 38px; text-align: center; border: 0; background: none; color: inherit; font: inherit; font-size: 15px; font-variant-numeric: tabular-nums; -moz-appearance: textfield; }
.bb-matrix .bb-grid-qty::-webkit-outer-spin-button, .bb-matrix .bb-grid-qty::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
.bb-matrix .bb-grid-qty:focus { outline: 2px solid var(--bb-accent, #3466e6); outline-offset: -2px; border-radius: 4px; }
/* Calm the grid: empty cells fade their "0" placeholder, filled cells get a bold
   accent value on the soft tint — so a size run reads at a glance instead of as a
   wall of identical zeros. */
.bb-matrix .bb-grid-qty::placeholder { color: var( --bb-line-strong, #d3d9e4 ); opacity: 1; }
.bb-matrix td.bb-matrix-cell.bb-has-qty .bb-grid-qty { font-weight: 700; color: var( --bb-accent, #3466e6 ); }
.bb-box-grid-matrix .bb-box-card-matrix { position: relative; }

/* A builder with no product image gets WooCommerce's full-width summary column,
   which let the matrix + design cards sprawl to ~1200px and read scattered. Cap
   the meter + grid together to a comfortable reading width (matrix boxes only, so
   the classic tile box is untouched); it left-aligns and has no effect in the
   usual ~50% product column. */
.bb-box:has( .bb-box-grid-matrix ) { max-width: 960px; }

/* ---- Gallery look: designs as a card grid; the opened one spans full width ---- */
.bb-box-grid.bb-box-grid-matrix.bb-box-layout-gallery { display: grid; grid-template-columns: repeat( auto-fill, minmax( 210px, 1fr ) ); gap: 12px; align-items: start; }
.bb-box-layout-gallery .bb-box-card-matrix { flex-direction: column; align-items: center; text-align: center; gap: 7px; padding: 14px; }
.bb-box-layout-gallery .bb-box-card-matrix > .bb-box-card-img,
.bb-box-layout-gallery .bb-box-card-matrix > .bb-noimg { width: 100%; height: 150px; border-radius: 8px; font-size: 34px; }
/* Show the whole garment, not a cover-cropped slice of it (product shots are
   portrait and were losing their neck/hem). Contain on a soft ground reads clean. */
.bb-box-layout-gallery .bb-box-card-matrix:not( .is-open ) > .bb-box-card-img { object-fit: contain; background: var( --bb-sunk, #f7f8fa ); padding: 6px; }
.bb-box-layout-gallery .bb-box-card-matrix > .bb-box-card-name { flex: none; font-size: 14px; }
.bb-box-layout-gallery .bb-box-card-picks { justify-content: center; }
.bb-box-layout-gallery .bb-box-card-chev { position: absolute; top: 12px; right: 12px; }
/* The open design leads the grid (order:-1) so it always sits full-width at the
   top with the remaining designs below it — instead of jumping to a full-width
   row wherever it was clicked and stranding the cards before it in a half-empty
   row (which read as the layout scattering, worst of all in a full-width column
   with no product image). */
.bb-box-layout-gallery .bb-box-card-matrix.is-open { grid-column: 1 / -1; order: -1; flex-direction: row; flex-wrap: wrap; align-items: center; text-align: left; scroll-margin-top: 84px; }
.bb-box-layout-gallery .bb-box-card-matrix.is-open > .bb-box-card-img,
.bb-box-layout-gallery .bb-box-card-matrix.is-open > .bb-noimg { width: 50px; height: 50px; font-size: 15px; }
.bb-box-layout-gallery .bb-box-card-matrix.is-open > .bb-box-card-name { flex: 1 1 140px; }
.bb-box-layout-gallery .bb-box-card-matrix.is-open .bb-box-card-chev { position: static; }

/* ---- Order-pad look: one continuous compact table of design rows (wholesale
   quick-order feel), thin dividers, small thumbs, tabular — distinct from the
   accordion's separated cards. Fast to scan a long range. ---- */
.bb-box-grid.bb-box-grid-matrix.bb-box-layout-pad { gap: 0; border: 1px solid var(--bb-line, #e4e8ef); border-radius: 10px; overflow: hidden; }
.bb-box-layout-pad .bb-box-card-matrix { border: 0; border-bottom: 1px solid var(--bb-line, #e4e8ef); border-radius: 0; padding: 8px 14px; gap: 4px 12px; }
.bb-box-layout-pad .bb-box-card-matrix:last-child { border-bottom: 0; }
.bb-box-layout-pad .bb-box-card-matrix.is-open { background: var(--bb-sunk, #fbfcfd); }
.bb-box-layout-pad .bb-box-card-matrix.bb-box-card-chosen { border-left: 3px solid var(--bb-accent, #3466e6); padding-left: 11px; }
.bb-box-layout-pad .bb-box-card-matrix > .bb-box-card-img,
.bb-box-layout-pad .bb-box-card-matrix > .bb-noimg { width: 38px; height: 38px; border-radius: 6px; font-size: 15px; }
.bb-box-layout-pad .bb-box-card-matrix > .bb-box-card-name { font-size: 13.5px; }
.bb-box-layout-pad .bb-box-card-matrix > .bb-card-matrix { margin-top: 6px; }

/* Grid layout: cards in a row stretch to one height, prices sit on one
   baseline, and long names clamp instead of pushing the photo around. */
.bundle-items.bundle-layout-grid {
    align-items: stretch;
}

/* A collapsed card folds to its own header height instead of stretching to
   its row-mate (which left an empty white panel until the neighbour was
   collapsed too). */
.bundle-items.bundle-layout-grid .bundle-item.bundle-item--collapsed {
    align-self: start;
}

.bundle-layout-grid .bundle-item {
    display: flex;
    flex-direction: column;
}

.bundle-layout-grid .bb-card-body.bundle-item-inner {
    flex: 1 1 auto;
}

.bundle-layout-grid .bundle-item-price {
    margin-top: auto;
}

/* In a grid card the no-photo tile fills the image panel edge to edge. */
.bundle-layout-grid .bb-noimg {
    max-width: none;
    width: 100%;
    height: 100%;
    aspect-ratio: auto;
    border-radius: 0;
}

.bundle-layout-grid .bb-item-name {
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
}

/* A product with no photo gets a quiet initial tile, never the broken
   placeholder frame. */
.bb-noimg {
    display: flex;
    align-items: center;
    justify-content: center;
    width: 100%;
    max-width: 96px;
    aspect-ratio: 1 / 1;
    background: color-mix( in srgb, var(--bb-border) 30%, var(--bb-surface) );
    color: var(--bb-text);
    opacity: 0.8;
    font-weight: 700;
    font-size: 1.3em;
    border-radius: 10px;
}

/* Gift options read as a section, not a stray checkbox. */
.bb-gift {
    border: 1.5px solid var(--bb-border);
    border-radius: 14px;
    padding: 14px 16px;
    margin-top: 14px;
    background: var(--bb-surface);
}

/* Sell the gift, not just offer a checkbox: icon, bold head and a one-line
   promise, so the feature is visible before the box is ticked. */
.bb-gift-toggle {
    display: flex;
    align-items: flex-start;
    gap: 10px;
    cursor: pointer;
}
.bb-gift-toggle > input { margin-top: 3px; }
.bb-gift-icon {
    color: var(--bb-accent, #7c3aed);
    display: inline-flex;
    margin-top: 2px;
}
.bb-gift-copy { display: flex; flex-direction: column; gap: 2px; }
.bb-gift-copy strong { font-size: 0.98em; }
.bb-gift-sub {
    font-size: 0.85em;
    color: var(--bb-text);
}


/* A units-table price cell waiting on a choice shows a quiet pending
   mark instead of sitting empty under its header. */
.bb-unit-price:empty::before {
    content: "...";
    color: var(--bb-text);
    opacity: 0.45;
}

/* The opt-in "view product" arrow: a small, deliberate target beside the
   name. Muted until hovered so it never competes with choosing. */
.bb-view-link {
    display: inline-flex;
    align-items: center;
    justify-content: center;
    width: 22px;
    height: 22px;
    margin-left: 4px;
    vertical-align: -3px;
    border: 1px solid var(--bb-border);
    border-radius: 50%;
    color: var(--bb-text);
    opacity: 0.65;
    text-decoration: none;
    transition: opacity 0.12s, border-color 0.12s, color 0.12s;
}

.bb-view-link:hover,
.bb-view-link:focus {
    opacity: 1;
    color: var(--bb-accent-deep, var(--bb-ink));
    border-color: currentColor;
    text-decoration: none;
}

/* ── Buy-more-bundles quantity (Phase 1) ──────────────────────── */
.bb-bundle-qty {
    display: flex;
    align-items: center;
    gap: 12px;
    flex-wrap: wrap;
    margin: 0 0 14px;
}
.bb-qty-label { font-weight: 600; color: var(--bb-ink); }
.bb-qty-stepper {
    display: inline-flex;
    align-items: stretch;
    border: 1px solid var(--bb-border);
    border-radius: 10px;
    overflow: hidden;
    background: var(--bb-surface);
}
.bb-qty-btn {
    border: 0;
    background: var(--bb-summary-bg, #f1f2f5);
    color: var(--bb-ink);
    width: 38px;
    font-size: 1.1rem;
    line-height: 1;
    cursor: pointer;
    padding: 0;
}
.bb-qty-btn:hover { background: var(--bb-border); }
.bb-qty-input {
    width: 3.2em;
    text-align: center;
    border: 0;
    border-left: 1px solid var(--bb-border);
    border-right: 1px solid var(--bb-border);
    font-weight: 600;
    -moz-appearance: textfield;
    padding: 8px 4px;
    background: var(--bb-surface);
    color: var(--bb-ink);
}
.bb-qty-input::-webkit-outer-spin-button,
.bb-qty-input::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }

/* The tray's state machine flips elements with the HTML hidden attribute.
   Theme or "custom CSS" rules that set display on .button with !important
   (".woocommerce button.button { display: inline-flex !important }" is
   everywhere in the wild) override the attribute's display:none and paint
   "hidden" buttons anyway — the two-buttons-at-once bug. The repeated
   [hidden] lifts this to (0,4,0) so it also beats compound selectors like
   ".woocommerce div.product form.cart .button". This guards the first paint
   and anything not JS-managed; elements bbSetHidden() manages additionally
   carry an inline display:none !important, which nothing can override. */
.bb-order-tray [hidden][hidden][hidden],
.bb-tray-peek[hidden][hidden][hidden],
.bundle-boss-form [hidden][hidden][hidden] {
    display: none !important;
}

/* =================================================================
   Slot options: per-unit option chips on simple products (Pro).
   Same visual language as variation chips; the selected ring is the
   class the module JS toggles.
   ================================================================= */
.bundle-boss-form .bb-simple-unit { padding: 10px 0 6px; border-top: 1px solid var(--bb-border, #e6e8ec); }
.bundle-boss-form .bb-simple-unit:first-of-type { border-top: 0; }
.bundle-boss-form .bb-unit-title { display: block; font-weight: 600; font-size: 13px; margin-bottom: 2px; }
.bundle-boss-form .bb-unit-options { display: flex; flex-wrap: wrap; align-items: center; gap: 8px; margin: 8px 0 4px; }
.bundle-boss-form .bb-unit-options-label { flex-basis: 100%; font-size: 12px; text-transform: uppercase; letter-spacing: 0.4px; color: #6b7280; }
.bundle-boss-form .bb-chip--option { display: inline-flex; align-items: center; gap: 7px; cursor: pointer; user-select: none; }
.bundle-boss-form .bb-chip--option .bb-chip-delta { color: #6b7280; font-size: 0.85em; }
.bundle-boss-form .bb-chip--option .bb-chip-delta .amount { font-size: inherit; }
.bundle-boss-form .bb-chip--option.bb-chip--selected { border-color: var(--bb-accent, #fd4730); box-shadow: 0 0 0 1px var(--bb-accent, #fd4730); font-weight: 600; }

/* Slot-option chips, scale fixes: our own radio clipping (themes cannot
   be trusted to ship screen-reader-text inside the form) and the dot. */
.bundle-boss-form .bb-chip--option .bb-option-pick { position: absolute; width: 1px; height: 1px; opacity: 0; margin: -1px; overflow: hidden; clip: rect(0 0 0 0); pointer-events: none; }
.bundle-boss-form .bb-chip--option { position: relative; }
.bundle-boss-form .bb-chip--option .bb-opt-dot { display: inline-block; width: 13px; height: 13px; border-radius: 50%; border: 1px solid rgba(0, 0, 0, 0.15); flex-shrink: 0; }

/* ---- On-photo personalisation: side tabs, the enlarged view, upload chip ----
   The side tabs and the enlarge modal are built by the overlay script, which
   ships only on a product that actually places something on its photo. */
.bb-preview-sides { display: flex; flex-wrap: wrap; gap: 8px; margin: 0 0 12px; }
.bb-preview-side {
	position: relative;
	cursor: pointer;
	border: 1px solid var(--bb-border);
	background: var(--bb-surface);
	color: var(--bb-ink);
	border-radius: 8px;
	padding: 7px 14px;
	font: inherit;
	font-size: 0.9em;
	font-weight: 600;
	line-height: 1;
}
.bb-preview-side:hover { border-color: var(--bb-accent); }
.bb-preview-side.is-active { border-color: var(--bb-accent); background: var(--bb-accent-bg); }
.bb-preview-side:focus-visible { outline: 2px solid var(--bb-accent); outline-offset: 2px; }
/* A side with its own photo becomes a labelled thumbnail: the picture of the
   back reads faster than the word. */
.bb-preview-side.has-thumb { display: flex; flex-direction: column; align-items: center; gap: 5px; padding: 6px; width: 78px; }
.bb-preview-sidethumb { width: 100%; height: 58px; object-fit: contain; border-radius: 5px; background: var(--bb-surface-alt); display: block; pointer-events: none; }
.bb-preview-side.has-thumb .bb-preview-sidelabel { font-size: 0.82em; font-weight: 700; }
.bb-preview-sidedot { display: inline-block; width: 7px; height: 7px; border-radius: 50%; background: var(--bb-accent); margin-left: 7px; vertical-align: middle; opacity: 0; transition: opacity .15s; }
.bb-preview-side.has-content .bb-preview-sidedot { opacity: 1; }
.bb-preview-side.has-thumb .bb-preview-sidedot { position: absolute; top: 6px; right: 6px; margin: 0; }

.bb-preview-modal { position: fixed; inset: 0; z-index: 100000; background: rgba( 15, 17, 22, .82 ); display: flex; align-items: center; justify-content: center; padding: 24px; }
.bb-preview-modal-inner { position: relative; display: flex; flex-direction: column; align-items: center; gap: 12px; max-width: 100%; max-height: 100%; }
.bb-preview-modal-canvas { max-width: 90vw; max-height: 78vh; background: var(--bb-surface); border-radius: 10px; box-shadow: 0 20px 60px rgba( 0, 0, 0, .5 ); }
.bb-preview-modal-close { position: absolute; top: -14px; right: -14px; width: 36px; height: 36px; border-radius: 50%; border: 0; background: var(--bb-surface); color: var(--bb-ink); font-size: 15px; cursor: pointer; box-shadow: 0 2px 8px rgba( 0, 0, 0, .3 ); }
.bb-preview-modal-tabs { display: flex; gap: 8px; flex-wrap: wrap; justify-content: center; }

/* The upload after-state: the button collapses into a chip showing the artwork
   itself, so the shopper can see WHAT they attached, not just its filename. */
.bb-file-chip { display: inline-flex; align-items: center; gap: 9px; padding: 6px 9px; border: 1px solid var(--bb-border); border-radius: 9px; background: var(--bb-surface-faint); max-width: 100%; }
.bb-file-thumb { flex: none; width: 38px; height: 38px; border-radius: 6px; overflow: hidden; background: var(--bb-surface-alt); display: flex; align-items: center; justify-content: center; color: var(--bb-muted); }
.bb-file-thumb img { width: 100%; height: 100%; object-fit: cover; display: block; }
.bb-file-thumb svg { width: 19px; height: 19px; }
.bb-file-meta { display: flex; flex-direction: column; gap: 2px; min-width: 0; }
.bb-file-fn { font-size: 13px; font-weight: 600; color: var(--bb-text); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; max-width: 210px; }
.bb-file-fs { font-size: 11px; color: var(--bb-muted); display: inline-flex; align-items: center; gap: 7px; }
.bb-file-ok { display: inline-flex; align-items: center; gap: 3px; color: var(--bb-success); font-weight: 600; }
.bb-file-ok svg { width: 11px; height: 11px; }
.bb-file-rm { flex: none; border: 0; background: none; cursor: pointer; color: var(--bb-muted); font-size: 16px; line-height: 1; padding: 2px 4px; }
.bb-file-rm:hover { color: var(--bb-error); }

/* A multi-file field: one row per file. A ready row IS the chip; an uploading
   or failed row wears the same quiet card so the set reads as one list. */
.bb-addon-file-rows { flex-basis: 100%; display: flex; flex-direction: column; gap: 6px; }
.bb-addon-file-rows:empty { display: none; }
.bb-addon-file-row { display: flex; align-items: center; flex-wrap: wrap; gap: 9px; }
.bb-addon-file-row.is-uploading, .bb-addon-file-row.is-failed { padding: 6px 9px; border: 1px solid var(--bb-border); border-radius: 9px; background: var(--bb-surface-faint); }
.bb-addon-file-row.is-failed { border-color: #f0c9bf; background: #fff6f3; }
.bb-addon-file-row .bb-file-chip { width: 100%; }
/* The row's own bar sits beside its name, not across the whole box. */
.bb-addon-file-row .bb-addon-file-bar { flex: 1 1 56px; flex-basis: 56px; max-width: 110px; margin-left: auto; }
.bb-addon-file-rowstatus { font-size: 12px; color: var(--bb-muted); white-space: nowrap; }
.bb-addon-file-err { font-size: 12.5px; color: #8a5a13; font-weight: 600; }
.bb-addon-file-row .bb-addon-art-note, .bb-addon-file-row .bb-addon-img-note { flex-basis: 100%; }
/* The count line: how much room is left, or the kind cap sentence. */
.bb-addon-file-count { flex-basis: 100%; font-size: 12.5px; color: var(--bb-muted); }
.bb-addon-file-count:empty { display: none; }
/* At the cap the pick control disables in place rather than vanishing. */
.bb-addon-upload-multi .bb-addon-file-btn.is-full,
.bb-addon-upload-multi .bb-addon-file-btn.is-full:hover { border-color: var(--bb-border); color: var(--bb-muted-light); background: var(--bb-surface); cursor: default; }

/* A live character count on a capped field, and a quiet done edge on a field the
   shopper has finished. Both are sugar: nothing depends on them. */
.bb-addon-counter { font-size: 11px; color: var(--bb-muted-light); font-variant-numeric: tabular-nums; }
.bb-addon-counter.is-near { color: var(--bb-warn); font-weight: 600; }
.bb-field-group.is-filled { border-left: 2px solid var(--bb-success-bright); padding-left: 9px; }

/* ---- Positions on the storefront: pick where it goes -----------------------
   Tiles are the merchant's own photo of that side with the print area marked,
   so a shopper sees the real product rather than a generic outline. */
.bb-positions-pick { margin: 0 0 1.1em; padding: 14px; border: 1px solid var(--bb-border); border-radius: 12px; background: var(--bb-surface); }
.bb-pp-head { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin: 0 0 11px; }
.bb-pp-title { font-weight: 700; font-size: 1.02em; color: var(--bb-ink); }
.bb-pp-count { font-size: 0.82em; color: var(--bb-muted); font-variant-numeric: tabular-nums; }
.bb-pp-tiles { display: grid; grid-template-columns: repeat( auto-fill, minmax( 128px, 1fr ) ); gap: 10px; }
.bb-pp-tile { position: relative; display: flex; flex-direction: column; align-items: center; gap: 5px; padding: 8px 8px 9px; border: 1.5px solid var(--bb-border); border-radius: 11px; background: var(--bb-surface); color: var(--bb-ink); font: inherit; cursor: pointer; text-align: center; transition: border-color .14s, box-shadow .14s, background .14s; }
.bb-pp-tile:hover { border-color: var(--bb-border-strong); }
.bb-pp-tile.is-on { border-color: var(--bb-accent); box-shadow: 0 0 0 1px var(--bb-accent); background: var(--bb-accent-bg); }
.bb-pp-tile:focus-visible { outline: 2px solid var(--bb-accent); outline-offset: 2px; }
.bb-pp-tick { position: absolute; top: 6px; right: 6px; z-index: 2; width: 19px; height: 19px; border-radius: 50%; background: var(--bb-accent); color: #fff; font-size: 11px; line-height: 19px; opacity: 0; transform: scale( .6 ); transition: opacity .13s, transform .13s; }
.bb-pp-tile.is-on .bb-pp-tick { opacity: 1; transform: scale( 1 ); }
.bb-pp-fee { position: absolute; top: 6px; left: 6px; z-index: 2; font-size: 0.68em; font-weight: 700; background: var(--bb-surface); border: 1px solid var(--bb-border); border-radius: 20px; padding: 2px 7px; }
.bb-pp-shot { position: relative; display: block; width: 100%; aspect-ratio: 1 / 1; background: var(--bb-surface-alt); border-radius: 7px; overflow: hidden; }
.bb-pp-shot img { width: 100%; height: 100%; object-fit: contain; display: block; }
/* A photo OF the area is framed on the thing already, so it fills the tile.
   A photo of the whole product must stay contained, or the marked area gets
   cropped off the edge and points at nothing. */
.bb-pp-shot.is-own img { object-fit: cover; }
.bb-pp-mark { position: absolute; border: 1px dashed var(--bb-accent); background: color-mix( in srgb, var(--bb-accent) 16%, transparent ); border-radius: 2px; pointer-events: none; }
.bb-pp-name { font-size: 0.86em; font-weight: 700; line-height: 1.25; }
.bb-pp-ways { font-size: 0.7em; color: var(--bb-muted); line-height: 1.35; }

.bb-pp-panels { display: flex; flex-direction: column; gap: 10px; }
.bb-pp-panels:not(:empty) { margin-top: 12px; }
.bb-pp-panel { border: 1px solid var(--bb-border); border-radius: 10px; overflow: hidden; }
.bb-pp-panel[hidden] { display: none; }
.bb-pp-panel-h { display: flex; align-items: baseline; gap: 9px; padding: 9px 12px; background: var(--bb-surface-faint); }
.bb-pp-panel-t { font-weight: 700; font-size: 0.92em; }
.bb-pp-panel-s { font-size: 0.76em; color: var(--bb-muted); }
.bb-pp-panel-b { display: flex; flex-direction: column; gap: 10px; padding: 12px; border-top: 1px solid var(--bb-border); }
.bb-pp-field { display: flex; flex-direction: column; gap: 4px; }
.bb-pp-field-s { max-width: 260px; }
.bb-pp-lab { display: flex; align-items: baseline; justify-content: space-between; gap: 10px; font-size: 0.82em; font-weight: 700; color: var(--bb-text); }
.bb-pp-panel-b input[type="text"], .bb-pp-panel-b select { width: 100%; box-sizing: border-box; font: inherit; font-size: 0.95em; padding: 8px 10px; border: 1px solid var(--bb-border-strong); border-radius: 8px; background: var(--bb-surface); color: var(--bb-ink); }

.bb-pp-total { display: flex; align-items: baseline; justify-content: space-between; gap: 12px; margin-top: 12px; padding: 10px 12px; background: var(--bb-surface-faint); border: 1px solid var(--bb-border); border-radius: 10px; font-size: 0.88em; }
.bb-pp-total[hidden] { display: none; }
.bb-pp-sum { font-size: 1.15em; font-weight: 700; }

@media ( max-width: 480px ) {
	.bb-pp-tiles { grid-template-columns: repeat( auto-fill, minmax( 104px, 1fr ) ); }
}

/* A tile is a <button>, so a theme's button:hover / :focus / :active rules
   out-specificity a plain class and repaint the label white on our light
   selected background. Scoped and stated for every state, so the name and the
   price stay readable in any theme. */
.bb-positions-pick .bb-pp-tile,
.bb-positions-pick .bb-pp-tile:hover,
.bb-positions-pick .bb-pp-tile:focus,
.bb-positions-pick .bb-pp-tile:active,
.bb-positions-pick .bb-pp-tile.is-on,
.bb-positions-pick .bb-pp-tile.is-on:hover,
.bb-positions-pick .bb-pp-tile.is-on:focus {
	color: var(--bb-ink);
	text-shadow: none;
	box-shadow: none;
}
.bb-positions-pick .bb-pp-tile.is-on,
.bb-positions-pick .bb-pp-tile.is-on:hover,
.bb-positions-pick .bb-pp-tile.is-on:focus { background: var(--bb-accent-bg); border-color: var(--bb-accent); box-shadow: 0 0 0 1px var(--bb-accent); }
.bb-positions-pick .bb-pp-tile:not(.is-on),
.bb-positions-pick .bb-pp-tile:not(.is-on):hover,
.bb-positions-pick .bb-pp-tile:not(.is-on):focus { background: var(--bb-surface); }
.bb-positions-pick .bb-pp-tile .bb-pp-name { color: var(--bb-ink); }
.bb-positions-pick .bb-pp-tile .bb-pp-ways { color: var(--bb-muted); }
.bb-positions-pick .bb-pp-tile .bb-pp-fee { color: var(--bb-ink); background: var(--bb-surface); }
.bb-positions-pick .bb-pp-tile .bb-pp-tick { color: #fff; }
/* Tiles line up on a row even when one has ways to apply it and another does not. */
.bb-pp-tiles { align-items: stretch; }
.bb-positions-pick .bb-pp-tile { justify-content: flex-start; }

/* ---- how the block presents its entry --------------------------------------
   Card (default): the same box, given weight so it reads as "do this" not an
   ignorable field stack. Accordion / button: a prominent launcher that keeps
   the buy box short, the fields still inside the cart form. */
.bb-positions-pick.bb-pp--card { border-color: var(--bb-accent); box-shadow: 0 0 0 1px var(--bb-accent-bg); }
.bb-pp--card .bb-pp-head { margin: -14px -14px 12px; padding: 11px 14px; background: var(--bb-accent-bg); border-bottom: 1px solid var(--bb-accent); border-radius: 12px 12px 0 0; }
.bb-pp--card .bb-pp-title { color: var(--bb-accent); }

.bb-pp--accordion, .bb-pp--button { padding: 0; border: 0; background: none; }
.bb-pp--accordion .bb-pp-head, .bb-pp--button .bb-pp-head { display: none; }

.bb-pp-launch { width: 100%; display: flex; align-items: center; gap: 12px; text-align: left; cursor: pointer;
  border: 1px solid var(--bb-accent); background: var(--bb-accent-bg); color: var(--bb-ink);
  border-radius: 12px; padding: 12px 14px; font: inherit; }
.bb-pp-launch-ic { flex: none; width: 30px; height: 30px; border-radius: 8px; background: var(--bb-accent); color: #fff; display: grid; place-items: center; }
.bb-pp-launch-txt { display: flex; flex-direction: column; line-height: 1.25; }
.bb-pp-launch-t { font-weight: 700; }
.bb-pp-launch-s { font-size: 0.85em; color: var(--bb-muted); }
.bb-pp-launch-chev { margin-left: auto; color: var(--bb-accent); font-size: 1.4em; line-height: 1; transition: transform 0.2s ease; }
.bb-pp-launch[aria-expanded="true"] .bb-pp-launch-chev { transform: rotate( 90deg ); }
.bb-pp-drawer { border: 1px solid var(--bb-border); border-top: 0; border-radius: 0 0 12px 12px; padding: 14px; margin-top: -1px; }
.bb-pp-drawer[hidden] { display: none; }

/* Distinct from the theme's Add-to-cart on purpose: an accent-outlined button,
   not a filled one, so it never reads as a second cart button. The !importants
   beat a theme that paints every button its own colour. */
.bb-positions-pick .bb-pp-open { width: 100%; display: flex; align-items: center; justify-content: center; gap: 9px; cursor: pointer;
  border: 1.5px solid var(--bb-accent) !important; background: var(--bb-accent-bg) !important; color: var(--bb-accent) !important;
  border-radius: 11px; padding: 12px 14px; font: inherit; font-weight: 700; box-shadow: none; text-shadow: none; }
.bb-positions-pick .bb-pp-open:hover { background: var(--bb-surface) !important; }
.bb-pp-open .bb-pp-launch-ic { width: 26px; height: 26px; border-radius: 7px; background: var(--bb-accent); color: #fff; display: grid; place-items: center; flex: none; }
.bb-pp-open-s { font-size: 0.85em; color: var(--bb-muted); font-weight: 400; }
.bb-pp-launch-ic .bb-pp-spark, .bb-pp-open .bb-pp-spark { display: block; }

.bb-pp-sheetwrap { position: fixed; inset: 0; z-index: 100000; }
.bb-pp-sheetwrap[hidden] { display: none; }
.bb-pp-scrim { position: absolute; inset: 0; background: rgba( 0, 0, 0, 0.45 ); }
.bb-pp-sheet { position: absolute; top: 0; right: 0; bottom: 0; width: min( 440px, 92vw ); background: var(--bb-surface);
  box-shadow: -8px 0 30px rgba( 0, 0, 0, 0.2 ); display: flex; flex-direction: column; animation: bbppSlideR 0.28s ease; }
@keyframes bbppSlideR { from { transform: translateX( 26px ); opacity: 0.4; } to { transform: none; opacity: 1; } }
.bb-pp-sheet-h { display: flex; align-items: center; gap: 10px; padding: 14px 16px; border-bottom: 1px solid var(--bb-border); background: var(--bb-accent-bg); }
.bb-pp-sheet-ic { flex: none; width: 28px; height: 28px; border-radius: 8px; background: var(--bb-accent); color: #fff; display: grid; place-items: center; }
.bb-pp-sheet-h b { font-size: 1.05em; color: var(--bb-accent); }
.bb-pp-close { margin-left: auto; border: 0; background: none; font-size: 1.2em; color: var(--bb-muted); cursor: pointer; line-height: 1; padding: 2px 4px; }
.bb-pp-sheet-b { flex: 1 1 auto; padding: 16px; overflow: auto; }
.bb-pp-sheet-f { flex: none; padding: 12px 16px; border-top: 1px solid var(--bb-border); background: var(--bb-surface); }
.bb-positions-pick .bb-pp-done { width: 100%; border: 0 !important; border-radius: 10px; background: var(--bb-accent) !important; color: #fff !important;
  font: inherit; font-weight: 700; padding: 12px; cursor: pointer; box-shadow: none; text-shadow: none; }
html.bb-pp-locked { overflow: hidden; }

/* On a phone the panel is a bottom sheet, so the keyboard never covers the preview. */
@media ( max-width: 600px ) {
  .bb-pp-sheet { top: auto; left: 0; right: 0; width: auto; max-height: 88vh; border-radius: 16px 16px 0 0; box-shadow: 0 -8px 30px rgba( 0, 0, 0, 0.2 ); animation: bbppSlideU 0.28s ease; }
  @keyframes bbppSlideU { from { transform: translateY( 30px ); opacity: 0.4; } to { transform: none; opacity: 1; } }
}

/* Sticky Add-to-cart, phones only, shown once the real button scrolls away. */
.bb-pp-stickybar { position: fixed; left: 0; right: 0; bottom: 0; z-index: 99999; display: none; align-items: center; gap: 12px;
  padding: 9px 14px; background: var(--bb-surface); border-top: 1px solid var(--bb-border); box-shadow: 0 -4px 20px rgba( 0, 0, 0, 0.12 ); }
.bb-pp-stickybar[hidden] { display: none; }
.bb-pp-sb-name { flex: 1; min-width: 0; font-weight: 700; font-size: 0.95em; color: var(--bb-ink); overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bb-pp-sb-add { flex: none; border: 0; border-radius: 9px; background: var(--bb-accent); color: #fff; font: inherit; font-weight: 700; padding: 11px 20px; cursor: pointer; }
@media ( max-width: 600px ) { .bb-pp-stickybar:not( [hidden] ) { display: flex; } }

/* The verdict on a shopper's artwork, and the seller's optional paid fix. */
.bb-pp-artwork { margin-top: 4px; padding: 10px 12px; border-radius: 9px; background: var(--bb-warn-bg); border: 1px solid var(--bb-warn-border); }
.bb-pp-artwork[hidden] { display: none; }
.bb-pp-artwork.is-blocking { background: color-mix( in srgb, var(--bb-error) 8%, transparent ); border-color: var(--bb-error); }
.bb-pp-art-msg { margin: 0 0 6px; font-size: 0.84em; line-height: 1.5; color: var(--bb-warn-deep); }
.bb-pp-artwork.is-blocking .bb-pp-art-msg { color: var(--bb-error); }
.bb-pp-art-msg:last-child { margin-bottom: 0; }
.bb-pp-art-help { display: inline-flex; align-items: center; gap: 8px; margin-top: 8px; padding: 7px 10px; border-radius: 8px; background: var(--bb-surface); border: 1px solid var(--bb-border); font-size: 0.86em; font-weight: 600; cursor: pointer; }
.bb-pp-art-help[hidden] { display: none; }

/* "We trimmed this for printing" — a statement, not a warning. Neutral, so it
   reads as openness about a change the seller asked us to make, rather than as
   something being wrong with what the shopper sent. */
.bb-addon-img-note { flex-basis: 100%; margin: 2px 0 0; padding: 8px 12px; border: 1px solid var(--bb-border);
	border-left: 3px solid var(--bb-accent); border-radius: 8px; background: var(--bb-surface-faint);
	font-size: 12.5px; line-height: 1.5; color: var(--bb-muted); }

/* ── Repeatable fields ───────────────────────────────────────────
   One field asked several times: a name per shirt, a line per glass.

   Built to the shape the Ministry of Justice design system ships for its
   "Add another" component, because that one has been through real user
   research: each instance is a bounded block with its own legend, and the
   control that removes it says WHAT it removes and sits after the field it
   belongs to rather than floating beside the heading. */

.bb-repeat-item { position: relative; padding: 12px 14px; border: 1px solid var(--bb-border);
	border-radius: 9px; background: var(--bb-surface-faint); }
.bb-repeat-item + .bb-repeat-item { margin-top: 8px; }

/* No legend above the input. The instance number is carried by the field's own
   label ("Engraving 1"), because a heading saying the same thing directly above
   a single input is the same word twice, and shouted. When a repeatable group
   carries several fields, a legend earns its place again and comes back with it.
   Deliberately no .bb-repeat-label display rule here: a class selector beats the
   browser's own [hidden] rule, so styling that element would force it visible. */

/* A quiet cross in the corner, the way a cart line removes itself. It is 28px
   square, comfortably past the WCAG 2.2 minimum of 24, and its accessible name
   is carried in full by the hidden text inside it. */
.bb-repeat-remove { position: absolute; top: 6px; right: 6px; width: 28px; height: 28px;
	display: inline-flex; align-items: center; justify-content: center; padding: 0;
	border: 0; border-radius: 6px; background: none; cursor: pointer;
	font: inherit; font-size: 1.15em; line-height: 1; color: var(--bb-muted); }
.bb-repeat-remove:hover { color: var(--bb-error); background: var(--bb-surface); }
.bb-repeat-remove:focus-visible { outline: 2px solid var(--bb-accent); outline-offset: 1px; }
/* Room for it, so a long value never runs under the cross. */
.bb-repeat-item { padding-right: 40px; }
/* Nothing to remove when one is left. Hidden by the script, not by CSS alone,
   so the server-rendered page is never wrong about it. */
.bb-repeat-remove[hidden] { display: none; }

.bb-repeat-add { display: inline-flex; align-items: center; gap: 7px; margin-top: 10px;
	padding: 9px 14px; min-height: 40px; border: 1.5px solid var(--bb-border); border-radius: 8px;
	background: var(--bb-surface); font: inherit; font-size: 0.88em; font-weight: 600;
	color: inherit; cursor: pointer; }
.bb-repeat-add::before { content: "+"; font-size: 1.15em; line-height: 1; font-weight: 700; color: var(--bb-accent); }
.bb-repeat-add:hover { border-color: var(--bb-accent); color: var(--bb-accent); }
.bb-repeat-add:focus-visible { outline: 2px solid var(--bb-accent); outline-offset: 2px; }

/* Stopped at the merchant's maximum: the button says so and stands down,
   dropping the "+" that promised more. Still readable, plainly not an offer. */
.bb-repeat-add:disabled { cursor: default; color: var(--bb-muted); background: var(--bb-surface-faint); }
.bb-repeat-add:disabled::before { content: none; }
.bb-repeat-add:disabled:hover { border-color: var(--bb-border); color: var(--bb-muted); }

/* The quantity dropped below a filled block: the block itself says what went,
   in the shop's warning colours. A statement beside the cards, never an
   alert(), and never a blocker; the next quantity change clears it. */
.bb-repeat-shrunk { margin: 8px 0 0; padding: 8px 12px; border-radius: 8px;
	background: var(--bb-warn-bg); border: 1px solid var(--bb-warn-border);
	font-size: 0.86em; line-height: 1.5; color: var(--bb-warn-deep); }
.bb-repeat-shrunk[hidden] { display: none; }

/* Thumbs are less accurate than a mouse, so the touch targets grow rather than
   the type. WCAG 2.5.5 asks for 44px where it can be given. */
@media (max-width: 600px) {
	.bb-repeat-remove { min-height: 44px; padding: 12px 2px; }
	.bb-repeat-add { width: 100%; justify-content: center; min-height: 44px; }
}

/* Touch devices, whatever the viewport. Two fixes in one gate:

   Inputs come up to 16px, because iOS Safari zooms the whole viewport on
   focusing anything smaller, and a shopper typing a name should not land
   zoomed into the box and have to pinch back out. !important because some of
   these sizes are set by the engine's own inline styles and by theme rules.

   Chips reach the house 44px target. They are the most-tapped control on the
   page and were the one control the earlier 44px pass left out. The plain
   chip grows in height only (inline-flex keeps the label centred); a swatch
   grows in both, staying round. These land after the chip theme guard, so
   the later same-specificity !important wins. */
@media (pointer: coarse) {
	.bb-addon-input,
	.bb-tbl-typedin,
	.bb-tbl-sel,
	.bb-mdim input,
	.bb-pp-panel-b input[type="text"],
	.bb-pp-panel-b select { font-size: 16px !important; }
	.bb-chips .bb-chip:not(.bb-chip--swatch) { min-height: 44px !important; display: inline-flex; align-items: center; }
	.bb-chips .bb-chip--swatch .bb-swatch-dot { width: 44px !important; height: 44px !important; }
}

/* ── Folding option groups ───────────────────────────────────────
   Native <details>, so the browser owns the open and shut: it works on a
   keyboard, page search finds text inside it, several can be open at once,
   and no page builder can break it by swallowing our script. All that is
   needed is to take the default triangle off and draw our own. Only OPTIONAL
   groups are details at all: a required group renders as a plain section
   (.bb-po-required), because a details always toggles on click and on Enter
   whatever is painted over it, and a required question must have nothing to
   toggle. The section wears the same card so the page reads as one family. */

.bb-po-fold,
.bb-po-section.bb-po-required { margin-bottom: 10px; border: 1px solid var(--bb-border); border-radius: 9px;
	background: var(--bb-surface-faint); overflow: hidden; }
.bb-po-fold > summary { display: flex; align-items: center; gap: 10px; padding: 12px 14px;
	cursor: pointer; font-weight: 600; list-style: none; }
/* WebKit ignores list-style on a summary, so it needs its own removal. */
.bb-po-fold > summary::-webkit-details-marker { display: none; }
/* Our own chevron, on the right where a disclosure is expected. */
.bb-po-fold > summary::after { content: ""; margin-left: auto; width: 7px; height: 7px;
	border-right: 2px solid var(--bb-muted); border-bottom: 2px solid var(--bb-muted);
	transform: rotate(45deg) translate(-2px, -2px); transition: transform 0.15s ease; flex-shrink: 0; }
.bb-po-fold[open] > summary::after { transform: rotate(-135deg); }
/* The summary IS the toggle button, so it must keep a visible focus ring. */
.bb-po-fold > summary:focus-visible { outline: 2px solid var(--bb-accent); outline-offset: -2px; }
.bb-po-fold > .bb-po-sectionbody,
.bb-po-section.bb-po-required > .bb-po-sectionbody { padding: 0 14px 14px; }

/* The required section's name row mirrors the summary's layout, minus the
   chevron: same flex, same padding, same weight, so open-next-to-open the two
   kinds of group line up. */
.bb-po-section.bb-po-required > .bb-po-sectionname { display: flex; align-items: center; gap: 10px;
	padding: 12px 14px; font-weight: 600; }

/* Both states are marked, never just one. Marking only the required fields is
   what leaves shoppers guessing whether the rest are needed too. Required
   wears the same accent-soft pill the field labels wear; optional stays the
   quiet word. */
.bb-po-reqflag, .bb-po-optflag { margin-left: auto; font-size: 0.72em; font-weight: 700;
	letter-spacing: 0.05em; padding: 2px 9px; border-radius: 99px; white-space: nowrap; }
.bb-po-reqflag { color: var(--bb-accent); background: #fff1ee;
	background: color-mix(in srgb, currentColor 9%, transparent); border: 0;
	font-variant-caps: all-small-caps; line-height: 1.5; }
.bb-po-optflag { color: var(--bb-muted); font-weight: 600; letter-spacing: 0; padding: 0; }
/* The summary reads name, flag, then the live value echo pushed right with the
   chevron after it: the flag hugs the name (the set heads' convention) and the
   echo takes the auto margin instead. */
.bb-po-fold > summary .bb-po-optflag { margin-left: 0; }
.bb-po-fold > summary .bb-po-foldecho { margin-left: auto; font-weight: 400; min-width: 0;
	overflow: hidden; text-overflow: ellipsis; white-space: nowrap; }
.bb-po-fold > summary .bb-po-foldecho.bb-gecho-todo { font-weight: 600; }
.bb-po-fold > summary::after { margin-left: 0; }

/* =================================================================
   The storefront skin (2026-07): the owner-approved hybrid.

   Treatment A's card system (white cards on a subtle warm tint, 15px
   radius, a soft double shadow) carrying treatment B's typography
   (11px tracked-uppercase group labels, tabular numerals). Everything
   below rides the --aob-* tokens, defined once here; the engine's own
   inline rules consume the same tokens with literal fallbacks, so the
   surface stays styled even if this file never loads.

   THEME INHERIT. A block theme publishes its palette as
   --wp--preset--color-- custom properties; the chains below let its
   accent and ink flow into the skin, with surface following base
   wherever ink follows contrast so the text/background pair can never
   invert into unreadable. A classic theme defines none of those, so
   every chain resolves to the gallery's neutral defaults. The muted
   and body text tones derive from ink + surface (they track whichever
   pair is live); the hairline stays the warm literal, which is where
   the approved palette's warmth lives. Each derived token declares a
   literal first, so a browser without color-mix() keeps the approved
   values instead of dropping the declaration.

   Merchants: the Colours section on the AddOnBoss settings screen
   overrides these same tokens; no rule below ever needs touching.
   ================================================================= */

/* THE storefront surface list. addon_boss_colour_override_css() writes the
   merchant's colours to this SAME set, and the two must not drift: a custom
   property resolves on the element that declares it, so if the skin defines a
   token one level up from where the override lands, the override never reaches
   the derived values below. That is exactly how the quantity grid ended up
   ignoring the Colours screen. Change one list, change the other. */
.bb-standalone-options,
.bb-standalone-grid,
.bb-quantity-grid,
.bb-tray-peek.bb-st-peek {
	/* Only the SEMANTIC slugs lend the accent. The numbered accent-N slots
	   are a theme's decorative highlights (Twenty Twenty-Five's accent-1 is
	   a pale yellow), and highlighter-coloured prompts are unreadable. */
	--aob-accent: var(--wp--preset--color--primary, var(--wp--preset--color--accent, #fd4730));
	--aob-ink: var(--wp--preset--color--contrast, #16181d);
	--aob-surface: var(--wp--preset--color--base, #ffffff);
	/* The wrapper paints NOTHING by default: the theme's own page shows
	   through on any store, and the cards carry themselves (border +
	   shadow). The gallery's warm tint is the Colours screen's optional
	   "options background" now, never a default. */
	--aob-wrap: transparent;
	--aob-text: #3a3f47;
	--aob-text: color-mix(in srgb, var(--aob-ink) 84%, var(--aob-surface));
	--aob-mut: #7d8590;
	--aob-mut: color-mix(in srgb, var(--aob-ink) 55%, var(--aob-surface));
	--aob-line: #e6e4df;
	--aob-soft: #faf9f7;
	--aob-soft: color-mix(in srgb, var(--aob-surface) 82%, var(--aob-line));
	--aob-radius: 15px;
	--aob-good: #1a7f37;

	/* ── One palette, one source of truth ──────────────────────────────
	   This file grew two accents. The skin's --aob-accent (above) is
	   theme-aware and is what the Colours screen writes; the legacy
	   --bb-accent is a hardcoded purple read by ~96 rules, the quantity
	   grid among them. A merchant who set a brand colour got twelve rules
	   repainted and the grid left purple, with nothing on the screen to
	   say why.

	   Rather than teach the settings screen to write both names, the
	   legacy names DERIVE from the skin here, once. Every rule that reads
	   --bb-accent keeps working untouched and now follows the merchant.

	   Scope matters: this block covers AddOnBoss's own surfaces only, so
	   BundleBoss's bundle pages keep their own brand-colour setting (it
	   writes --bb-accent at :root) and nothing changes over there.

	   Only the BRAND family follows the accent. Semantic colours (success,
	   warn, error) and the neutrals stay exactly as they are: a sold-out
	   badge must never turn into the shop's brand colour. Literal first,
	   as everywhere else here, so a browser without color-mix() keeps a
	   sensible value rather than dropping the declaration. */
	--bb-accent: var(--aob-accent);
	--bb-accent-bg: #f5f3ff;
	--bb-accent-bg: color-mix(in srgb, var(--aob-accent) 10%, var(--aob-surface));
	--bb-accent-deep: #4c1d95;
	--bb-accent-deep: color-mix(in srgb, var(--aob-accent) 72%, var(--aob-ink));
}

/* ── Chips, the gallery's shape: comfortable 44px+ pill targets, the
      readable 0.9em label, the selected state in the ink (or the
      merchant's selected-state colour where one is set). The theme
      guard earlier in this file pins geometry with !important, so the
      skin restates its own geometry the same way at higher
      specificity; colour stays negotiable, geometry does not. ──────── */
.bb-standalone-options .bb-chips { gap: 9px; }
.bb-standalone-options .bb-chips .bb-chip {
	display: inline-flex !important;
	align-items: center;
	min-height: 44px !important;
	padding: 8px 17px !important;
	border: 1.5px solid var(--aob-line);
	border-radius: 99px !important;
	background: var(--aob-surface);
	color: var(--aob-text);
	font-size: 0.9em !important;
	font-weight: 600;
	line-height: 1.4 !important;
	white-space: normal;
}
.bb-standalone-options .bb-chips .bb-chip:hover {
	border-color: color-mix(in srgb, var(--aob-mut) 55%, var(--aob-line));
	background: var(--aob-surface);
	color: var(--aob-ink);
}
.bb-standalone-options .bb-chips .bb-chip.is-selected,
.bb-standalone-options .bb-chips .bb-chip.is-selected:hover {
	background: var(--aob-sel, var(--aob-ink));
	border-color: var(--aob-sel, var(--aob-ink));
	color: var(--aob-surface);
}
.bb-standalone-options .bb-chip:focus-visible { outline: 2px solid var(--aob-accent); outline-offset: 2px; }
.bb-standalone-options .bb-chip-delta { font-variant-numeric: tabular-nums; }
/* The pill chip is a flex row, which swallows the whitespace between the
   label and its price; the card states its price on its own line. */
.bb-standalone-options .bb-chips .bb-chip:not(.bb-chip--card) .bb-chip-delta { margin-left: 6px; }
/* The same swallow bites the Required word and the counter beside a field
   label at small sizes; real breathing room instead of a 2px space. */
.bb-standalone-options .bb-addon-label .bb-gopt,
.bb-standalone-options .bb-addon-label .bb-addon-req,
.bb-standalone-options .bb-addon-label .bb-addon-counter,
.bb-standalone-options .bb-addon-check .bb-gopt,
.bb-standalone-options .bb-addon-check .bb-addon-req { margin-left: 6px; }
.bb-standalone-options .bb-chips .bb-chip.is-selected .bb-chip-delta { color: inherit; opacity: 0.8; }

/* Swatches: the gallery anatomy on the tokens. A 44px visual at every
   pointer, its quiet inner edge kept, the name (and price) beneath it,
   and the selected ring the gallery's double ring: a gap of surface,
   then the ink (or the merchant's selected-state colour). */
.bb-standalone-options .bb-chips .bb-chip--swatch {
	min-height: 0 !important;
	gap: 6px;
}
.bb-standalone-options .bb-chips .bb-chip--swatch .bb-swatch-dot {
	width: 44px !important;
	height: 44px !important;
	box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.12), 0 0 0 1px var(--aob-line);
}
.bb-standalone-options .bb-chips .bb-chip--swatch:hover .bb-swatch-dot {
	box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.12), 0 0 0 1px color-mix(in srgb, var(--aob-mut) 55%, var(--aob-line));
}
.bb-standalone-options .bb-chips .bb-chip--swatch.is-selected .bb-swatch-dot,
.bb-standalone-options .bb-chips .bb-chip--swatch.is-selected:hover .bb-swatch-dot {
	box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.12), 0 0 0 2px var(--aob-surface), 0 0 0 4px var(--aob-sel, var(--aob-ink)) !important;
}
.bb-standalone-options .bb-chip--swatch.is-selected .bb-swatch-dot::after {
	background-color: var(--aob-sel, var(--aob-ink));
	border-color: var(--aob-surface);
}
.bb-standalone-options .bb-sw-name { color: var(--aob-text); }
.bb-standalone-options .bb-chip--swatch.is-selected .bb-sw-name { color: var(--aob-ink); }
.bb-standalone-options .bb-chip--swatch .bb-chip-delta { color: var(--aob-mut); }

/* ── Swatch shapes (D1): a per-set display choice. Square is the
      default (a soft 8px block), circle the classic dot, and the photo
      card an Abbott Lyon-style tile with the visual filling the chip
      and the name and price beneath. The row carries the shape class;
      geometry overrides ride the same !important discipline as the
      theme guard, at higher specificity and later in the file. ──────── */
.bb-standalone-options .bb-chips.bb-shape-square .bb-chip--swatch .bb-swatch-dot { border-radius: 10px; }
.bb-standalone-options .bb-chips.bb-shape-circle .bb-chip--swatch .bb-swatch-dot { border-radius: 50%; }
/* Square squares the BUTTON, not only the dot inside it. The two rules above
   reach `.bb-chip--swatch .bb-swatch-dot`, a class a chip only earns when its
   option carries a colour, so on a set of plain text choices (S, M, L) the
   shape control was offered, saved, and changed nothing on screen at all: the
   chip stayed a 99px pill. "Square buttons" has to mean the buttons. Circle
   deliberately stays a pill here, because circle describes the dot and a
   circular text chip is not a thing; card has its own block below. */
.bb-standalone-options .bb-chips.bb-shape-square .bb-chip { border-radius: 8px !important; }
.bb-standalone-options .bb-chips.bb-shape-card .bb-chip.bb-chip--card {
	flex-direction: column;
	align-items: stretch;
	width: 96px;
	min-height: 0 !important;
	padding: 9px 10px 8px !important;
	border-radius: 12px !important;
	text-align: center;
}
.bb-standalone-options .bb-chip--card .bb-cc-img {
	display: block;
	width: 100%;
	height: 56px;
	border-radius: 8px;
	object-fit: cover;
	box-shadow: inset 0 0 0 1px rgba(0, 0, 0, 0.06);
}
.bb-standalone-options .bb-chip--card .bb-cc-name { display: block; font-size: 12px; font-weight: 600; margin-top: 6px; }
.bb-standalone-options .bb-chip--card .bb-chip-delta { display: block; font-size: 11.5px; opacity: 1; color: var(--aob-mut); }
/* The selected card keeps its picture on the surface and takes the ink
   ring instead of the filled state (a filled card would drown the photo). */
.bb-standalone-options .bb-chips .bb-chip--card.is-selected,
.bb-standalone-options .bb-chips .bb-chip--card.is-selected:hover {
	background: var(--aob-surface);
	border-color: var(--aob-sel, var(--aob-ink));
	color: var(--aob-text);
	box-shadow: 0 0 0 1.5px var(--aob-sel, var(--aob-ink));
}
.bb-standalone-options .bb-chips .bb-chip--card.is-selected .bb-cc-name { color: var(--aob-ink); font-weight: 700; }
.bb-standalone-options .bb-chips .bb-chip--card.is-selected .bb-chip-delta { color: var(--aob-mut); }

/* ── Several-of: a chosen one wears a tick ─────────────────────────────
   A ring on its own is what a chosen RADIO looks like, so a set a shopper
   may take three of read exactly like a set they may take one of. The
   convention is older than the web: a tick means "as many as you like".
   The same badge the positions panel already uses, so the two surfaces
   agree with each other.

   Absolutely placed and sitting proud of the corner, on purpose: the card
   is 96px wide with a 56px picture and nothing here may move either. An
   element of its own would have cost the row a reflow, and the picture is
   the thing being chosen. */
.bb-chips.bb-multi .bb-chip { position: relative; }
.bb-chips.bb-multi .bb-chip.is-selected::after {
	content: "";
	position: absolute;
	top: -7px;
	right: -7px;
	width: 18px;
	height: 18px;
	border-radius: 50%;
	background-color: var(--aob-sel, var(--aob-ink, #16181d));
	background-image: url("data:image/svg+xml;charset=utf-8,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 12 12'%3E%3Cpath d='M2.4 6.2 4.8 8.6 9.6 3.3' fill='none' stroke='%23fff' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
	background-repeat: no-repeat;
	background-position: center;
	background-size: 11px 11px;
	box-shadow: 0 0 0 2px var(--aob-surface, #fff);
	/* Over the picture, which is a positioned element later in the markup:
	   without this the badge paints underneath it and is simply not there. */
	z-index: 2;
	pointer-events: none;
}
/* Room at the top of the row for a badge that hangs off the corner, so the
   first line never clips it. */
.bb-chips.bb-multi { padding-top: 7px; }

/* Each pick keeps its own note, named, because with several answers on the
   screen at once an unattributed sentence belongs to nobody. */
.bb-standalone-options .bb-opt-note--many { display: grid; gap: 5px; }
.bb-standalone-options .bb-opt-noteline { display: block; }
.bb-standalone-options .bb-opt-noteline b { font-weight: 600; }

/* ── Fields: the gallery's inputs (1.5px hairline, 10px radius, a real
      16px type size so iOS never zooms, focus in the ink with a soft
      ring), the fee figure in tabular numerals. ─────────────────────── */
.bb-standalone-options .bb-addon-input {
	padding: 11px 13px;
	border: 1.5px solid var(--aob-line);
	border-radius: 10px;
	font-size: 16px;
	background: var(--aob-surface);
	color: var(--aob-ink);
}
.bb-standalone-options .bb-addon-input:focus {
	outline: none;
	border-color: var(--aob-ink);
	box-shadow: 0 0 0 3px color-mix(in srgb, var(--aob-ink) 7%, transparent);
}
.bb-standalone-options .bb-addon-label { color: var(--aob-text); }
/* The Required pill on the token ground: accent ink over the accent-soft
   mix of the live palette, so it keeps its softness under a merchant
   accent too. Shared with the fold family's section flag below. */
.bb-standalone-options .bb-addon-req,
.bb-standalone-options .bb-po-reqflag {
	color: var(--aob-accent);
	background: #fff1ee;
	background: color-mix(in srgb, var(--aob-accent) 9%, var(--aob-surface));
}
.bb-standalone-options .bb-addon-check { color: var(--aob-text); }
.bb-standalone-options .bb-addon-fee { font-variant-numeric: tabular-nums; }
.bb-standalone-options .bb-field-group .bb-addon-fee { color: var(--aob-mut); }
.bb-standalone-options .bb-field-group.is-filled .bb-addon-fee { color: var(--aob-ink); }

/* The filled field's mark moves off the card edge (a 2px green border
   reshaped the card) and into the fee + echo colouring; the card keeps
   its own geometry and a whisper of the good green on its border. */
.bb-standalone-options .bb-field-group.is-filled {
	border-left: 1px solid color-mix(in srgb, var(--aob-good) 32%, var(--aob-line));
	border-color: color-mix(in srgb, var(--aob-good) 32%, var(--aob-line));
	padding-left: 16px;
}

/* ── Repeat blocks: the gallery card (surface, 12px radius, 1.5px
      line), and the add-another control as the coral dashed invitation
      that stands down politely at the cap. ──────────────────────────── */
.bb-standalone-options .bb-repeat-item {
	border: 1.5px solid var(--aob-line);
	border-radius: 12px;
	background: var(--aob-surface);
	padding: 12px 13px 12px;
	padding-right: 40px;
}
.bb-standalone-options .bb-repeat-item + .bb-repeat-item { margin-top: 9px; }
.bb-standalone-options .bb-repeat-add {
	width: 100%;
	justify-content: center;
	min-height: 44px;
	margin-top: 9px;
	padding: 11px 14px;
	border: 1.5px dashed var(--aob-accent);
	border-radius: 11px;
	background: color-mix(in srgb, var(--aob-accent) 7%, var(--aob-surface));
	color: var(--aob-accent);
	font-weight: 700;
}
.bb-standalone-options .bb-repeat-add::before { color: inherit; }
.bb-standalone-options .bb-repeat-add:hover { border-color: var(--aob-accent); color: var(--aob-accent); filter: brightness(0.97); }
.bb-standalone-options .bb-repeat-add:disabled,
.bb-standalone-options .bb-repeat-add:disabled:hover {
	border: 1.5px solid var(--aob-line);
	background: var(--aob-soft);
	color: var(--aob-mut);
	filter: none;
}
.bb-standalone-options .bb-repeat-shrunk { border-radius: 10px; }
.bb-standalone-options .bb-repeat-remove { color: var(--aob-mut); }
.bb-standalone-options .bb-repeat-remove:hover { background: var(--aob-soft); }

/* ── Fold-as-you-go (D2, per-item repeats): each instance is an
      accordion. The header carries the number badge (the good green
      tick once filled), the instance name, and a summary quoting the
      typed value while shut; the first unfilled one stands open. ────── */
.bb-standalone-options .bb-repeat-item.bb-racc { padding: 0; overflow: hidden; }
.bb-standalone-options .bb-racc-head {
	display: flex;
	align-items: center;
	gap: 9px;
	width: 100%;
	min-height: 46px;
	margin: 0;
	padding: 11px 40px 11px 13px;
	border: 0;
	border-radius: 0;
	background: none;
	box-shadow: none;
	font: inherit;
	text-align: left;
	cursor: pointer;
	color: var(--aob-text);
}
.bb-standalone-options .bb-racc-head:hover { background: var(--aob-soft); }
.bb-standalone-options .bb-racc-head:focus-visible { outline: 2px solid var(--aob-accent); outline-offset: -2px; }
.bb-standalone-options .bb-racc-n {
	width: 24px;
	height: 24px;
	border-radius: 50%;
	background: var(--aob-soft);
	border: 1px solid var(--aob-line);
	color: var(--aob-mut);
	font-size: 12px;
	font-weight: 800;
	display: grid;
	place-items: center;
	flex: none;
}
.bb-standalone-options .bb-racc.is-done .bb-racc-n {
	background: var(--aob-good);
	border-color: var(--aob-good);
	color: #fff;
}
.bb-standalone-options .bb-racc.is-done { border-color: color-mix(in srgb, var(--aob-good) 35%, var(--aob-line)); }
.bb-standalone-options .bb-racc-t { font-size: 14px; font-weight: 700; color: var(--aob-ink); flex: none; }
.bb-standalone-options .bb-racc-sum {
	margin-left: auto;
	min-width: 0;
	overflow: hidden;
	text-overflow: ellipsis;
	white-space: nowrap;
	font-size: 12.5px;
	color: var(--aob-mut);
}
.bb-standalone-options .bb-racc-sum.is-todo { color: var(--aob-accent); font-weight: 700; }
.bb-standalone-options .bb-racc-bd { display: none; padding: 2px 13px 13px; }
.bb-standalone-options .bb-racc.is-open .bb-racc-bd { display: block; }
/* The header already names the instance; its in-body label steps back to
   a quiet caption (it still labels the input, for everyone). */
.bb-standalone-options .bb-racc-bd .bb-addon-label { font-size: 11.5px; font-weight: 600; color: var(--aob-mut); }

/* ── Group instances: one block holds several fields. The legend the
      single-field card deliberately dropped comes back here, because it
      gathers several fields under it; the fields stack inside with the
      block title carrying the number, and each keeps its own quiet
      caption-weight label. ─────────────────────────────────────────── */
.bb-standalone-options .bb-gi-head {
	display: flex;
	align-items: center;
	gap: 10px;
	margin: 0 0 10px;
}
.bb-standalone-options .bb-gi-n {
	width: 22px;
	height: 22px;
	border-radius: 50%;
	background: var(--aob-soft);
	border: 1px solid var(--aob-line);
	color: var(--aob-mut);
	font-size: 11px;
	font-weight: 800;
	display: grid;
	place-items: center;
	flex: none;
}
.bb-standalone-options .bb-gi-t { font-size: 14px; font-weight: 700; color: var(--aob-ink); }
.bb-gi-fields { display: flex; flex-direction: column; gap: 10px; }
.bb-gi-field > .bb-addon-field { margin: 0; }
.bb-standalone-options .bb-gi .bb-addon-label { font-size: 11.5px; font-weight: 600; color: var(--aob-mut); }
/* Two short members pair onto one row where the block is wide enough,
   the same D5 discipline ungrouped short fields follow. */
@media (min-width: 500px) {
	.bb-gi-fields { flex-direction: row; flex-wrap: wrap; }
	.bb-gi-field { flex: 1 1 100%; min-width: 0; }
	.bb-gi-field.bb-fk-select,
	.bb-gi-field.bb-fk-number,
	.bb-gi-field.bb-fk-checkbox { flex: 1 1 calc(50% - 5px); }
}

/* ── Folding sections wear the section card they sit in (the engine
      styles own the card itself); the summary row keeps the tracked
      label rhythm and an accent focus ring. ────────────────────────── */
.bb-standalone-options .bb-po-fold,
.bb-standalone-options .bb-po-section.bb-po-required { border: 0; background: none; }
/* A shut fold is a designed card row, not a thin strip: the same 54px
   presence whether shut or open, everything centred on one line, and the
   value echo standing clear of the name on one side and the chevron on
   the other. The required section's name row keeps the identical rhythm,
   so closed-next-to-open the rows line up. */
.bb-standalone-options .bb-po-fold > summary { padding: 15px 16px; min-height: 54px; box-sizing: border-box;
	align-items: center; gap: 12px; }
.bb-standalone-options .bb-po-fold > summary:hover { background: var(--aob-soft); }
.bb-standalone-options .bb-po-fold[open] > summary:hover { background: none; }
.bb-standalone-options .bb-po-fold > summary .bb-po-foldecho { font-size: 12.5px; padding-left: 4px; }
.bb-standalone-options .bb-po-section.bb-po-required > .bb-po-sectionname { padding: 15px 16px 0;
	min-height: 54px; box-sizing: border-box; align-items: center; gap: 12px; }
.bb-standalone-options .bb-po-optflag { color: var(--aob-mut); }

/* ── The style picker and the file chips ride the same tokens. ─────── */
.bb-standalone-options .bb-style-lab { color: var(--aob-text); }
.bb-standalone-options .bb-file-chip { border-color: var(--aob-line); background: var(--aob-soft); border-radius: 10px; }
.bb-standalone-options .bb-file-fn { color: var(--aob-text); }
.bb-standalone-options .bb-file-fs { color: var(--aob-mut); }
.bb-standalone-options .bb-file-ok { color: var(--aob-good); }
.bb-standalone-options .bb-addon-file-btn {
	border: 1.5px dashed var(--aob-line);
	border-radius: 99px;
	padding: 9px 16px;
	min-height: 40px;
	align-items: center;
	background: var(--aob-surface);
	color: var(--aob-text);
}
.bb-standalone-options .bb-addon-file-btn:hover { border-color: var(--aob-accent); color: var(--aob-accent); }
.bb-standalone-options .bb-addon-drop-hint,
.bb-standalone-options .bb-addon-file-count,
.bb-standalone-options .bb-addon-file-status { color: var(--aob-mut); }
/* The what-we-accept line reads as a caption, not body copy, and takes
   its own line beneath the pick control, as the gallery draws it. */
.bb-standalone-options .bb-addon-file-hint {
	display: block;
	flex-basis: 100%;
	font-size: 12.5px;
	color: var(--aob-mut);
}
.bb-standalone-options .bb-addon-file-bar { background: var(--aob-soft); }
.bb-standalone-options .bb-addon-file-bar > i { background: var(--aob-accent); }

/* ── The peek bar: same surface, same ink, the total in tabular
      numerals; its CTA is painted by the engine styles. ─────────────── */
.bb-tray-peek.bb-st-peek {
	background: var(--aob-surface);
	border-top: 1px solid var(--aob-line);
}
.bb-tray-peek.bb-st-peek .bb-peek-info { color: var(--aob-ink); font-variant-numeric: tabular-nums; }

/* ── D5: two short fields share a row where there is room. The wrapper
      (and a section body) flows as a wrapping row; everything takes the
      full width, except that a text or number field standing NEXT TO
      another collapses to half. Each half insists on 354px, so the pair
      only shares a row once the wrapper can give both sides that plus
      the gap, which is the design's 720px container; anywhere narrower
      the minimum forces the wrap straight back to the stack. :has()
      finds the FIRST of the pair, so a lone short field between two
      full-width groups keeps its full row. Old browsers fail the :has()
      selector and simply keep the stack. ─────────────────────────────── */
.bb-standalone-options,
.bb-standalone-options .bb-po-sectionbody { display: flex; flex-wrap: wrap; column-gap: 12px; align-items: flex-start; }
.bb-standalone-options > *,
.bb-standalone-options .bb-po-sectionbody > * { flex: 0 0 100%; min-width: 0; box-sizing: border-box; }
.bb-standalone-options > :is(.bb-fk-text, .bb-fk-number):has(+ :is(.bb-fk-text, .bb-fk-number)),
.bb-standalone-options > :is(.bb-fk-text, .bb-fk-number) + :is(.bb-fk-text, .bb-fk-number),
.bb-standalone-options .bb-po-sectionbody > :is(.bb-fk-text, .bb-fk-number):has(+ :is(.bb-fk-text, .bb-fk-number)),
.bb-standalone-options .bb-po-sectionbody > :is(.bb-fk-text, .bb-fk-number) + :is(.bb-fk-text, .bb-fk-number) {
	flex: 1 1 calc(50% - 6px);
	min-width: min(354px, 100%);
}
/* The paired card's input stretches to its own card, not the page. */
.bb-standalone-options .bb-field-group .bb-addon-input { max-width: 100%; }

/* -----------------------------------------------------------------
   The paste-the-list box: a per-item single-field repeat at trade
   volume folds its cards into one textarea, one entry per line, with
   the live count beneath. Wears the same tokens as every input.
   ----------------------------------------------------------------- */
.bb-standalone-options .bb-bulk { margin-top: 4px; }
.bb-standalone-options .bb-bulk-in { display: block; width: 100%; max-width: 460px; min-height: 150px; padding: 10px 12px; border: 1.5px solid var(--aob-line, #e6e4df); border-radius: 10px; background: var(--aob-surface, #fff); color: var(--aob-ink, #16181d); font: inherit; font-size: max(16px, 0.95em); line-height: 1.7; box-sizing: border-box; resize: vertical; }
.bb-standalone-options .bb-bulk-in:focus { border-color: var(--aob-ink, #16181d); outline: none; box-shadow: 0 0 0 3px color-mix(in srgb, var(--aob-ink, #16181d) 7%, transparent); }
.bb-standalone-options .bb-bulk-count { margin: 7px 0 0; font-size: 0.85em; color: var(--aob-mut, #7d8590); font-variant-numeric: tabular-nums; font-weight: 600; }
.bb-standalone-options .bb-bulk-count.is-short { color: var(--aob-accent, #fd4730); }
.bb-standalone-options .bb-bulk-count.is-over { color: #b91c1c; }
