/* Vendored from bundlex-cart shopify/src/drawer/src/styles.ts (BASE_CSS).
   Do not edit here - run bin/sync-cart-drawer-css.sh. */
#bxc-drawer, #bxc-drawer * { box-sizing: border-box; }
/* Theme bleed: our markup sits in the theme's document, so its bare-element rules apply to us.
   Only properties our own component classes never set, so this can never outrank our styling. */
#bxc-drawer img { opacity: 1; visibility: visible; }
#bxc-drawer p, #bxc-drawer h2, #bxc-drawer ul { margin: 0; }
/* textarea belongs here with button and input: the order note renders as one for the multi-line
   type, and without it the theme's own field styling — a focus ring drawn as a box-shadow, its own
   min-height — reached the only control in the drawer that is neither of the other two. */
#bxc-drawer button, #bxc-drawer input, #bxc-drawer textarea {
  min-height: auto; min-width: auto; margin: 0; box-shadow: none;
  font-family: inherit; text-transform: none; letter-spacing: normal;
}
#bxc-drawer a { text-decoration: none; }
/* A link a merchant put in a text: the text's own colour, underlined so it reads as a link. */
#bxc-drawer a.bxc-rich-link { color: inherit; text-decoration: underline; text-underline-offset: 2px; }
/* Modules hide themselves with the hidden attribute, whose display:none lives in the UA sheet —
   any author rule (ours included: .bxc-countdown is display:flex) overrides the UA sheet, so
   without this the attribute is silently defeated. */
#bxc-drawer [hidden] { display: none !important; }
/* Dawn-family themes ship 'div:empty { display: none }' in base.css, which out-specifies a bare
   class selector. These two boxes are empty by design: the overlay is the outside-click close
   target, the fill is a width-styled progress bar. Without the id-scoped override, Dawn hides
   both — the drawer stops closing on outside clicks and the rewards bar never shows progress. */
#bxc-drawer .bxc-overlay { display: block; }
#bxc-drawer .bxc-rewards__fill { display: block; }
/* Themes draw their own checkbox with pseudo-elements, which doubles up over ours. */
#bxc-drawer input[type="checkbox"]::before, #bxc-drawer input[type="checkbox"]::after { content: none; }
/* Stroke icons only — payment badges are fill-based and must keep their own fill. */
#bxc-drawer .bxc-trust__icon, #bxc-drawer .bxc-line__remove svg { fill: none; stroke: currentColor; }
/* Filled box while the bitmap is in flight, so an unloaded thumb is not a stark empty hole.
   Transparent text hides the alt string mid-load without taking it from the accessibility tree. */
.bxc-line__img, .bxc-upsell__img { display: block; background: var(--bxc-border); color: transparent; }
#bxc-drawer {
  --bxc-z: 2147483000;
  /* Layout tokens: one place for every box the modules share, so no module hardcodes a spacing
     literal and a redesign moves the whole system together. */
  --bxc-gutter: 20px;
  --bxc-space: 16px;
  --bxc-gap: 12px;
  /* Wider than the row-gap inside a line: two products must separate more than a line's own
     info separates from its controls. The rule between lines does half the separating, which is
     why this sits under the spacing-only 24px it once was. */
  --bxc-line-gap: 16px;
  /* Type scale: one token per size in use, each the px it had before the knob times the merchant's
     factor, so at scale 1 every rule computes to its old px. Two 16px guards below stay literal. */
  --bxc-fs-10: calc(10px * var(--bxc-type-scale, 1));
  --bxc-fs-11: calc(11px * var(--bxc-type-scale, 1));
  --bxc-fs-12: calc(12px * var(--bxc-type-scale, 1));
  --bxc-fs-13: calc(13px * var(--bxc-type-scale, 1));
  --bxc-fs-14: calc(14px * var(--bxc-type-scale, 1));
  --bxc-fs-15: calc(15px * var(--bxc-type-scale, 1));
  --bxc-fs-16: calc(16px * var(--bxc-type-scale, 1));
  --bxc-fs-18: calc(18px * var(--bxc-type-scale, 1));
  --bxc-divider: 1px solid var(--bxc-border);
  /* The ring derives from the text colour so it flips polarity with the palette: near-black on a
     light drawer (what the old rgba(0,0,0,0.06) was), near-white on a dark one — where a black
     ring is invisible and a borderless card would melt into the tray. The depth layers stay black:
     darkness under an edge is how elevation reads on a light surface, and on a dark one it simply
     vanishes, which is what dark surfaces do with cast shadows anyway. */
  --bxc-card-ring: color-mix(in srgb, var(--bxc-text) 7%, transparent);
  /* --bxc-shadow-panel and --bxc-shadow-card are emitted by appearanceCss, before this block:
     the elevation switch re-points them, and a declaration here would win at equal specificity. */
  /* Secondary surface (the side rail): recedes a step behind the cart so it reads as a shelf, not
     more cart. Derived rather than stored so it tracks any merchant palette in either polarity —
     slightly darker on a light cart, slightly lighter on a dark one — and a future colour theme
     can re-point the token without migrating saved carts. */
  --bxc-bg-subdued: color-mix(in srgb, var(--bxc-bg) 96%, var(--bxc-text));
  /* Scrim over the page, not drawer elevation, so no layout preset varies it. */
  --bxc-overlay: rgba(0,0,0,0.4);
  /* Radius scale derived from the merchant's one --bxc-radius knob: small controls stay tighter
     than their own height, surfaces stay softer than controls, and setting the base to 0 or to a
     large value moves the whole system together. Icon buttons are circles, off the scale. */
  --bxc-radius-sm: calc(var(--bxc-radius) * 0.75);
  --bxc-radius-lg: calc(var(--bxc-radius) * 1.5);
  /* Form controls stop at 8px whatever the knob says: a text field or a select at a card's
     40px is a pill with its text crammed into the bend, while a button or a card wears it fine.
     Same rule as Bundlex's selects. Unchanged at the default of 8. */
  --bxc-radius-input: min(var(--bxc-radius), 8px);
  --bxc-radius-ctl: min(var(--bxc-radius-sm), 8px);
  font-family: inherit;
  line-height: 1.4;
}

/* Every module root carries data-bxc-module, which makes it the section box: one gutter, one
   vertical rhythm, one divider rule. Modules own what is inside; the shell owns the boxes. */
.bxc-panel [data-bxc-module] { padding: var(--bxc-space) var(--bxc-gutter); }
/* A module that renders nothing (disabled, gated, no content) must not leave a padded gap. */
.bxc-panel [data-bxc-module]:empty { display: none; }
/* :empty cannot see the second way a module renders nothing: the upsell modules keep a
   visually-hidden live region through a clear, so a late announcement has somewhere to land. That
   leaves a childful module drawing a full section box under a cart with no upsells. It has to stay
   in the accessibility tree, so it can lose its box but never be hidden. */
.bxc-panel [data-bxc-module]:not(:has(> :not(.bxc-sr))) { padding: 0; }
/* Dividers come from the parent, so the card layout drops all of them by re-mapping --bxc-divider
   and buys the separation back with edge shadows. ~ over + and the :has() pair: a module showing
   nothing must not draw a line, which is what put a rule under the last cart line when no upsell
   or trust-badge module had anything to show. */
.bxc-region--top > [data-bxc-module]:not([hidden]):has(> :not(.bxc-sr)) ~ [data-bxc-module]:not([hidden]):has(> :not(.bxc-sr)),
.bxc-region--body > [data-bxc-module]:not([hidden]):has(> :not(.bxc-sr)) ~ [data-bxc-module]:not([hidden]):has(> :not(.bxc-sr)),
.bxc-region--footer > [data-bxc-module]:not([hidden]):has(> :not(.bxc-sr)) ~ [data-bxc-module]:not([hidden]):has(> :not(.bxc-sr)) {
  border-top: var(--bxc-divider);
}
/* On a short cart the free space collects after the line items, pinning upsells and trust badges
   to the bottom edge — offers meet the shopper's eye where the checkout button is. On a long cart
   the auto margin resolves to zero and the same modules simply scroll under the footer. On the
   list, not its followers: a hidden follower drops out of flex layout and would take the pin with
   it, while free space after the list pushes whatever does render. */
.bxc-region--body { flex: 1 0 auto; display: flex; flex-direction: column; }
/* Id-scoped out of necessity, not habit: the line-items module root IS the <ul>, so the theme-bleed
   reset above (#bxc-drawer ul, a 1-0-1) silently zeroes any lighter margin rule aimed at it. */
#bxc-drawer .bxc-region--body > [data-bxc-module="lineItems"] { margin-bottom: auto; }
/* Region boundary and the cast over the list both belong to whatever sits directly above it, the
   way the footer already owns its own top edge. With modules pinned that is this region, not the
   header two elements up — which is why the cast used to land on the announcement bar and the list
   scrolled under a bare hairline. Drawn only when the region actually shows something. */
.bxc-region--top:has([data-bxc-module]:not([hidden]):not(:empty)) {
  border-bottom: var(--bxc-divider);
  position: relative; z-index: 1;
  box-shadow: var(--bxc-cast-down);
}
/* The header is the list's neighbour only when nothing is pinned under it, so it casts only then. */
.bxc-panel:not(:has(.bxc-region--top [data-bxc-module]:not([hidden]):not(:empty))) .bxc-header {
  box-shadow: var(--bxc-cast-down);
}
.bxc-overlay {
  position: fixed; inset: 0; z-index: var(--bxc-z);
  background: var(--bxc-overlay);
  opacity: 0; visibility: hidden;
  transition: opacity .28s ease, visibility .28s ease;
}
/* One box: the cart column and the rail's region are its two children, so one shadow, one radius
   and one slide cover both. As wide as its columns; on a narrow viewport the cart gives way to the
   rail, never the other way round. */
.bxc-panel {
  position: fixed; top: 0; bottom: 0; z-index: calc(var(--bxc-z) + 1);
  width: fit-content; max-width: 100vw;
  /* How far the docked edge sits off the viewport: zero while docked, the gap while floating. */
  --bxc-dock-inset: 0px;
  display: flex; flex-direction: row; align-items: stretch;
  /* The row is physical: data-side names a side of the screen, and a flex row would follow a
     right-to-left page. The columns take the page's direction back below. */
  direction: ltr;
  background: var(--bxc-bg); color: var(--bxc-text);
  box-shadow: var(--bxc-shadow-panel);
  /* visibility:hidden keeps the closed (offscreen) panel out of the tab order. */
  visibility: hidden;
  transition: transform .28s ease, visibility .28s ease;
}
.bxc-cart { position: relative; display: flex; flex-direction: column; flex: 1 1 auto; min-width: 0; width: var(--bxc-width); }
/* The reward celebration: placed and sized over the cart column by the shell, above the sheet. */
#bxc-drawer .bxc-celebration { position: absolute; z-index: 4; pointer-events: none; display: block; margin: 0; padding: 0; border: 0; max-width: none; }
#bxc-drawer[data-open="true"] .bxc-panel { visibility: visible; }
.bxc-panel[data-position="right"] { right: 0; transform: translateX(100%); }
.bxc-panel[data-position="left"]  { left: 0;  transform: translateX(-100%); }
/* Floating layout: the panel is inset from all four viewport edges instead of docked against one.
   The closed transform has to clear the inset as well as the panel's own width, or a shut drawer
   sits a gap's width on screen. */
.bxc-panel[data-layout="floating"] {
  --bxc-dock-inset: var(--bxc-gap-edge);
  top: var(--bxc-gap-edge); bottom: var(--bxc-gap-edge);
  max-width: calc(100vw - var(--bxc-gap-edge) * 2);
  border-radius: var(--bxc-radius-lg);
  /* The columns inside have square corners of their own; the box cuts both to its radius. */
  overflow: clip;
}
.bxc-panel[data-layout="floating"][data-position="right"] {
  right: var(--bxc-gap-edge); transform: translateX(calc(100% + var(--bxc-gap-edge)));
}
.bxc-panel[data-layout="floating"][data-position="left"] {
  left: var(--bxc-gap-edge); transform: translateX(calc(-100% - var(--bxc-gap-edge)));
}
/* Editor preview: the width setting renders at full size only on the storefront. The pane beside
   the form is fixed, and following the merchant's width meant either squeezing the form or scaling
   the drawer — both worse than an honest constant. */
/* The cap resolves against the transformed stage, the fixed panel's containing block, so on a pane
   narrower than the pin (phone admin, stacked editor) the preview shrinks to fit instead of
   cropping its undocked edge. Floating draws as on the storefront, gap and radius included: the
   editor widens its pane by the gap on both sides. The storefront's 480px full-bleed rule cannot
   help here: the preview pin out-specifies it at every viewport. */
#bxc-drawer[data-preview="true"] .bxc-panel { --bxc-width: 450px; max-width: calc(100% - var(--bxc-dock-inset) * 2); }
/* A pane can be shorter than the drawer's content: cut at the panel's own edge, rounded while
   floating, not in a floating panel's gap. */
#bxc-drawer[data-preview="true"] .bxc-panel { overflow: clip; }
/* Editor preview: the panel is pinned open, so its slide has nothing to narrate — it only played
   once on boot and again on every reload. The storefront keeps the full slide. */
#bxc-drawer[data-preview="true"] .bxc-panel { transition: visibility .28s ease; }
#bxc-drawer[data-open="true"] .bxc-overlay { opacity: 1; visibility: visible; }
#bxc-drawer[data-preview="true"] .bxc-overlay { display: none; }
#bxc-drawer[data-open="true"] .bxc-panel { transform: translateX(0); }

/* Mirror of the footer: both pinned edges cast onto the list that scrolls between them. Same
   position/z-index reason as the footer — a static sibling's shadow paints below the list's text. */
/* The sheets reuse .bxc-close on the drawer background, so the header hands its text to its own
   close button through --bxc-close-text rather than the button reading the header key. */
.bxc-header {
  display: flex; align-items: center; justify-content: space-between; gap: var(--bxc-gap);
  padding: var(--bxc-header-space, var(--bxc-space)) var(--bxc-gutter); border-bottom: var(--bxc-divider);
  flex: 0 0 auto;
  position: relative; z-index: 1;
  background: var(--bxc-header-bg, transparent); color: var(--bxc-header-text, inherit);
  --bxc-close-text: var(--bxc-header-text, var(--bxc-text));
}
.bxc-title { font-size: var(--bxc-fs-16); font-weight: 600; margin: 0; color: var(--bxc-header-text, inherit); }
/* One interaction language across the drawer: icon buttons take a 5% currentColor wash, primary
   buttons lighten their own background, links underline. --bxc-wash keeps it in one place. */
#bxc-drawer { --bxc-wash: color-mix(in srgb, currentColor 6%, transparent); }
/* Far lighter than the wash: a text field only has to sit off the panel, and at the wash's own
   weight it reads as disabled rather than editable. Hover deepens it, focus clears it. */
#bxc-drawer {
  --bxc-input-bg: color-mix(in srgb, currentColor 2%, transparent);
  --bxc-input-bg-hover: color-mix(in srgb, currentColor 4%, transparent);
  /* An outlined button has to hold its own beside the solid one below it, which --bxc-border, a
     hairline for separating rows, cannot do. currentColor-based, so it follows a themed drawer. */
  --bxc-border-control: color-mix(in srgb, currentColor 32%, transparent);
}
/* Icon buttons are ID-scoped whole, base rule included: a theme's own button padding/border beats
   a single class, which is what turned the close button into a bordered oval. */
#bxc-drawer .bxc-close {
  appearance: none; border: 0; background: transparent; cursor: pointer;
  width: 32px; height: 32px; padding: 0; flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  line-height: 1; color: var(--bxc-close-text, var(--bxc-text));
  border-radius: 999px; transition: background-color .15s ease;
}
/* Interactive states carry the #bxc-drawer scope so a theme's own button:hover cannot outrank
   them — a single class would lose to any slightly more specific theme selector. */
#bxc-drawer .bxc-close:hover { background: var(--bxc-wash); }
/* 2px thickness is the accessible minimum and stays; the offset is what made small controls look
   ringed rather than focused, so it drops to 1px. */
#bxc-drawer :focus-visible { outline: 2px solid var(--bxc-accent); outline-offset: 1px; }
/* Selects only, and only after a pointer: Chrome treats a focused <select> as focus-visible whoever
   focused it, so a click left the ring sitting on the control long after the choice was made. Every
   other control already answers :focus-visible correctly, and a keyboard user never sets the flag,
   so this takes the ring from nobody who needs it. */
#bxc-drawer[data-bxc-pointer] select:focus-visible { outline: none; }
/* The panel takes focus on open so keyboard and screen readers enter the dialog; it is not a
   control, so it must not paint a ring. */
#bxc-drawer .bxc-panel:focus, #bxc-drawer .bxc-panel:focus-visible { outline: none; }

.bxc-region--top { flex: 0 0 auto; }
/* 0..1 per edge: how much of a cue that edge has earned, 0 whenever nothing can scroll past it.
   DrawerShell writes the live values — CSS cannot read scroll position on its own.
   Declared on the panel rather than on either scroll box: the casts are drawn by the boxes'
   neighbours, and this is the ancestor every reader shares. */
.bxc-panel {
  --bxc-fade-top: 0; --bxc-fade-bottom: 0; --bxc-side-fade-top: 0; --bxc-side-fade-bottom: 0;
  /* One place to tune every scroll cast. Tight and slightly denser rather than wide and faint: at
     8px the falloff spread far enough down the first card to read as a gradient on the card itself
     rather than as an edge above it. Each rides the same 0..1 the fade signal carries, so depth
     eases in over the first 24px of scroll and a list that fits casts nothing. */
  --bxc-cast-down: 0 1px 4px rgba(0,0,0, calc(0.12 * var(--bxc-fade-top, 0)));
  --bxc-cast-up: 0 -1px 4px rgba(0,0,0, calc(0.12 * var(--bxc-fade-bottom, 0)));
  --bxc-side-cast-down: 0 1px 4px rgba(0,0,0, calc(0.12 * var(--bxc-side-fade-top, 0)));
}
/* A flex column at every state (not just empty): the body region stretches to the scrollport so
   a short cart can pin its trailing modules to the bottom edge. */
.bxc-body {
  flex: 1 1 auto; overflow-y: auto; overflow-x: hidden;
  /* Same containment the rail and the carousel already take: without it a flick past the end of the
     list chains to the storefront behind the drawer. */
  overscroll-behavior: contain;
  display: flex; flex-direction: column;
  /* No edge mask to go with the casts. Fading content to transparent over the panel reads as the
     list being cut off rather than as depth, and the two cues sat on the same edges: the mask was
     carrying the whole job while the cast it duplicates was misplaced on the header. */
}
/* Lift off the scrolling list, so the checkout block reads as pinned rather than as the last row.
   position/z-index are load-bearing: a static sibling's shadow paints in the background phase, which
   is below the earlier sibling's text, so the list would scroll straight over it. */
.bxc-region--footer {
  flex: 0 0 auto; border-top: var(--bxc-divider);
  position: relative; z-index: 1;
  box-shadow: var(--bxc-cast-up);
}
/* Header, top region and footer never shrink, so on a short viewport with several modules enabled
   the list collapses to nothing and the footer runs past the panel's bottom edge — checkout ends up
   unreachable. Scrolling the whole cart column is the fallback; the min-height keeps a line visible
   rather than trading one unusable state for another. Scoped to the exact complement of the rail's
   query, and the column rather than the box, so the rail beside it is never scrolled. */
/* Preview is excluded because the pane is never short in the way a phone is. */
@media (max-width: 899px), (max-height: 599px) {
  #bxc-drawer:not([data-preview="true"]) .bxc-cart { overflow-y: auto; overscroll-behavior: contain; }
  /* The list gives up its containment here so a drag that reaches its end carries on into the
     panel — otherwise the fallback is only reachable by dragging the header, and the checkout it
     exists to expose stays off screen. The panel's own containment still stops the storefront. */
  .bxc-body { min-height: 140px; overscroll-behavior: auto; }
}
/* Same fallback, reached the other way: the keyboard shrinks only the visual viewport, so a tablet
   keeps matching the tall media query while its panel is now short. DrawerShell sets the attribute
   from visualViewport. Keep the two declaration blocks identical. */
#bxc-drawer[data-bxc-keyboard]:not([data-preview="true"]) .bxc-cart { overflow-y: auto; overscroll-behavior: contain; }
#bxc-drawer[data-bxc-keyboard] .bxc-body { min-height: 140px; overscroll-behavior: auto; }

/* sheet — a card over the panel, for content a module hands the shell */
/* A zero-height anchor on the cart column's bottom edge, with the layer hung upward from it. The
   column rather than the box: the sheet is the cart's, and the rail beside it dims on its own. */
.bxc-sheet { position: absolute; left: 0; right: 0; bottom: 0; flex: 0 0 auto; height: 0; z-index: 3; border-radius: inherit; }
/* Where the panel is a scroll box an absolute anchor would scroll away with the content, so there
   it sticks to the scrollport's bottom instead. Same two selectors as the fallback above, and only
   those: with no scroll box of its own, sticky would answer to the page's scroll. */
@media (max-width: 899px), (max-height: 599px) {
  #bxc-drawer:not([data-preview="true"]) .bxc-sheet { position: sticky; }
}
#bxc-drawer[data-bxc-keyboard]:not([data-preview="true"]) .bxc-sheet { position: sticky; }
/* The radius is inherited down from the panel so a floating drawer's corners, the ones squared
   against the rail included, crop the dim layer and the card exactly as they crop the panel. */
.bxc-sheet__layer {
  position: absolute; left: 0; right: 0; bottom: 0; height: var(--bxc-sheet-h, 100vh);
  display: flex; flex-direction: column; justify-content: flex-end;
  overflow: hidden; border-radius: inherit;
}
/* Id-scoped for the same reason as the overlay: it is empty by design, and themes hide empty divs. */
#bxc-drawer .bxc-sheet__dim {
  display: block; position: absolute; inset: 0;
  background: var(--bxc-overlay);
  animation: bxc-sheet-dim .28s ease both;
}
.bxc-sheet__card {
  position: relative; display: flex; flex-direction: column;
  max-height: 80%;
  background: var(--bxc-bg); color: var(--bxc-text);
  border-radius: var(--bxc-radius-lg) var(--bxc-radius-lg) 0 0;
  box-shadow: 0 -4px 24px rgba(0,0,0,0.16);
  animation: bxc-sheet-rise .28s ease both;
}
/* The panel already ends where the keyboard starts, and what is left is too short to give a fifth
   of it away: the field and its button have to stay in view. */
#bxc-drawer[data-bxc-keyboard] .bxc-sheet__card { max-height: 100%; }
#bxc-drawer .bxc-sheet__card:focus, #bxc-drawer .bxc-sheet__card:focus-visible { outline: none; }
.bxc-sheet__head {
  flex: 0 0 auto;
  display: flex; align-items: center; justify-content: space-between; gap: var(--bxc-gap);
  padding: 12px var(--bxc-gutter) 8px;
}
#bxc-drawer .bxc-sheet__title {
  margin: 0; min-width: 0; overflow-wrap: anywhere;
  font-size: var(--bxc-fs-16); font-weight: 600; line-height: 1.4;
  font-family: inherit; text-transform: none; letter-spacing: normal; color: inherit;
}
.bxc-sheet__body {
  flex: 1 1 auto; min-height: 0; overflow-y: auto; overflow-x: hidden;
  overscroll-behavior: contain;
  padding: 4px var(--bxc-gutter) var(--bxc-space);
}
/* The rail sits beside the cart column, outside the sheet's dim layer, so it dims on its own: a
   rail that cannot be used must not look as if it could. On ::before: ::after is the seam cast.
   Above the cards and that cast, and it lets the click through to the region, which closes the
   sheet. */
#bxc-drawer[data-bxc-sheet] .bxc-region--side:has(.bxc-upsell, .bxc-upsells__placeholder)::before {
  content: ''; position: absolute; inset: 0; z-index: 3; pointer-events: none;
  background: var(--bxc-overlay);
  animation: bxc-sheet-dim .28s ease both;
}
/* The seam line is the region's border, and the dim cannot reach it: ::before stops at the padding
   box and the region clips there. So the line takes the dimmed colour itself, --bxc-overlay's 40%
   black over it, and arrives with the dim. */
#bxc-drawer[data-bxc-sheet] .bxc-panel[data-side] .bxc-region--side:has(.bxc-upsell) {
  border-color: color-mix(in srgb, #000 40%, var(--bxc-border));
  transition: border-color .28s ease;
}
/* For browsers without inert. Tab is held by the shell's trap either way. */
#bxc-drawer[data-bxc-sheet] .bxc-region--side .bxc-side { pointer-events: none; }
@keyframes bxc-sheet-rise { from { transform: translateY(100%); } to { transform: translateY(0); } }
@keyframes bxc-sheet-dim { from { opacity: 0; } to { opacity: 1; } }
/* The rail's selector is repeated whole: without its :has() this one is lighter and loses. */
@media (prefers-reduced-motion: reduce) {
  #bxc-drawer .bxc-sheet__dim, #bxc-drawer .bxc-sheet__card,
  #bxc-drawer[data-bxc-sheet] .bxc-region--side:has(.bxc-upsell, .bxc-upsells__placeholder)::before { animation: none; }
  #bxc-drawer[data-bxc-sheet] .bxc-panel[data-side] .bxc-region--side:has(.bxc-upsell) { transition: none; }
}

/* The module root is now only a column: its message centres in the free space and its upsell block
   takes the bottom, so an empty cart lays out on the same grammar as a full one. */
.bxc-empty { display: none; flex-direction: column; background: var(--bxc-empty-bg, transparent); color: var(--bxc-empty-text, inherit); }
.bxc-panel .bxc-empty { padding: 0; }
.bxc-empty__message {
  flex: 1 1 auto;
  display: flex; flex-direction: column; align-items: center; justify-content: center;
  gap: 4px; text-align: center; padding: 48px 24px;
}
#bxc-drawer[data-empty="true"] .bxc-empty { display: flex; }
#bxc-drawer[data-empty="true"] .bxc-hide-when-empty { display: none; }
/* The body is already a flex column; the empty region just takes the whole scroll area, so its
   own justify-content can centre the empty state. */
/* min-width:0 on the item, not just min-height on the region: this flex container runs in the
   default row direction, so the empty state sits on its main axis and its automatic minimum size
   is its content's min-content width. A carousel of wide cards then refuses to shrink and pushes
   the whole column past the panel instead of scrolling inside it. */
#bxc-drawer[data-empty="true"] .bxc-region--empty { display: flex; flex: 1 1 auto; min-height: 0; }
#bxc-drawer[data-empty="true"] .bxc-empty { min-width: 0; }
#bxc-drawer[data-empty="true"] .bxc-empty { flex: 1 1 auto; }
/* Sized in px, not the icon's own units, so a tall custom upload cannot stretch the empty state. */
/* Base gap is tight so title + subtitle read as one pair; the icon and the action buy their own
   separation with margins rather than every element sharing one uniform gap. */
.bxc-empty__icon { width: 48px; height: 48px; object-fit: contain; margin-bottom: 12px; }
.bxc-empty__title { font-size: var(--bxc-fs-16); font-weight: 600; margin: 0; }
.bxc-empty__subtitle { color: var(--bxc-muted); margin: 0; }
/* Id-prefixed: the theme reset above zeroes every button margin, and this button is a <button>
   whenever the merchant leaves the link empty (the close-the-drawer case). */
#bxc-drawer .bxc-empty__btn { text-decoration: none; margin-top: 20px; }
/* A real section box, not a block nested in the centred message: same gutter as every module in
   the panel, and the divider comes from the same token, so it lines up with the drawer rather than
   with the empty state's own 24px inset. */
.bxc-empty__upsells {
  text-align: left; padding: var(--bxc-space) var(--bxc-gutter);
  border-top: var(--bxc-divider);
}
/* Same unboxing the module boxes get: a cleared list holds only its live region, and this host
   would otherwise keep its band and border as a ghost strip under the empty state. Unboxed, not
   hidden — display:none would take the live region out of the accessibility tree. */
.bxc-empty__upsells:not(:has(> :not(.bxc-sr))) { padding: 0; border: 0; background: none; }

.bxc-btn {
  appearance: none; cursor: pointer; border: 0; border-radius: var(--bxc-radius);
  font: inherit; font-weight: 600; padding: 14px 20px;
  background: var(--bxc-accent); color: var(--bxc-accent-text);
  display: inline-flex; align-items: center; justify-content: center; gap: 8px;
  transition: background-color .15s ease, opacity .15s ease;
}
/* Mixes toward the panel background, so it reads as a hover on a dark or a light accent alike. */
#bxc-drawer .bxc-btn:hover:not(:disabled) { background: color-mix(in srgb, var(--bxc-accent) 86%, var(--bxc-bg)); }
#bxc-drawer .bxc-btn:active:not(:disabled) { background: color-mix(in srgb, var(--bxc-accent) 78%, var(--bxc-bg)); }
.bxc-btn:disabled { opacity: .5; cursor: default; }
.bxc-btn--ghost { background: transparent; color: var(--bxc-footer-ink, var(--bxc-text)); border: 1px solid var(--bxc-border-control); }
#bxc-drawer .bxc-btn--ghost:hover:not(:disabled) { background: var(--bxc-wash); }
/* Theme bleed: a bare button:hover outranks our class rules (one paints black with white text on
   our white panel), and the fills above skip disabled buttons. Only states are pinned, so merchant
   CSS still wins at rest. */
#bxc-drawer .bxc-btn:is(:hover, :focus, :active) { color: var(--bxc-accent-text); }
#bxc-drawer .bxc-btn:disabled:hover { background: var(--bxc-accent); }
#bxc-drawer .bxc-btn--ghost:is(:hover, :focus, :active) { color: var(--bxc-footer-ink, var(--bxc-text)); }
#bxc-drawer .bxc-btn--ghost:disabled:hover { background: transparent; }
/* The checkout button's own colours: the same states at the same weight, each reading the block
   key with the drawer's accent as its fallback, so an absent key paints exactly the rules above. */
#bxc-drawer .bxc-checkout:hover:not(:disabled) { background: color-mix(in srgb, var(--bxc-checkout-bg, var(--bxc-accent)) 86%, var(--bxc-bg)); }
#bxc-drawer .bxc-checkout:active:not(:disabled) { background: color-mix(in srgb, var(--bxc-checkout-bg, var(--bxc-accent)) 78%, var(--bxc-bg)); }
#bxc-drawer .bxc-checkout:is(:hover, :focus, :active) { color: var(--bxc-checkout-text, var(--bxc-accent-text)); }
#bxc-drawer .bxc-checkout:disabled:hover { background: var(--bxc-checkout-bg, var(--bxc-accent)); }
/* The upsell Add and the empty cart's button the same way: class weight after .bxc-btn, then the
   states at their weight. The Add reads its roots' variables, as the gift sheet reuses it. */
.bxc-upsell__add { background: var(--bxc-upsell-add-bg, var(--bxc-accent)); color: var(--bxc-upsell-add-text, var(--bxc-accent-text)); }
#bxc-drawer .bxc-upsell__add:hover:not(:disabled) { background: color-mix(in srgb, var(--bxc-upsell-add-bg, var(--bxc-accent)) 86%, var(--bxc-bg)); }
#bxc-drawer .bxc-upsell__add:active:not(:disabled) { background: color-mix(in srgb, var(--bxc-upsell-add-bg, var(--bxc-accent)) 78%, var(--bxc-bg)); }
#bxc-drawer .bxc-upsell__add:is(:hover, :focus, :active) { color: var(--bxc-upsell-add-text, var(--bxc-accent-text)); }
#bxc-drawer .bxc-upsell__add:disabled:hover { background: var(--bxc-upsell-add-bg, var(--bxc-accent)); }
.bxc-empty__btn { background: var(--bxc-empty-btn, var(--bxc-accent)); color: var(--bxc-empty-btn-text, var(--bxc-accent-text)); }
#bxc-drawer .bxc-empty__btn:hover:not(:disabled) { background: color-mix(in srgb, var(--bxc-empty-btn, var(--bxc-accent)) 86%, var(--bxc-empty-bg, var(--bxc-bg))); }
#bxc-drawer .bxc-empty__btn:active:not(:disabled) { background: color-mix(in srgb, var(--bxc-empty-btn, var(--bxc-accent)) 78%, var(--bxc-empty-bg, var(--bxc-bg))); }
#bxc-drawer .bxc-empty__btn:is(:hover, :focus, :active) { color: var(--bxc-empty-btn-text, var(--bxc-accent-text)); }
#bxc-drawer .bxc-empty__btn:disabled:hover { background: var(--bxc-empty-btn, var(--bxc-accent)); }

/* line items */
/* padding:0 is load-bearing, not tidying: the UA sheet indents every ul by 40px, and the reset at
   the top of this file only zeroes margin. Themes that reset list padding globally hid this, so the
   lines sat at the gutter on some storefronts and 40px inside it on others. */
.bxc-lines { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--bxc-line-gap); }
/* The shopper's own items are a list, so a rule separates them — the offers around them are cards,
   and the contrast is what lets the eye split "yours" from "suggested" without reading anything.
   On the second item of a pair, so the list never opens or closes on a rule; the flex gap above and
   equal padding below centre each rule in its band. */
.bxc-lines > .bxc-line + .bxc-line { border-top: 1px solid var(--bxc-border); padding-top: var(--bxc-line-gap); }
/* One row of three: the controls live inside the info column rather than on a row of their own,
   so a tall price stack cannot push them away from the product they belong to. */
.bxc-line {
  display: grid; column-gap: var(--bxc-gap); align-items: start;
  grid-template-columns: var(--bxc-line-img) 1fr auto;
  grid-template-areas: 'img info price';
}
.bxc-line__img {
  grid-area: img; width: var(--bxc-line-img); height: auto; aspect-ratio: var(--bxc-img-ratio, 1 / 1);
  object-fit: cover; border-radius: var(--bxc-radius);
  /* Width, not shorthand: the colour stays on the merchant's border token, so turning the edge
     off never has to restate it. */
  border: var(--bxc-img-border-w, 1px) solid var(--bxc-border);
}
.bxc-line__media { grid-area: img; display: block; line-height: 0; }
.bxc-line__img { transition: opacity .15s ease; }
#bxc-drawer .bxc-line__media[href]:hover .bxc-line__img { opacity: .82; }
/* One stack, one gap: title, variant, discount and controls are all rows of the same column. */
.bxc-line__info { grid-area: info; min-width: 0; display: flex; flex-direction: column; align-items: flex-start; gap: 4px; }
.bxc-line__link { color: inherit; text-decoration: none; }
#bxc-drawer a.bxc-line__link:hover { text-decoration: underline; }
.bxc-line__title { font-weight: 600; font-size: var(--bxc-fs-13); margin: 0; }
/* Full-strength like the select it replaces: which variant was bought is part of identifying the
   item, and toggling the selector off should not demote that information. */
.bxc-line__variant { color: var(--bxc-text); font-size: var(--bxc-fs-12); }
/* Borderless: it reads as the variant label it replaces, with the chevron carrying the affordance.
   Chevron is a background image on the control, not a wrapper pseudo-element, which would detach. */
#bxc-drawer .bxc-line__variant-select {
  appearance: none; -webkit-appearance: none;
  /* Full-strength text, unlike the static .bxc-line__variant label: colour is what separates the
     interactive variant from the one that only reports. */
  font: inherit; font-size: var(--bxc-fs-12); line-height: 1.4; color: var(--bxc-text);
  /* field-sizing shrinks to the selected option; width:auto is the pre-support fallback. */
  align-self: flex-start; width: auto; field-sizing: content; max-width: 100%; min-width: 0;
  /* No left padding: the control sits flush with the title and discount above and below it. */
  margin: 0; padding: 0 16px 0 0; border: 0; border-radius: var(--bxc-radius-ctl); cursor: pointer;
  background-color: transparent;
  background-image: var(--bxc-chevron);
  background-repeat: no-repeat; background-position: right center; background-size: 12px;
  text-overflow: ellipsis; transition: color .15s ease;
}
/* Underline, matching the product title's hover: the drawer's one signal for interactive text.
   A colour shift cannot work here — the resting colour is already full-strength --bxc-text. */
#bxc-drawer .bxc-line__variant-select:hover:not(:disabled) { text-decoration: underline; }
#bxc-drawer .bxc-line__variant-select:disabled { opacity: .6; cursor: progress; }
/* Disabled with no swap in flight is a gift at the line budget: nothing is loading. */
#bxc-drawer .bxc-line:not([data-bxc-busy]) .bxc-line__variant-select:disabled { cursor: not-allowed; }
/* Tighter than the info column's 4px: plan and properties are one group, not separate rows. */
.bxc-line__details { display: flex; flex-direction: column; gap: 2px; min-width: 0; max-width: 100%; }
.bxc-line__plan,
.bxc-line__prop { color: var(--bxc-muted); font-size: var(--bxc-fs-12); min-width: 0; overflow-wrap: anywhere; }
.bxc-line__prop-label { font-weight: 500; }
/* Underlined at rest, unlike the drawer's hover-underline links: inside muted property text there
   is no other cue that the filename opens the shopper's upload. */
#bxc-drawer a.bxc-line__prop-file { color: inherit; text-decoration: underline; }
/* The items inside a merged line: the toggle reads as a line detail, the rows as the line's own
   text. Id-scoped like the accordion toggle, so the button reset above does not take its box. */
.bxc-line__components { display: flex; flex-direction: column; gap: 4px; min-width: 0; max-width: 100%; }
#bxc-drawer .bxc-line__components-toggle {
  appearance: none; border: 0; background: transparent; cursor: pointer;
  display: inline-flex; align-items: center; gap: 4px; padding: 0; margin: 0;
  font: inherit; font-size: var(--bxc-fs-12); line-height: 1.4; color: var(--bxc-muted); text-align: left;
}
#bxc-drawer .bxc-line__components-toggle:hover { text-decoration: underline; }
.bxc-line__components-toggle::after {
  content: ''; width: 12px; height: 12px; flex: 0 0 auto;
  background: var(--bxc-chevron) center / 12px no-repeat;
  transition: transform .15s ease;
}
.bxc-line__components-toggle[aria-expanded="true"]::after { transform: rotate(180deg); }
.bxc-line__components-list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: 4px; }
.bxc-component { display: flex; align-items: center; gap: 8px; min-width: 0; font-size: var(--bxc-fs-12); color: var(--bxc-text); }
.bxc-component__img {
  display: block; width: 32px; height: 32px; flex: 0 0 auto; object-fit: cover;
  border-radius: var(--bxc-radius); border: var(--bxc-img-border-w, 1px) solid var(--bxc-border);
}
.bxc-component__text { display: flex; flex-direction: column; min-width: 0; overflow-wrap: anywhere; }
.bxc-component__variant { color: var(--bxc-muted); }
/* Muted: the discount name explains a price the shopper can already see, so it sits below the
   variant in the hierarchy rather than competing with it. */
.bxc-line__discount {
  display: flex; align-items: flex-start; gap: 4px; min-width: 0;
  color: var(--bxc-muted); font-size: var(--bxc-fs-12);
}
/* Icon tracks the first text line, not the block's centre, so wrapped names stay aligned. */
/* Centres the fixed 12px icon in the first text line at any font size or leading. The em version
   is the fallback for browsers without the lh unit and assumes the default 1.4. */
.bxc-line__discount svg { flex: 0 0 auto; margin-top: calc((1.4em - 12px) / 2); }
@supports (margin-top: 1lh) {
  .bxc-line__discount svg { margin-top: calc((1lh - 12px) / 2); }
}
.bxc-line__discount span { min-width: 0; overflow-wrap: anywhere; }
.bxc-line__error { color: var(--bxc-critical); font-size: var(--bxc-fs-12); min-width: 0; overflow-wrap: anywhere; }
/* Dim only, never disabled: the store applies deltas against live quantity inside its queue, so
   rapid clicks must keep landing. Pointer stays default so the line does not read as locked. */
.bxc-line[data-bxc-working] .bxc-line__price,
.bxc-line[data-bxc-working] .bxc-stepper__val { opacity: .5; transition: opacity .15s ease; }
/* Stacked, not inline: the cell is narrow and right-aligned, so two long amounts on one line
   wrap unpredictably. */
.bxc-line__prices { display: flex; flex-direction: column; align-items: flex-end; gap: 2px; font-size: var(--bxc-fs-13); }
.bxc-line__price--was { color: var(--bxc-muted); text-decoration: line-through; font-size: var(--bxc-fs-12); }
.bxc-line__price--now { font-weight: 600; }
.bxc-line__per-unit { color: var(--bxc-muted); font-size: var(--bxc-fs-12); }
.bxc-line__save { color: var(--bxc-sale); font-size: var(--bxc-fs-12); }
.bxc-line__price { grid-area: price; display: flex; flex-direction: column; align-items: flex-end; gap: 2px; text-align: right; }
.bxc-line__prices:not(:only-child) { margin-bottom: 2px; }
/* 4px reads as 8px beside the trash, whose hit padding sits between the gap and the icon. Wider
   next to the stepper: the trash must not sit within mis-tap range of the + button. */
.bxc-line__qty { display: flex; align-items: center; gap: 4px; }
/* Visual only: the controls are rendered disabled and the module rejects their keys, since
   pointer-events would not stop keyboard activation. */
.bxc-line[data-bxc-busy] { opacity: .6; }
/* The info column's 4px buys the text rows ~6px of visible air, because each line box carries
   half-leading the controls below them do not. Both qty rules add that back so the boundary
   between "what this is" and "what I can change" lands at ~10px, the loosest gap in the stack. */
.bxc-line__qty:has(.bxc-stepper) { gap: var(--bxc-gap); margin-top: 6px; }
/* overflow: hidden so the button wash is clipped by the container's radius. */
/* No hover on the group: only the +/- are interactive, and darkening the border when the cursor is
   over the (inert) number reads as a false affordance. Each button signals on itself. */
#bxc-drawer .bxc-stepper {
  display: inline-flex; align-items: center; overflow: hidden;
  border: 1px solid var(--bxc-border-control); border-radius: var(--bxc-radius-ctl);
  transition: border-color .15s ease;
}
/* 32px, not the bare 24px WCAG floor: at the floor a merchant's own font or padding tightening
   drops the control under it, and the cluster reads as an afterthought beside a 64px thumbnail. */
#bxc-drawer .bxc-stepper__btn {
  appearance: none; border: 0; background: transparent; cursor: pointer;
  width: 32px; height: 32px; font-size: var(--bxc-fs-14); line-height: 1; color: var(--bxc-text);
  transition: background-color .15s ease;
}
#bxc-drawer .bxc-stepper__btn:hover:not(:disabled) { background: var(--bxc-wash); }
.bxc-stepper__btn:disabled { opacity: .4; cursor: not-allowed; }
#bxc-drawer .bxc-stepper__btn:disabled:hover { background: transparent; }
/* tabular-nums: the value must not jiggle when it crosses 9 to 10. */
/* 16px for the same iOS zoom reason as the form controls in the mobile block below, but declared on
   the base rule: this is a focusable input at every width, and the block below stops at 480px. */
#bxc-drawer .bxc-stepper__val {
  width: 34px; text-align: center; font-size: 16px; font-variant-numeric: tabular-nums;
  appearance: textfield; -moz-appearance: textfield;
  border: 0; padding: 0; margin: 0; background: transparent; color: inherit; font-family: inherit;
}
/* The zoom guard above is a touch constraint: iOS reports a coarse pointer, so a fine one can never
   trigger it. Cursor devices drop to the line's own type scale and a target sized for a mouse. */
@media (pointer: fine) {
  #bxc-drawer .bxc-stepper__btn { width: 28px; height: 28px; }
  #bxc-drawer .bxc-stepper__val { width: 28px; font-size: var(--bxc-fs-13); }
}
.bxc-stepper__val::-webkit-outer-spin-button,
.bxc-stepper__val::-webkit-inner-spin-button { -webkit-appearance: none; margin: 0; }
/* Focus lands on the group, not the borderless input inside it: drawn on the input it is a hard
   square boxed inside the rounded border, and a text field matches :focus-visible on click. Same
   signal as the discount and note fields — the border takes the accent, no ring on top. */
#bxc-drawer .bxc-stepper__val:focus-visible { outline: none; }
#bxc-drawer .bxc-stepper:has(.bxc-stepper__val:focus-visible) { border-color: var(--bxc-accent); }
/* The +/- ring the same way: the group clips overflow, so a button's own outline would show as a
   bare bar on its inner edge. */
#bxc-drawer .bxc-stepper__btn:focus-visible { outline: none; }
#bxc-drawer .bxc-stepper:has(.bxc-stepper__btn:focus-visible) { border-color: var(--bxc-accent); box-shadow: 0 0 0 1px var(--bxc-accent); }
.bxc-line__qtyval { font-size: var(--bxc-fs-13); color: var(--bxc-muted); font-variant-numeric: tabular-nums; }
#bxc-drawer .bxc-line__remove {
  flex: 0 0 auto;
  appearance: none; border: 0; background: transparent; cursor: pointer; color: var(--bxc-muted);
  padding: 4px; line-height: 0; border-radius: 999px;
  transition: color .15s ease, background-color .15s ease;
}
/* Same round wash the close button takes: both are bare icon buttons, so they answer a cursor the
   same way. */
#bxc-drawer .bxc-line__remove:hover { color: var(--bxc-text); background: var(--bxc-wash); }
/* Alone on the row, the icon's 4px hit-area padding would indent it from the text above; pulling
   the box back aligns the glyph optically without shrinking the tap target. */
#bxc-drawer .bxc-line__qty > .bxc-line__remove:first-child { margin-left: -4px; }
/* Without a stepper the icon button's own 4px hit padding is already part of the gap, so it takes
   2px rather than the stepper's 6px to reach the same ~10px. */
.bxc-line__qty:not(:has(.bxc-stepper)) { margin-top: 2px; }

/* rewards bar */
.bxc-rewards { display: flex; flex-direction: column; gap: 8px; background: var(--bxc-rewards-bg, transparent); color: var(--bxc-rewards-text, inherit); }
.bxc-rewards__msg { font-size: var(--bxc-fs-13); margin: 0; text-align: center; }
.bxc-rewards__msg strong { font-weight: 600; }
.bxc-rewards__track { height: 8px; border-radius: 999px; background: var(--bxc-border); overflow: hidden; }
.bxc-rewards__fill { height: 100%; background: var(--bxc-rewards-bar, var(--bxc-accent)); width: 0; transition: width .4s ease; }
/* Markers overhang the track ends, so these layouts trade the overflow crop for a rounded fill. */
.bxc-rewards[data-layout="milestones"] .bxc-rewards__track,
.bxc-rewards[data-layout="tags"] .bxc-rewards__track,
.bxc-rewards[data-layout="icon"] .bxc-rewards__track { overflow: visible; position: relative; }
.bxc-rewards[data-layout="milestones"] .bxc-rewards__fill,
.bxc-rewards[data-layout="tags"] .bxc-rewards__fill,
.bxc-rewards[data-layout="icon"] .bxc-rewards__fill { border-radius: 999px; }
/* The last stop sits on the track's right end, so the track stops half a stop short of the panel
   edge there. The left end carries no stop (a first stop near it is pushed right by the same half,
   see --bxc-rw-half), so the track starts at the edge. Labels share the same insets. */
.bxc-rewards[data-layout="icon"] .bxc-rewards__track { margin: 11px 15px 11px 0; }
.bxc-rewards[data-layout="milestones"] .bxc-rewards__track,
.bxc-rewards[data-layout="tags"] .bxc-rewards__track { margin: 3px 7px 3px 0; }
.bxc-rewards__tags { display: flex; flex-wrap: wrap; justify-content: center; gap: 6px; }
.bxc-rewards__tags:empty { display: none; }
.bxc-rewards__tag {
  display: inline-flex; align-items: center; gap: 6px; max-width: 100%;
  padding: 4px 10px; border-radius: 999px; font-size: var(--bxc-fs-12); line-height: 1.4;
  border: 1px solid var(--bxc-border); color: var(--bxc-rewards-text, var(--bxc-muted));
  transition: border-color .3s ease, background .3s ease, color .3s ease;
}
.bxc-rewards__tag[data-unlocked="true"] {
  border-color: color-mix(in srgb, var(--bxc-rewards-bar, var(--bxc-accent)) 45%, transparent);
  background: color-mix(in srgb, var(--bxc-rewards-bar, var(--bxc-accent)) 8%, transparent);
  color: var(--bxc-rewards-text, var(--bxc-text));
}
.bxc-rewards__tag-svg { flex-shrink: 0; }
.bxc-rewards__tag-name { white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bxc-rewards__tag-value { opacity: .75; }
.bxc-rewards__tag-check { flex-shrink: 0; color: var(--bxc-rewards-bar, var(--bxc-accent)); display: none; }
.bxc-rewards__tag[data-unlocked="true"] .bxc-rewards__tag-check { display: block; }
/* The absolute labels give the row no height, so it reserves two lines. */
.bxc-rewards__labels { --bxc-rw-inset: 7px; position: relative; height: 30px; margin: 0 var(--bxc-rw-inset) 0 0; }
.bxc-rewards[data-layout="icon"] .bxc-rewards__labels { --bxc-rw-inset: 15px; margin-top: -4px; }
.bxc-rewards__marker { --bxc-rw-half: 7px; }
.bxc-rewards__stop { --bxc-rw-half: 15px; }
.bxc-rewards__label {
  position: absolute; top: 0; transform: translateX(-50%);
  display: flex; flex-direction: column; align-items: center;
  font-size: var(--bxc-fs-11); line-height: 1.3; color: var(--bxc-rewards-text, var(--bxc-muted)); text-align: center;
}
/* End labels line up with the stop's outer edge, which is the panel's content edge, not its centre. */
.bxc-rewards__label[data-edge="start"] { transform: none; align-items: flex-start; text-align: left; }
.bxc-rewards__label[data-edge="end"] {
  transform: translateX(calc(-100% + var(--bxc-rw-inset))); align-items: flex-end; text-align: right;
}
.bxc-rewards__label-name { max-width: 100%; white-space: nowrap; overflow: hidden; text-overflow: ellipsis; }
.bxc-rewards__label[data-unlocked="true"] { color: var(--bxc-rewards-text, var(--bxc-text)); }
.bxc-rewards__label[data-unlocked="true"] .bxc-rewards__label-name { font-weight: 600; }
.bxc-rewards__marker {
  position: absolute; top: 50%; transform: translate(-50%, -50%);
  width: 14px; height: 14px; border-radius: 999px;
  background: var(--bxc-rewards-bg, var(--bxc-bg)); border: 2px solid var(--bxc-border);
  transition: border-color .3s ease, background .3s ease;
}
.bxc-rewards__marker[data-unlocked="true"] { border-color: var(--bxc-rewards-bar, var(--bxc-accent)); background: var(--bxc-rewards-bar, var(--bxc-accent)); }
.bxc-rewards__stop {
  position: absolute; top: 50%; transform: translate(-50%, -50%);
  display: flex; align-items: center; justify-content: center;
  width: 30px; height: 30px; border-radius: 999px;
  background: var(--bxc-rewards-bg, var(--bxc-bg)); border: 1.5px solid var(--bxc-border); color: var(--bxc-muted);
  transition: border-color .3s ease, color .3s ease, background .3s ease;
}
.bxc-rewards__stop[data-unlocked="true"] {
  border-color: var(--bxc-rewards-bar, var(--bxc-accent)); color: var(--bxc-rewards-bg, var(--bxc-bg)); background: var(--bxc-rewards-bar, var(--bxc-accent));
}
/* One-shot motion, only when something changes: a stop pops as its tier unlocks, and the fill
   sweeps one shine when the drawer opens close to the next tier. Never looping. The translate
   is repeated because the keyframes replace the stop's centring transform. */
@keyframes bxc-rw-pop {
  0% { transform: translate(-50%, -50%) scale(1); }
  45% { transform: translate(-50%, -50%) scale(1.28); }
  100% { transform: translate(-50%, -50%) scale(1); }
}
.bxc-rewards__stop[data-anim="pop"], .bxc-rewards__marker[data-anim="pop"] {
  animation: bxc-rw-pop .45s cubic-bezier(.34, 1.56, .64, 1);
}
.bxc-rewards__fill { position: relative; overflow: hidden; }
@keyframes bxc-rw-shine { from { transform: translateX(-100%); } to { transform: translateX(100%); } }
.bxc-rewards__fill[data-anim="shine"]::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(90deg, transparent, color-mix(in srgb, var(--bxc-rewards-bg, var(--bxc-bg)) 55%, transparent), transparent);
  animation: bxc-rw-shine 1.1s ease-in-out .45s both;
}
@media (prefers-reduced-motion: reduce) {
  .bxc-rewards__stop[data-anim], .bxc-rewards__marker[data-anim], .bxc-rewards__fill[data-anim]::after { animation: none; }
}

/* announcement */
.bxc-announce { background: var(--bxc-announce-bg, var(--bxc-accent)); color: var(--bxc-announce-text, var(--bxc-accent-text)); font-size: var(--bxc-fs-13); text-align: center; display: flex; flex-direction: column; align-items: center; gap: 2px; }
.bxc-panel .bxc-announce { padding-block: 8px; }

/* countdown */
.bxc-countdown { background: var(--bxc-countdown-bg, var(--bxc-accent)); color: var(--bxc-countdown-text, var(--bxc-accent-text)); text-align: center; display: flex; flex-direction: column; align-items: center; gap: 4px; }
.bxc-panel .bxc-countdown { padding-block: 8px; }
.bxc-countdown__text { font-size: var(--bxc-fs-13); }
.bxc-countdown__title { font-size: var(--bxc-fs-13); font-weight: 600; }
.bxc-countdown__units { display: flex; align-items: flex-start; gap: 8px; }
.bxc-countdown__unit { display: flex; flex-direction: column; align-items: center; min-width: 34px; }
.bxc-countdown__num { font-size: var(--bxc-fs-18); font-weight: 600; font-variant-numeric: tabular-nums; line-height: 1.1; }
.bxc-countdown__label { font-size: var(--bxc-fs-10); text-transform: uppercase; letter-spacing: .04em; opacity: .8; }

/* upsells */
.bxc-upsells { display: flex; flex-direction: column; gap: var(--bxc-gap); }
/* Declared on the block's three roots only (the cart's, the empty cart's, the rail): the gift sheet
   reuses the Add and the card on the drawer colours. */
.bxc-upsells, .bxc-side {
  --bxc-upsell-add-bg: var(--bxc-upsells-btn, var(--bxc-accent));
  --bxc-upsell-add-text: var(--bxc-upsells-btn-text, var(--bxc-accent-text));
  /* Set only while the block's Text is: an absent key leaves it invalid, so the card inherits as
     before; at 0% the mix is the drawer text, which the card's drawer background needs. */
  --bxc-upsell-card-text: color-mix(in srgb, var(--bxc-upsells-text) 0%, var(--bxc-text));
}
.bxc-upsells__title { font-size: var(--bxc-fs-13); font-weight: 600; margin: 0; }
.bxc-upsells__list { list-style: none; margin: 0; padding: 0; display: flex; flex-direction: column; gap: var(--bxc-gap); }
/* Editor-preview only: empty-selection notice rendered in place of the product cards, and in place
   of the add-on row before a product is picked. */
.bxc-upsells__placeholder,
.bxc-toggle__placeholder {
  margin: 0; padding: 14px 12px; text-align: center;
  border: 1px dashed var(--bxc-border); border-radius: var(--bxc-radius-sm);
  color: var(--bxc-muted); font-size: var(--bxc-fs-12);
}
.bxc-upsell {
  display: grid; gap: var(--bxc-gap); align-items: center;
  grid-template-columns: var(--bxc-upsell-img, 48px) 1fr auto;
  grid-template-areas: 'img info action';
}
.bxc-upsell__media { grid-area: img; display: block; line-height: 0; }
#bxc-drawer .bxc-upsell__media[href]:hover .bxc-upsell__img { opacity: .82; }
.bxc-upsell__img { grid-area: img; width: var(--bxc-upsell-img, 48px); height: auto; aspect-ratio: var(--bxc-img-ratio, 1 / 1); object-fit: cover; border-radius: var(--bxc-radius-sm); }
.bxc-upsell__link { color: inherit; text-decoration: none; }
#bxc-drawer a.bxc-upsell__link:hover { text-decoration: underline; }
.bxc-upsell__info { grid-area: info; min-width: 0; display: flex; flex-direction: column; gap: 2px; }
/* Upsell adds are a secondary action next to the checkout button, so they run one step down from
   the .bxc-btn box rather than matching it. One size across every upsell surface: the same action
   at two sizes reads as an inconsistency rather than a hierarchy, and at the larger box the button
   outweighed the product title it belongs to. */
#bxc-drawer .bxc-upsell__add { grid-area: action; padding: 7px 12px; font-size: var(--bxc-fs-12); }
/* A half-step under the price's 600: on a card the price and the Add are the decision, and two
   equal-weight lines stacked in one column read as neither leading. Cart-line titles stay 600 —
   there the title is the shopper's own item and sits in its own column. */
.bxc-upsell__title {
  font-size: var(--bxc-fs-12); font-weight: 500; margin: 0;
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
}
.bxc-upsell__prices { display: flex; gap: 6px; align-items: baseline; }
/* Same pair the cart lines draw: the price being charged carries the weight and the body colour,
   and only the crossed-out one is muted and a step down. Muting both left the tile with no price
   the eye lands on. */
.bxc-upsell__price { font-size: var(--bxc-fs-12); font-weight: 600; }
.bxc-upsell__price--was { font-size: var(--bxc-fs-11); color: var(--bxc-muted); text-decoration: line-through; }
/* The charged price and its percent badge stay one unit, so the tile's stacked column cannot
   split them onto two rows. */
.bxc-upsell__now { display: inline-flex; align-items: baseline; gap: 4px; }
.bxc-upsell__badge {
  font-size: var(--bxc-fs-10); font-weight: 600; line-height: 1.4; padding: 0 4px; border-radius: 4px;
  color: var(--bxc-sale); border: 1px solid currentColor; white-space: nowrap;
}
#bxc-drawer .bxc-upsell__variant { align-self: start; max-width: 100%; }
.bxc-upsell__error { color: var(--bxc-critical); font-size: var(--bxc-fs-12); min-width: 0; overflow-wrap: anywhere; }
/* Transparent text keeps the button's width; the spinner sits where the label was. Id weight and
   after the pinned button states, or a hover would paint the label back over the spinner. */
#bxc-drawer .bxc-btn.bxc-upsell__add--busy { color: transparent; position: relative; }
.bxc-upsell__add--busy::after {
  content: ''; position: absolute; inset: 0; margin: auto; width: 14px; height: 14px;
  border: 2px solid var(--bxc-upsell-add-text, var(--bxc-accent-text)); border-top-color: transparent; border-radius: 50%;
  animation: bxc-spin .6s linear infinite;
}
@keyframes bxc-spin { to { transform: rotate(360deg); } }
@media (prefers-reduced-motion: reduce) {
  .bxc-upsell__add--busy::after { animation-duration: 1.6s; }
}

/* carousel layout — native scroll-snap so mobile swipes without any touch handlers */
.bxc-upsells__head { display: flex; align-items: center; justify-content: space-between; gap: var(--bxc-gap); }
.bxc-upsells__nav { display: flex; gap: 4px; }
.bxc-upsells__nav[hidden] { display: none; }
/* Filled, not transparent: the upsell module sits on the subdued surface, so a see-through control
   shows the tray through itself and loses its edge. The cards beside it take the primary surface
   for the same reason — a control has to read as sitting on the tray, not cut out of it. */
#bxc-drawer .bxc-upsells__nav-btn {
  display: flex; align-items: center; justify-content: center;
  width: 28px; height: 28px; padding: 0;
  background: var(--bxc-bg); color: var(--bxc-text);
  border: 1px solid var(--bxc-border-control); border-radius: var(--bxc-radius-ctl); cursor: pointer;
}
#bxc-drawer .bxc-upsells__nav-btn:hover:not(:disabled) { background: var(--bxc-wash); }
.bxc-upsells__nav-btn:disabled { opacity: .4; cursor: default; }
.bxc-upsells__list--carousel {
  flex-direction: row;
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  /* Both competitors omit this; without it a swipe past the last card chains to the page. */
  overscroll-behavior-x: contain;
  scrollbar-width: none;
  -ms-overflow-style: none;
}
.bxc-upsells__list--carousel::-webkit-scrollbar { display: none; }
.bxc-upsells__list--carousel .bxc-upsell {
  /* A peek of the next card is the swipe affordance; competitors' full-width slides hide it. */
  flex: 0 0 45%;
  scroll-snap-align: start;
}
/* One row at a time. Full-width slides leave no peek to hint the swipe, so the arrows carry that
   job here — which is why this layout always pages rather than free-scrolls. */
.bxc-upsells__list--single .bxc-upsell { flex-basis: 100%; }
.bxc-upsell--tile { display: flex; flex-direction: column; align-items: stretch; gap: 6px; }
/* Capped and centred, not just 100%: a tile's container width differs per surface (the rail is a
   fixed 220px, a carousel slide is a share of the merchant's drawer width), so an uncapped square
   renders a different size in each. The cap is what makes one product look the same size wherever
   it appears; the auto margins keep it centred when the card is wider than the cap. */
.bxc-upsell--tile .bxc-upsell__img {
  width: 100%; max-width: var(--bxc-upsell-tile-img); height: auto; aspect-ratio: var(--bxc-img-ratio, 1 / 1);
  margin-inline: auto;
}
.bxc-upsell--tile .bxc-upsell__info { flex: 1 1 auto; }
/* Price and Add share the card's last row. min-width:0 on the prices, not the button: a long
   compare-at pair has to be what gives, or it pushes the label out of its own card. */
.bxc-upsell--tile .bxc-upsell__buy {
  display: flex; align-items: center; justify-content: space-between; gap: var(--bxc-gap);
}
/* Stacked, not side by side: two prices in a row leave a 220px tile nothing for the button, and
   the was/now pair reads as a single price column anyway. */
/* No gap and tight leading: the pair has to read as one block the height of the button beside it,
   and at the cart lines' 2px it stood taller than the Add and pulled the row out of true. */
.bxc-upsell--tile .bxc-upsell__prices {
  min-width: 0; flex-direction: column; align-items: flex-start; gap: 0; line-height: 1.2;
}
/* Sized to its label rather than the card: beside a price, a full-width Add read as the tile's
   subject instead of its action. The box itself is the shared one above. */
#bxc-drawer .bxc-upsell--tile .bxc-upsell__add { flex: 0 0 auto; }
/* Content-width, like the row card's and the cart line's: stretched to the card, the chevron parks
   at the far edge with a gap of dead space between it and the variant it belongs to. */
#bxc-drawer .bxc-upsell--tile .bxc-upsell__variant { width: auto; max-width: 100%; }
/* Visually hidden, still announced: the upsell live region. */
.bxc-sr { position: absolute; width: 1px; height: 1px; margin: -1px; padding: 0; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0; }

/* side upsell rail — a 220px column of the box beside the cart, full height, desktop only */
/* overflow:hidden is what makes the rail read as emerging from under the cart on open: its child
   slides within this box instead of across the page. It is also what closes the region around a
   departing copy. */
.bxc-region--side { position: relative; flex: 0 0 auto; width: 220px; display: none; overflow: hidden; }
.bxc-panel[data-side="left"] .bxc-region--side { order: -1; }
/* The shell writes the page's computed direction here: a theme may set it by CSS alone, which no
   attribute selector sees. */
#bxc-drawer[data-dir="rtl"] :is(.bxc-cart, .bxc-region--side) { direction: rtl; }
/* Height as well as width: a phone held sideways clears 900px (932x430 on a Pro Max) but has room
   for neither a tile nor the cart beside it. RAIL_QUERY in sideUpsell.ts has to match this query. */
/* The keyboard exclusion is the same judgement by another route: it leaves no vertical room, and
   the panel becomes a scroll box under it, which would clip the rail rather than hide it. */
/* Shown only around content: the region is a column of the box, so an empty one would be a blank
   220px strip inside the drawer. The editor placeholder counts as content. */
@media (min-width: 900px) and (min-height: 600px) {
  /* Empty is spelled out rather than left to the hide rule below: every :not() added here raises
     this selector's weight, and that rule used to win on source order alone. */
  #bxc-drawer[data-open="true"]:not([data-bxc-keyboard]):not([data-empty="true"]) .bxc-region--side:has(.bxc-upsell, .bxc-upsells__placeholder) { display: block; }
}
/* The editor pane reserves room for the rail, so preview shows it at any window width. */
#bxc-drawer[data-preview="true"][data-open="true"] .bxc-region--side:has(.bxc-upsell, .bxc-upsells__placeholder) { display: block; }
#bxc-drawer[data-empty="true"] .bxc-region--side { display: none; }
/* Compound selector: it has to outrank the [data-bxc-module] section padding, not lose to it. */
.bxc-panel .bxc-side {
  background: var(--bxc-upsells-bg, var(--bxc-bg-subdued)); color: var(--bxc-upsells-text, var(--bxc-text));
  /* No inset of its own: every child owns its spacing here. The scroll box needs the rail's full
     width to put its scrollbar in the gutter rather than over the cards, and it has to reach the
     rail's bottom edge — a band there cuts the cards off short, which reads as the last one rather
     than as more below. The heading pays for the top band, the last card for the bottom. */
  padding: 0;
  /* Full height so the rail reads as the same surface as the panel. The scroll lives on the list
     below, not here: a scrolling surface takes its background with it only as far as its own box,
     so the shelf itself has to stay put and let the cards move against it. */
  height: 100%; overflow: hidden;
}
/* The cart sits a plane above the shelf, and the shadow that says so cannot come from the box:
   the rail is its DOM child, and a parent's box-shadow paints beneath its children. Nor
   from an inset shadow on the rail itself — the heading band paints its own background over it and
   cuts the cast off at the top. So it is an overlay on the region's inner edge, above everything on
   the shelf, shaped like the visible half of a cast: full alpha at the seam, gone in 10px. At the
   cards' own shadow weight, not the header's scroll-cast one — a constant cue only has to whisper.
   The border stays underneath, same pairing as the header's edge: the line closes the seam, the
   cast carries the depth. Gated like the border, so a placeholder-only rail has no floating stripe.
   Turning elevation off blanks the cast and leaves the border: the shelf still reads as its own
   surface, it just stops sitting a plane below the cart. */
.bxc-panel[data-side="left"] .bxc-region--side:has(.bxc-upsell) {
  border-right: 1px solid var(--bxc-border);
}
.bxc-panel[data-side="right"] .bxc-region--side:has(.bxc-upsell) {
  border-left: 1px solid var(--bxc-border);
}
.bxc-panel .bxc-region--side:has(.bxc-upsell)::after {
  content: ''; position: absolute; top: 0; bottom: 0; width: 10px; z-index: 2; pointer-events: none;
}
.bxc-panel[data-side="right"] .bxc-region--side:has(.bxc-upsell)::after {
  left: 0; background: linear-gradient(to right, var(--bxc-seam-cast, rgba(0,0,0,0.05)), transparent);
}
.bxc-panel[data-side="left"] .bxc-region--side:has(.bxc-upsell)::after {
  right: 0; background: linear-gradient(to left, var(--bxc-seam-cast, rgba(0,0,0,0.05)), transparent);
}
/* No gap: the heading is a band with its own padding, so a gap on top of it double-spaces the
   first card away from a header it is supposed to sit directly under. */
.bxc-panel .bxc-side { display: flex; flex-direction: column; gap: 0; }
/* Tighter than the cart's line gap: a shadowed card carries its own edge, so the stack needs
   breathing room, not separation. */
/* The rail's scroll box. min-height:0 is load-bearing: a flex item's auto minimum is its content,
   so without it the list grows the rail instead of scrolling inside it. The heading sits outside,
   so it stays put while the cards move, and the rail's padding stays a fixed band at both ends
   rather than scrolling off with the last card.
   The side inset is the list's own, not the rail's: a scrollbar is laid out against the padding
   edge, so an inset scroll box puts it hard against the cards (and over them, where the platform
   draws overlay scrollbars). Full-width here leaves it in the gutter with the inset still between. */
.bxc-side .bxc-upsells__list {
  gap: var(--bxc-gap); flex: 1; min-height: 0; overflow-y: auto; overscroll-behavior: contain;
  padding-inline: var(--bxc-space);
  /* A scroll container clips at its padding box, and the card's edge is a 1px ring drawn outside
     its own box — without this the first card's top edge is sheared off. The negative margin hands
     the space back so the list still starts flush under the heading, which covers that strip
     anyway once the cards scroll. Same trick the carousel track uses, in one axis. */
  padding-block-start: 2px; margin-block-start: -2px;
}
/* Margin, not padding: the box has a dashed edge, so insetting its text only left that edge running
   the full width of the rail while the heading above it sat a gutter in.
   Id-scoped for the margin's sake: the placeholder is a <p>, so the theme-bleed reset
   (#bxc-drawer p, a 1-0-1) beats a class-level margin and silently drops the inset. */
#bxc-drawer .bxc-side .bxc-upsells__placeholder {
  margin-inline: var(--bxc-space); margin-block-end: var(--bxc-space);
}
/* The rail's header band, and the same cast .bxc-header makes over the cart body: it eases in over
   the first 24px, so depth appears exactly when a card passes under the heading. No mask on the
   list to go with it — over a 220px rail a fade that wide washes the top card out and reads as
   disabled rather than as more above.
   The clip keeps the blur out of the rail's top edge, which the cart's header never needed: there
   is panel above that one, and only the shelf's own surface above this one. */
.bxc-side .bxc-upsells__title {
  padding: var(--bxc-gap) var(--bxc-space);
  position: relative; z-index: 1;
  background: var(--bxc-upsells-bg, var(--bxc-bg-subdued));
  box-shadow: var(--bxc-side-cast-down);
  clip-path: inset(0 0 -12px 0);
}
/* The end inset rides with the last card instead of sitting on the scroll box: a flex column drops
   its own end padding from the scrollable area in some engines, and there the last card would end
   flush against the rail's edge. */
.bxc-side .bxc-upsells__list > :last-child { margin-block-end: var(--bxc-space); }
.bxc-side:empty { display: none; }
/* A cleared list keeps its live region, so :empty alone no longer covers the no-cards state. The
   preview placeholder counts as content: it is the editor's only "select products" guidance for
   the rail, and hiding the rail hides the instruction to fill it. */
.bxc-side:not(:has(.bxc-upsell)):not(:has(.bxc-upsells__placeholder)) { display: none; }
/* The rail arrives as its own beat, uncovered by the cart shifting inward rather than sliding over
   the page. Both motions share the panel's duration, so the rail's inner edge tracks the cart edge.
   Restarts whenever the rail goes from no cards to cards, which covers the async case: the first
   recommendation fetch usually resolves after the drawer has finished opening. */
/* Scoped to the live rail inside the region: the outgoing copy below is a sibling of the region,
   and it owns its own motion rather than inheriting the arrival beat. */
/* Only when the rail hangs off the dock side's opposite edge. On the matching side the panel
   itself shifts inward, and the region rides on the panel — so the rail's own slide runs against a
   moving box, the two eased motions never cancel cleanly, and the rail skids. There the shift is
   the whole reveal: the pair travels rigidly and the rail enters from the viewport edge. */
#bxc-drawer[data-open="true"] .bxc-panel[data-side="left"]:not([data-position="left"]) .bxc-region--side .bxc-side { animation: bxc-rail-left .28s ease .08s both; }
#bxc-drawer[data-open="true"] .bxc-panel[data-side="right"]:not([data-position="right"]) .bxc-region--side .bxc-side { animation: bxc-rail-right .28s ease .08s both; }
@keyframes bxc-rail-left { from { transform: translateX(100%); } to { transform: translateX(0); } }
@keyframes bxc-rail-right { from { transform: translateX(-100%); } to { transform: translateX(0); } }
/* There is no arrival to narrate in the editor: the drawer is pinned open, and every settings edit
   repaints the rail, so the beat replayed on each keystroke. */
#bxc-drawer[data-preview="true"][data-open="true"] .bxc-panel[data-side] .bxc-region--side .bxc-side { animation: none; }
/* Except on the one edit that is an arrival: the shell stamps this for a single run when the rail
   is switched on, changes sides, or comes back with the cart. The region is a column of the box, so
   it opens by width on either side with its cards held against the cart, and the box grows with it:
   no delay, nothing else moves. */
#bxc-drawer[data-preview="true"][data-open="true"][data-bxc-side-enter] .bxc-region--side { animation: bxc-rail-open .28s ease both; }
#bxc-drawer[data-preview="true"][data-open="true"][data-bxc-side-enter] .bxc-region--side .bxc-side { position: absolute; top: 0; bottom: 0; width: 220px; }
#bxc-drawer[data-preview="true"][data-open="true"][data-bxc-side-enter] .bxc-panel[data-side="left"] .bxc-region--side .bxc-side { right: 0; }
#bxc-drawer[data-preview="true"][data-open="true"][data-bxc-side-enter] .bxc-panel[data-side="right"] .bxc-region--side .bxc-side { left: 0; }
@keyframes bxc-rail-open { from { width: 0; } to { width: 220px; } }

/* Departure, editor only. The module is gone the instant the setting flips, so the shell parks a
   copy of the rail in the region, held at full width against the cart, and the region closes
   around it: the cart slides over the cards and the box shrinks with it, the mirror of the arrival. */
.bxc-side-exit { position: absolute; top: 0; bottom: 0; width: 220px; overflow: hidden; pointer-events: none; }
.bxc-panel[data-side="left"] .bxc-side-exit { right: 0; }
.bxc-panel[data-side="right"] .bxc-side-exit { left: 0; }
.bxc-region--side:has(.bxc-side-exit) { animation: bxc-rail-close .28s ease both; }
@keyframes bxc-rail-close { from { width: 220px; } to { width: 0; } }
/* [data-side][data-position] rather than nothing: the storefront arrival carries a :not(), so its
   override matches that weight with the second attribute and wins on order. The shell also skips
   the exit copy entirely under reduced motion, so nothing waits on an animationend that never fires. */
@media (prefers-reduced-motion: reduce) {
  #bxc-drawer[data-open="true"] .bxc-panel[data-side][data-position] .bxc-region--side .bxc-side { animation: none; }
  #bxc-drawer[data-preview="true"][data-open="true"][data-bxc-side-enter] .bxc-region--side { animation: none; }
  #bxc-drawer .bxc-region--side:has(.bxc-side-exit) { animation: none; }
}

/* upsell toggle add-on row */
/* Its host sits among the footer's rows, and .bxc-footer is a gapped flex column — an empty host
   still counts as an item, so a disabled toggle would leave a gap where nothing renders. */
.bxc-toggle-host:empty { display: none; }
/* The footer's own module box already pays the gutter and the column gap, so the section padding
   this host would otherwise take insets the row past the subtotal and checkout button it sits with.
   Id-scoped to outrank that padding rule rather than lose to it. */
#bxc-drawer .bxc-footer > [data-bxc-module="upsellToggle"] { padding: 0; }
/* Tighter than the module gutter: the thumbnail labels the copy beside it, so it has to read as
   part of the same row rather than a separate column. */
.bxc-toggle { display: flex; align-items: center; gap: 8px; cursor: pointer; }
/* A switch, not a checkbox: the control is the row's trailing affordance, so it reads as an opt-in
   rather than a form field. Still a real checkbox underneath — it is stretched over the switch at
   zero opacity, so the hit target, keyboard focus and :checked all stay native. The visual lives on
   a sibling because browsers do not render ::before/::after on an <input> at all, so painting the
   thumb on the input itself draws nothing. */
.bxc-toggle__switch { position: relative; flex: none; width: 36px; height: 20px; }
#bxc-drawer .bxc-toggle__input {
  position: absolute; inset: 0; width: 100%; height: 100%;
  margin: 0; opacity: 0; cursor: pointer;
}
.bxc-toggle__track {
  position: absolute; inset: 0; border-radius: 999px; pointer-events: none;
  /* Not --bxc-border: the thumb is --bxc-bg, and on the border token the two are a shade apart, so
     the off state reads as a flat pill with no thumb in it. Derived from the text colour so it
     inverts with the palette instead of going invisible on a dark one. */
  background: color-mix(in srgb, var(--bxc-footer-ink, var(--bxc-text)) 22%, transparent);
  transition: background 120ms ease;
}
.bxc-toggle__track::after {
  content: ''; position: absolute; top: 2px; left: 2px;
  width: 16px; height: 16px; border-radius: 50%;
  background: var(--bxc-footer-surface, var(--bxc-bg)); box-shadow: 0 1px 2px rgba(0,0,0,0.25);
  transition: transform 120ms ease;
}
#bxc-drawer .bxc-toggle__input:checked ~ .bxc-toggle__track { background: var(--bxc-accent); }
#bxc-drawer .bxc-toggle__input:checked ~ .bxc-toggle__track::after { transform: translateX(16px); }
/* The input carries the focus but paints nothing, so the ring has to be drawn on the track. */
#bxc-drawer .bxc-toggle__input:focus-visible ~ .bxc-toggle__track { outline: 2px solid var(--bxc-accent); outline-offset: 2px; }
@media (prefers-reduced-motion: reduce) {
  .bxc-toggle__track, .bxc-toggle__track::after { transition: none; }
}
/* Stretches to the text block's height so image and copy read as one row; the merchant's image
   size is the ceiling, and the slider minimum (24px) floors a title-only row. The shared crop
   applies here too: max-height stays the ceiling, so portrait narrows the image instead of
   growing the row. The width floor is 3/4 of the height floor, not equal to it: min/max are
   applied independently of aspect-ratio, so an equal floor squares the image off at the bottom
   of the size range (24-31px) exactly where portrait was asked for. */
.bxc-toggle__img { display: block; flex: none; align-self: stretch; width: auto; height: auto; aspect-ratio: var(--bxc-img-ratio, 1 / 1); min-width: 18px; min-height: 24px; max-width: var(--bxc-toggle-img, 56px); max-height: var(--bxc-toggle-img, 56px); object-fit: cover; border-radius: var(--bxc-radius-sm); background: var(--bxc-border); }
.bxc-toggle__info { display: flex; flex-direction: column; gap: 2px; flex: 1; min-width: 0; }
.bxc-toggle__title { font-size: var(--bxc-fs-13); font-weight: 600; }
.bxc-toggle__desc { font-size: var(--bxc-fs-12); color: var(--bxc-muted); }
.bxc-toggle__price { font-size: var(--bxc-fs-13); font-weight: 600; white-space: nowrap; }
.bxc-toggle__price--was { color: var(--bxc-muted); font-weight: 400; text-decoration: line-through; }
.bxc-toggle__error { color: var(--bxc-critical); font-size: var(--bxc-fs-12); margin-top: 4px; }

/* footer */
/* Tighter than the panel's rhythm on both axes: the footer is pinned, so every pixel it takes
   comes off the list. The padding has to match the section-box rule's weight to replace it. */
/* The footer hands its pair to the rows inside it through --bxc-footer-ink and -surface: the note
   and discount sheets reuse those rows on the drawer background. */
.bxc-footer {
  display: flex; flex-direction: column; gap: 8px;
  background: var(--bxc-footer-bg, transparent); color: var(--bxc-footer-text, inherit);
  --bxc-footer-ink: var(--bxc-footer-text, var(--bxc-text));
  --bxc-footer-surface: var(--bxc-footer-bg, var(--bxc-bg));
}
.bxc-panel .bxc-footer { padding-block: 12px; }
.bxc-footer__row { display: flex; align-items: center; justify-content: space-between; font-size: var(--bxc-fs-14); }
.bxc-footer__subtotal { font-weight: 600; }
.bxc-footer__subtotal-prices { display: flex; align-items: baseline; gap: 8px; }
.bxc-footer__subtotal-was { color: var(--bxc-muted); text-decoration: line-through; }
.bxc-discount { display: flex; gap: 8px; }
/* Off the primary-button scale: at .bxc-btn's own padding this stretches the input beside it and
   the row outgrows the checkout button, putting the footer's largest control on a utility field. */
#bxc-drawer .bxc-discount__apply { padding: 9px 14px; font-size: var(--bxc-fs-14); }
/* Filled rather than outlined: against a panel the same colour, a bare border reads as a container
   instead of something to type in. currentColor-based, so it follows a themed drawer. */
/* Id-scoped whole, base rule included, like the icon buttons above: a theme's input[type="text"] or
   .field__input carries an attribute or an extra class and beats a single class of ours. The two
   fields stack in the footer, so one of them silently taking the theme's radius, tracking or
   placeholder tint is exactly the mismatch this scope prevents. */
#bxc-drawer .bxc-discount__input, #bxc-drawer .bxc-notes__input {
  flex: 1; padding: 10px 12px; border: 1px solid var(--bxc-border); border-radius: var(--bxc-radius-input);
  font: inherit; font-size: var(--bxc-fs-13); background: var(--bxc-input-bg); color: var(--bxc-footer-ink, var(--bxc-text));
  transition: background-color .15s ease, border-color .15s ease;
}
#bxc-drawer .bxc-discount__input::placeholder,
#bxc-drawer .bxc-notes__input::placeholder { color: var(--bxc-muted); opacity: 1; }
/* Only the multi-line note: without a height of our own the theme's rows count decides, and free
   resize lets a shopper drag the field wider than the panel it sits in. */
#bxc-drawer textarea.bxc-notes__input { min-height: 72px; resize: vertical; }
#bxc-drawer .bxc-discount__input:hover:not(:focus),
#bxc-drawer .bxc-notes__input:hover:not(:focus) { background: var(--bxc-input-bg-hover); }
.bxc-discounts { display: flex; flex-direction: column; gap: 8px; }
/* A one-line field and the button that empties it. The wrapper takes the field's place in its row
   or column; the button sits over the field's end, where the padding keeps the text clear of it. */
.bxc-clearable { position: relative; display: flex; flex: 1; min-width: 0; }
#bxc-drawer .bxc-clearable > input { min-width: 0; width: 100%; padding-inline-end: 36px; }
#bxc-drawer .bxc-clearable__btn {
  appearance: none; border: 0; background: transparent; padding: 0; margin: 0; cursor: pointer;
  position: absolute; top: 0; bottom: 0; inset-inline-end: 0; width: 36px; min-width: 0; min-height: 0;
  display: flex; align-items: center; justify-content: center;
  line-height: 1; color: var(--bxc-muted);
}
#bxc-drawer .bxc-clearable__btn[hidden] { display: none; }
#bxc-drawer .bxc-clearable__btn:hover { color: var(--bxc-footer-ink, var(--bxc-text)); }
.bxc-discount__error { font-size: var(--bxc-fs-12); color: var(--bxc-critical); }
/* A summary line rather than part of the entry control above it, so it sits on the footer's own
   rhythm with the subtotal it precedes, not on the 8px that ties a field to its toggle. */
.bxc-discounts__row {
  display: flex; align-items: center; flex-wrap: wrap; gap: 8px; font-size: var(--bxc-fs-14);
  margin-top: calc(var(--bxc-gap) - 8px);
}
.bxc-discounts__badges { display: flex; flex-wrap: wrap; gap: 6px; min-width: 0; }
.bxc-discounts__badge {
  display: inline-flex; align-items: center; gap: 4px;
  padding: 2px 8px; border-radius: 999px;
  background: var(--bxc-wash); color: var(--bxc-footer-ink, var(--bxc-text)); font-size: var(--bxc-fs-12);
}
#bxc-drawer .bxc-discounts__remove {
  appearance: none; border: 0; background: transparent; padding: 0; cursor: pointer;
  width: 14px; height: 14px; flex: 0 0 auto;
  display: inline-flex; align-items: center; justify-content: center;
  line-height: 1; color: var(--bxc-muted);
}
#bxc-drawer .bxc-discounts__remove:hover:not(:disabled) { color: var(--bxc-footer-ink, var(--bxc-text)); }
#bxc-drawer .bxc-discounts__remove:disabled { cursor: progress; opacity: .6; }
/* Pushed right so the amount lines up with the subtotal below it. */
.bxc-discounts__amount { margin-left: auto; color: var(--bxc-sale); }
/* The two buttons stack, so they share one size: a mismatched pair reads as a bug. The height has
   to zero .bxc-btn's own vertical padding to govern — with it, the box never measures under ~50px
   and the lower half of the merchant's range does nothing. min-height rather than height, so a
   label that wraps grows the button instead of spilling out of it. */
.bxc-checkout, .bxc-secondary {
  width: 100%; font-size: var(--bxc-checkout-font, 16px);
  padding-block: 0; min-height: var(--bxc-checkout-h, 48px);
}
/* Only the height, and only where a <button> carries it: the theme-bleed reset sets
   min-height:auto on every button at (1,0,1), so at class weight the height is silently dropped and
   a button with no vertical padding left collapses to its text. The secondary action renders as an
   <a> for the link actions, which the reset never touches. Everything else above stays at class
   weight so the merchant's own CSS, appended after this sheet, can still override it. */
#bxc-drawer button.bxc-checkout,
#bxc-drawer button.bxc-secondary { min-height: var(--bxc-checkout-h, 48px); }
/* Class weight, after .bxc-btn: the block key wins by order, and an absent key is .bxc-btn's pair. */
.bxc-checkout { background: var(--bxc-checkout-bg, var(--bxc-accent)); color: var(--bxc-checkout-text, var(--bxc-accent-text)); }
.bxc-checkout-hint { font-size: var(--bxc-fs-12); color: var(--bxc-muted); text-align: center; }
/* Rendered as an <a> for the link action, so the button box has to be reasserted. */
.bxc-secondary { display: flex; align-items: center; justify-content: center; text-decoration: none; }
/* The link look: a 20px text row with the accordion toggle's 28px target, 4px into the gap on each
   side. Two classes plus the id, to outrank the button height rule above and the reset's margin
   and underline rules. */
#bxc-drawer .bxc-secondary.bxc-secondary--link {
  appearance: none; border: 0; background: transparent; cursor: pointer;
  width: auto; align-self: center; min-height: 0;
  padding: 4px 0; margin-block: -4px;
  font: inherit; font-size: var(--bxc-fs-14); color: var(--bxc-footer-ink, var(--bxc-text)); text-align: center;
  text-decoration: underline; text-underline-offset: 2px;
}
#bxc-drawer .bxc-secondary.bxc-secondary--link:hover { color: var(--bxc-muted); }
.bxc-notes { display: flex; flex-direction: column; gap: 8px; }
/* Full width so the label anchors left and the chevron anchors right: both collapsible sections
   share one scan line, and the whole row is the target rather than the label alone.
   14px text at 1.4 is a 20px box, under the 24px WCAG 2.2 target floor, and these are buttons
   rather than inline links so the exception does not apply. The padding buys the height and the
   negative margin hands the space back, so the footer's rhythm is unchanged. */
/* Id-scoped for the margin's sake: the theme-bleed reset at the top of this file zeroes every
   button margin at (1,0,1), so at class weight the compensation is silently dropped and the
   padding grows the footer instead. */
#bxc-drawer .bxc-accordion__toggle {
  appearance: none; border: 0; background: transparent; cursor: pointer;
  width: 100%; display: flex; align-items: center; gap: 8px;
  padding: 4px 0; margin-block: -4px;
  font: inherit; font-size: var(--bxc-fs-14); color: var(--bxc-footer-ink, var(--bxc-text)); text-decoration: none; text-align: left;
}
/* Only the label underlines: the chevron is a separate flex item with no text of its own, so the
   rule stops where the words do rather than running the width of the row. */
#bxc-drawer .bxc-accordion__toggle:hover { text-decoration: underline; }
/* No underline on hover: the chevron already carries the affordance, and across a full-width row
   the underline would run beneath the chevron and read as a rule rather than a link. */
/* The chevron glyph spans only 8x4 units of its 20-unit canvas, so the canvas is drawn
   oversized (24px) and cropped by the box to render the glyph itself at ~9.5px. */
.bxc-accordion__toggle::after {
  content: ''; margin-left: auto; width: 16px; height: 16px; flex: 0 0 auto;
  background: var(--bxc-chevron-lg) center / 24px no-repeat;
  transition: transform .15s ease;
}
.bxc-accordion__toggle[aria-expanded="true"]::after { transform: rotate(180deg); }
/* A row that opens a sheet unfolds nothing, so it ends in a plus; a note row whose cart holds a
   note ends in a tick. Same 16px box as the chevron, so the row's height and target do not move. */
.bxc-accordion__toggle--sheet::after { background: var(--bxc-plus) center / 16px no-repeat; transition: none; }
.bxc-accordion__toggle--sheet.bxc-accordion__toggle--filled::after { background-image: var(--bxc-tick); }
/* What a note or code row hands the sheet: the field at full width with its button or its error
   under it. The note field is a flex item of a row in the footer and of a column here. */
.bxc-field-sheet { display: flex; flex-direction: column; gap: 12px; }
#bxc-drawer .bxc-field-sheet > .bxc-notes__input,
#bxc-drawer .bxc-field-sheet > .bxc-clearable { flex: 0 0 auto; width: 100%; }
#bxc-drawer .bxc-field-sheet > textarea.bxc-notes__input { min-height: 96px; }
.bxc-field-sheet__done { width: 100%; }
/* In the sheet Apply is the one action, so it takes the primary button's size back from the
   utility scale it has beside the field in the footer. */
#bxc-drawer .bxc-field-sheet > .bxc-discount__apply { width: 100%; padding: 14px 20px; font-size: inherit; }
.bxc-discount__body { display: flex; flex-direction: column; gap: 8px; }
/* Same type as the toggle it replaces when the field cannot collapse, minus the link affordance. */
.bxc-notes__label { font-size: var(--bxc-fs-14); color: var(--bxc-footer-ink, var(--bxc-text)); }
.bxc-notes__body { display: flex; }
/* Brightens out of the wash on focus, so the field being typed into is the lightest thing in the
   footer and never sits at the same fill as a discount chip. */
#bxc-drawer .bxc-discount__input:focus,
#bxc-drawer .bxc-notes__input:focus { border-color: var(--bxc-accent); background: var(--bxc-footer-surface, var(--bxc-bg)); outline: none; }
/* Centered like the dashboard's own checkbox row; the label is one short line. */
.bxc-terms { display: flex; align-items: center; gap: 8px; font-size: var(--bxc-fs-13); cursor: pointer; }
/* Fully owned appearance: themes style bare checkboxes aggressively, and our content:none reset
   erases their pseudo-element checkmarks — drawing our own is the only theme-proof rendering. */
#bxc-drawer .bxc-terms__checkbox {
  appearance: none; width: 16px; height: 16px; flex: 0 0 auto;
  margin: 0; display: grid; place-content: center;
  background: var(--bxc-footer-surface, var(--bxc-bg)); border: 1px solid var(--bxc-muted); border-radius: 4px;
  cursor: pointer; transition: background-color .15s ease, border-color .15s ease;
}
/* input-prefixed to (1,1,2): the checkbox reset above carries the same weight through its type
   selector, and at a class-only (1,1,1) its content:none erases this checkmark too. */
#bxc-drawer input.bxc-terms__checkbox::before {
  content: ''; width: 10px; height: 8px;
  /* Intrinsic size + crop, not contain: the glyph sits in the middle of a 16x20 canvas, and
     shrinking the whole canvas into this box renders the check illegibly small. */
  background: var(--bxc-checkmark) center no-repeat;
  transform: scale(0); transform-origin: center;
  transition: transform .12s ease;
}
#bxc-drawer .bxc-terms__checkbox:hover { border-color: var(--bxc-footer-ink, var(--bxc-text)); }
#bxc-drawer .bxc-terms__checkbox:checked { background: var(--bxc-accent); border-color: var(--bxc-accent); }
#bxc-drawer input.bxc-terms__checkbox:checked::before { transform: scale(1); }
#bxc-drawer .bxc-terms__checkbox:focus { outline: none; }
#bxc-drawer .bxc-terms__checkbox:focus-visible { outline: 2px solid var(--bxc-accent); outline-offset: 1px; }
/* Gap derived from the icon height, so a row the merchant scaled up does not crowd. */
.bxc-payment-icons {
  display: flex; justify-content: center; flex-wrap: wrap; color: var(--bxc-muted);
  gap: calc(var(--bxc-payment-icon-h, 24px) * 0.25);
}
/* An uploaded icon can be any aspect ratio; the cap keeps a wide one from eating the row, and it
   scales with the height so it never crops what the branded set renders in full. */
.bxc-payment-icon--custom { object-fit: contain; border-radius: 3px; max-width: calc(var(--bxc-payment-icon-h, 24px) * 2.5); }
/* The branded SVGs carry width/height attributes, so both axes have to be set for the merchant's
   height to take effect at the icon's own aspect ratio. */
.bxc-payment-icon { display: block; height: var(--bxc-payment-icon-h, 24px); width: auto; }
.bxc-accelerated {
  display: flex; flex-direction: column; gap: var(--bxc-gap);
  /* Shopify's documented component API: wallet rows share the footer rhythm, wallet corners
     follow the merchant's radius knob like every other control. */
  --shopify-accelerated-checkout-row-gap: var(--bxc-gap);
  --shopify-accelerated-checkout-button-border-radius: var(--bxc-radius);
}
#bxc-drawer shopify-accelerated-checkout, #bxc-drawer shopify-accelerated-checkout-cart { --shopify-accelerated-checkout-row-gap: var(--bxc-gap); --shopify-accelerated-checkout-button-border-radius: var(--bxc-radius); }
/* Legacy (pre-component) wallet markup ships without the theme CSS that laid it out, so the
   lifted buttons stack as bare block divs; two-up with a floor keeps them tappable. */
.bxc-accelerated [data-shopify-buttoncontainer] { display: flex; flex-wrap: wrap; gap: 8px; }
.bxc-accelerated [data-shopify-buttonwrapper] { flex: 1 1 calc(50% - 4px); min-width: 140px; }
.bxc-accelerated--preview { pointer-events: none; flex-direction: row; flex-wrap: wrap; }
.bxc-accelerated-mock {
  display: flex; align-items: center; justify-content: center;
  flex: 1 1 calc(50% - 4px); min-width: 140px;
  min-height: var(--shopify-accelerated-checkout-button-block-size, 42px);
  border-radius: var(--bxc-radius); color: #fff; font-weight: 600; font-size: var(--bxc-fs-15);
}
/* The wordmark viewBox carries the badge's own padding, so near-button-height still renders the
   mark itself at roughly the size real wallet buttons draw theirs. */
.bxc-accelerated-wordmark {
  height: calc(var(--shopify-accelerated-checkout-button-block-size, 42px) * 0.85);
  width: auto;
}
/* Brand colours, deliberately not palette tokens: the mock stands in for wallet buttons the
   merchant cannot restyle, so it must not follow the merchant's palette either. */
.bxc-accelerated-mock--shoppay { background: #5a31f4; }
.bxc-accelerated-mock--paypal { background: #ffc439; }
/* No background of its own: the module box beneath it paints the offer surface, and a second
   opaque layer here would cover it. */
.bxc-trust {
  display: flex; gap: var(--bxc-gap); justify-content: center; flex-wrap: wrap;
  color: var(--bxc-trust-text, var(--bxc-muted)); font-size: var(--bxc-fs-12);
}
.bxc-trust__badge { display: inline-flex; align-items: center; gap: 6px; }

/* Offers sit on the secondary surface and the shopper's own items on the primary one — the same
   split the side rail already makes, now said in the cart body too. White cards lift off it exactly
   as the rail's do. Trust badges join them: they are also not the shopper's items, and leaving them
   white would stripe the pinned-to-bottom group. The add-on toggle stays primary — it lives in the
   footer and reads as a checkout option rather than an offer. */
.bxc-region--body > [data-bxc-module="upsells"] {
  background: var(--bxc-upsells-bg, var(--bxc-bg-subdued)); color: var(--bxc-upsells-text, inherit);
}
.bxc-region--body > [data-bxc-module="trustBadges"] {
  background: var(--bxc-trust-bg, var(--bxc-bg-subdued));
}
/* The empty cart's box takes the drawer text only while the empty cart has a Text of its own, so that
   Text stops at the upsell block; with neither key the declaration is invalid and the box inherits. */
.bxc-empty__upsells {
  background: var(--bxc-upsells-bg, var(--bxc-bg-subdued));
  color: var(--bxc-upsells-text, color-mix(in srgb, var(--bxc-empty-text) 0%, var(--bxc-text)));
}

/* Offers are cards: upsell rows, upsell tiles and the rail's tiles take one surface treatment — the
   same layered stack the dashboard's own cards use, whose first layer is a 1px ring, so no border
   rides along with it (that would draw the card's edge twice). Explicit primary background: inside
   the panel it matches the backdrop, but on the rail's subdued surface it is what makes a card read
   as raised rather than cut out. The add-on toggle is deliberately not in this list: it renders flat
   so it lines up with the footer rows it sits among. */
.bxc-upsell {
  background: var(--bxc-bg); color: var(--bxc-upsell-card-text);
  border-radius: var(--bxc-radius-lg); padding: var(--bxc-gap);
  box-shadow: var(--bxc-shadow-card);
}
/* The carousel scrolls, and a scroll container clips its children's shadows at the padding box —
   overflow-x:auto forces overflow-y to auto as well, so an unpadded track shears the cast off the
   top and bottom of every card. The padding buys the shadow room inside the scrollport and the
   negative margin hands the space back, so the track's outer geometry is unchanged.
   The vertical lists need none of this: the module gutter already clears their casts. */
/* Id-scoped for the margin's sake: the track is a <ul>, so the theme-bleed reset (#bxc-drawer ul,
   a 1-0-1) beats a class-level margin and silently drops the compensation — leaving the padding to
   inset every card by 8px from the gutter the title and the cart lines sit on. */
#bxc-drawer .bxc-upsells__list--carousel {
  padding: 8px; margin: -8px;
  /* scroll-padding, not just padding: mandatory snap aligns the first card's edge to the snapport
     start, which without this is the padding edge — so it scrolls the left 8px out of view at rest
     and shears that one card's shadow. Nothing looks wrong until you scroll back. */
  scroll-padding: 8px;
}

/* Full-bleed is the one thing here that really is about viewport width. */
@media (max-width: 480px) {
  .bxc-panel { --bxc-width: 100vw; }
}

/* Everything below follows the pointer, not the width: a phone held sideways and every tablet are
   touch devices well past 480px, and were dropping back to mouse-sized controls and zoom-triggering
   form text. The rules stay one block because they are interlocked — the form-control size sets the
   type scale, which sets the rhythm. */
@media (max-width: 480px), (pointer: coarse) {
  /* 16px is not a style choice: iOS Safari zooms the whole page when a focused form control is
     any smaller, and the shopper is left having to pinch back out. */
  #bxc-drawer .bxc-line__variant-select,
  #bxc-drawer .bxc-discount__input,
  #bxc-drawer .bxc-notes__input {
    font-size: 16px;
  }
  /* 36px keeps a comfortable thumb target without the stepper taking two thirds of the text
     column. Not smaller: a mis-tap here changes what the shopper pays. */
  #bxc-drawer .bxc-stepper__btn { width: 36px; height: 36px; font-size: var(--bxc-fs-16); }
  #bxc-drawer .bxc-stepper__val { width: 40px; }
  #bxc-drawer .bxc-line__remove { padding: 12px; }
  #bxc-drawer .bxc-line__qty > .bxc-line__remove:first-child { margin-left: -12px; }
  /* Same ~10px boundary as desktop, against this block's larger 12px hit padding. */
  .bxc-line__qty:not(:has(.bxc-stepper)) { margin-top: -6px; }
  /* Clears the WCAG floor at 28px already, so this is thumb comfort rather than compliance. The
     swipe is the primary affordance on touch and the arrows sit beside the heading, which caps how
     much they can grow before they outweigh it. */
  #bxc-drawer .bxc-upsells__nav-btn { width: 36px; height: 36px; }
  /* The desktop box is a mouse size, and Add is the one control on an upsell card a thumb has to
     hit. It keeps the larger box here rather than following the tightened desktop one. */
  #bxc-drawer .bxc-upsell__add { padding: 9px 14px; font-size: var(--bxc-fs-13); }
  /* The select carries no border, so its own padding is what gives it a tappable height — and the
     negative margin keeps that padding from also reading as a gap, as it does on the trash. */
  #bxc-drawer .bxc-line__variant-select { padding-top: 4px; padding-bottom: 4px; margin-block: -4px; }
  /* Rhythm scales with the controls: 42px-tall controls against 4px rows read as cramped text
     wrapped around oversized buttons. Less than desktop's ratio suggests, because the rule between
     lines separates at any size — the band only needs to clear the taller controls. */
  #bxc-drawer { --bxc-line-gap: 20px; }
  /* 16px text at 1.4 carries 3.2px of half-leading each side, so an 8px gap rendered as ~14px.
     Tightening the leading is what actually controls the rhythm here; the gap then reads true. */
  .bxc-line__info { gap: 4px; line-height: 1.25; }
  #bxc-drawer .bxc-line__variant-select { line-height: 1.25; }
  /* The 16px forced on form controls would otherwise outrank the title, inverting the hierarchy,
     so the line's own scale steps up with it: 16 primary, 13 secondary. */
  .bxc-line__title { font-size: var(--bxc-fs-16); }
  .bxc-line__prices { font-size: var(--bxc-fs-16); }
  .bxc-line__variant,
  .bxc-line__discount,
  .bxc-line__plan,
  .bxc-line__prop,
  #bxc-drawer .bxc-line__components-toggle,
  .bxc-component,
  .bxc-line__error,
  .bxc-line__price--was,
  .bxc-line__per-unit,
  .bxc-line__save,
  .bxc-line__qtyval { font-size: var(--bxc-fs-13); }
}


/* Gift claim row, under the reward track: one button that opens the gift sheet. Id-scoped so the
   theme-bleed reset on buttons does not take its box. */
.bxc-gift-claim { text-align: left; }
#bxc-drawer .bxc-gift-claim__btn {
  appearance: none; cursor: pointer; width: 100%; min-height: 36px;
  display: flex; align-items: center; justify-content: space-between; gap: 8px;
  padding: 6px 12px; border: 1px solid var(--bxc-border); border-radius: var(--bxc-radius-sm);
  background: var(--bxc-bg); color: var(--bxc-text);
  font: inherit; font-size: var(--bxc-fs-13); font-weight: 600; line-height: 1.4; text-align: start;
}
.bxc-gift-claim__label { min-width: 0; overflow-wrap: anywhere; }
.bxc-gift-claim__side { display: flex; align-items: center; gap: 6px; flex: 0 0 auto; }
.bxc-gift-claim__count {
  min-width: 20px; padding: 2px 7px; border-radius: 999px; text-align: center;
  font-size: var(--bxc-fs-12); line-height: 1.35; background: var(--bxc-accent); color: var(--bxc-accent-text);
}
.bxc-gift-claim__chevron { display: flex; line-height: 0; }
#bxc-drawer[data-dir="rtl"] .bxc-gift-claim__chevron { transform: scaleX(-1); }

/* Gift sheet: a group per tier, each the tier's name over its gifts, the upsell card (.bxc-upsell)
   made compact, one per gift, stacked at full width. */
.bxc-gift-sheet { display: flex; flex-direction: column; gap: 16px; text-align: left; }
.bxc-gift-strip { display: flex; flex-direction: column; gap: 8px; }
.bxc-gift-strip__items { display: flex; flex-direction: column; gap: 8px; }
#bxc-drawer .bxc-gift-strip__item {
  grid-template-columns: 40px minmax(0, 1fr) auto; gap: 8px; padding: 8px;
}
.bxc-gift-strip__media { position: relative; }
#bxc-drawer .bxc-gift-strip__item .bxc-upsell__img { width: 40px; }
.bxc-gift-strip__badge {
  position: absolute; top: -5px; right: -5px; display: grid; place-items: center;
  min-width: 16px; height: 16px; padding: 0 4px; border-radius: 999px; font-size: var(--bxc-fs-10); font-weight: 600;
  line-height: 1; background: var(--bxc-accent); color: var(--bxc-accent-text); box-shadow: 0 0 0 2px var(--bxc-bg);
}
#bxc-drawer .bxc-gift-strip__name { -webkit-line-clamp: 1; }
.bxc-gift-strip__meta { display: flex; align-items: baseline; gap: 6px; min-width: 0; }
.bxc-gift-strip__selects { display: flex; flex: 0 1 auto; flex-direction: column; gap: 4px; min-width: 0; max-width: 60%; }
#bxc-drawer .bxc-gift-strip__selects .bxc-upsell__variant { min-width: 0; max-width: 100%; }
.bxc-gift-strip__meta .bxc-upsell__prices { flex: 0 0 auto; }
.bxc-gift-strip__msg { margin: 0; font-size: var(--bxc-fs-12); color: var(--bxc-critical); }

/* Sticky cart button. Id-scoped throughout: the theme-bleed button reset above is a 1-0-1. It sits
   one layer under the overlay, and a bottom bar lifts it by --bxc-sticky-lift. */
#bxc-drawer .bxc-sticky {
  position: fixed; z-index: calc(var(--bxc-z) - 1);
  bottom: calc(20px + env(safe-area-inset-bottom, 0px) + var(--bxc-sticky-lift, 0px));
  width: 56px; height: 56px; padding: 0; border: 0; border-radius: 50%;
  display: flex; align-items: center; justify-content: center;
  background: var(--bxc-sticky-bg, var(--bxc-accent)); color: var(--bxc-sticky-text, var(--bxc-accent-text));
  box-shadow: var(--bxc-shadow-card); cursor: pointer;
  transform: translate(var(--bxc-sticky-dx, 0px), var(--bxc-sticky-dy, 0px));
  transition: opacity .15s ease, visibility .15s ease;
}
#bxc-drawer .bxc-sticky[data-corner="bottom-right"] { right: calc(20px + env(safe-area-inset-right, 0px)); }
#bxc-drawer .bxc-sticky[data-corner="bottom-left"] { left: calc(20px + env(safe-area-inset-left, 0px)); }
#bxc-drawer .bxc-sticky[data-draggable] { touch-action: none; user-select: none; -webkit-user-select: none; }
#bxc-drawer .bxc-sticky__icon { display: block; pointer-events: none; }
#bxc-drawer .bxc-sticky__count {
  position: absolute; top: -4px; right: -4px;
  min-width: 22px; height: 22px; padding: 0 5px; border-radius: 11px;
  border: 1.5px solid var(--bxc-sticky-bg, var(--bxc-accent));
  background: var(--bxc-sticky-text, var(--bxc-accent-text)); color: var(--bxc-sticky-bg, var(--bxc-accent));
  font-size: var(--bxc-fs-11); font-weight: 700; line-height: calc(19px * var(--bxc-type-scale, 1)); text-align: center;
  pointer-events: none;
}
/* Unknown counts as empty: before the first cart read there is no count to show. */
#bxc-drawer:not([data-preview-surface="page"])[data-open="true"] .bxc-sticky,
#bxc-drawer:not([data-preview-surface="page"]):not([data-empty="false"]) .bxc-sticky:not([data-show-empty]) {
  opacity: 0; visibility: hidden;
}
@media (max-width: 767px) {
  #bxc-drawer:not([data-preview-surface="page"]) .bxc-sticky:not([data-mobile]) { display: none; }
}
@media (min-width: 768px) {
  #bxc-drawer:not([data-preview-surface="page"]) .bxc-sticky:not([data-desktop]) { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  #bxc-drawer .bxc-sticky { transition: none; }
}
/* Editor preview, page surface: the panel steps aside so the button shows while the drawer stays
   pinned open underneath; the editor draws the page behind it. */
#bxc-drawer[data-preview-surface="page"] .bxc-panel { display: none; }
/* Editor preview: the block whose colour row the merchant hovers wears a blue ring and wash, inside
   its box. A fixed blue, never a drawer colour: an accent ring vanishes on an accent-coloured bar. */
#bxc-drawer[data-preview="true"] [data-bxc-highlight] { box-shadow: inset 0 0 0 2px #458fff, inset 0 0 0 9999px rgba(69, 143, 255, 0.07) !important; }
