/*
 * be.Bold — woo.css
 * =================
 * The shop, single product, cart, checkout and account screens. Loaded only on those screens
 * (inc/woocommerce.php), after shell.css and after WooCommerce's own woocommerce-general.
 *
 * There is no approved design file for any of these pages — the seven in be.Bold/design/ stop at
 * the homepage's collection band. So nothing here is a port: it is the design's own vocabulary
 * (the tokens, .eyebrow / .dsp / .rule / .btn, and the .card tile now in shell.css) applied to
 * WooCommerce's markup.
 *
 * Two of WooCommerce's three stylesheets are dropped and this file replaces them:
 *   woocommerce-layout, woocommerce-smallscreen   floats and grids — replaced by CSS grid below
 *   woocommerce-general                           KEPT: form rows, notices, the quantity stepper,
 *                                                 the payment box. Re-skinned here, not rebuilt.
 *
 * Rule of thumb when adding to this file: if the selector names a WooCommerce class it belongs
 * here; if it names a be.Bold component that another page also uses, it belongs in shell.css.
 */

/* ============ page shell ============ */

/*
 * WooCommerce puts `woocommerce-js` on the body and Astra's replacement stylesheet carries
 * `.woocommerce-js h2{margin-bottom:.7em}` — one element rule that out-specifies the design's
 * `.dsp{margin:0}` on every heading. Outside the shop that stylesheet no longer loads at all
 * (inc/woocommerce.php); here it is still wanted for its form and table internals, so the one
 * rule that fights the design is matched selector-for-selector and undone.
 */
.woocommerce-js h2{margin-bottom:0}

/*
 * .hdr is sticky rather than fixed, so it occupies flow and content needs no top offset — but the
 * shop has no hero to sit under it either, so it gets a little breathing room of its own.
 */
.woo{padding-top:clamp(18px,2.4vw,34px)}

/* ============ archive header ============ */

.woo-head{padding-bottom:clamp(20px,2.4vw,32px)}
.woo-head-inner{max-width:760px;margin:0 auto;text-align:center}
.woo-head-inner .eyebrow{margin:0 0 14px}
.woo-head-desc{margin:clamp(18px,2.2vw,26px) auto 0;max-width:56ch}

/* ============ catalogue controls (off by default) ============ */

/*
 * The sorting menu and the result count. Both are switched off under
 * WooCommerce → Settings → Products, so on this build NONE of this is on the page — see
 * bebold_catalogue_option() in inc/woocommerce.php.
 *
 * ⚑ STYLED ANYWAY, AND THAT IS THE POINT. A toggle that turns on unstyled markup is not a
 * feature, it is a bug with a checkbox in front of it: the client would switch it on, see a
 * browser-default grey <select> sitting against the design, and switch it off again. The cost of
 * carrying these rules while they are unused is a few hundred bytes in a file that is already
 * loaded.
 *
 * The count sits left and the menu right. `margin-left:auto` rather than `justify-content:
 * space-between`, so the menu stays right when the count is off and the menu is the only child.
 */
.woo-toolbar{
  display:flex;align-items:center;gap:16px;flex-wrap:wrap;
  margin:0 0 clamp(18px,2.2vw,28px);
}
.woo-toolbar .woocommerce-result-count{
  margin:0;font-size:.85rem;color:var(--muted);letter-spacing:.02em;
}
.woo-toolbar .woocommerce-ordering{margin:0 0 0 auto}
.woo-toolbar .woocommerce-ordering select{
  background:var(--cream);color:var(--ink);
  border:1px solid var(--hair);border-radius:3px;
  padding:10px 13px;font-size:.85rem;line-height:1.4;height:auto;
  min-height:var(--tap);
  transition:border-color .3s,background .3s;
}
.woo-toolbar .woocommerce-ordering select:focus{
  border-color:var(--green);background:var(--sand-hi);outline:none;
}

/* Under ~480px the two stack, and the menu goes full width rather than clinging to the right. */
@media(max-width:480px){
  .woo-toolbar{gap:10px}
  .woo-toolbar .woocommerce-ordering{margin-left:0;width:100%}
  .woo-toolbar .woocommerce-ordering select{width:100%}
}

/* ============ the product grid ============ */

.woo-list{padding-top:0}

.woo-grid{
  list-style:none;margin:0;padding:0;
  display:grid;grid-template-columns:repeat(2,1fr);gap:clamp(14px,1.6vw,26px);
}

/* The tile itself is .card, in shell.css. Only the parts WooCommerce contributes are here. */
.woo-grid .card{display:flex;flex-direction:column}
.woo-grid .card-media-link{display:block}
.woo-grid .card-info{margin-top:auto}
.woo-grid .card h3 a{color:inherit}
.woo-grid .card h3 a:hover{color:var(--green-mid)}

/*
 * WooCommerce wraps every price in <span class="price"><span class="amount">…</span></span> and
 * marks a sale with a <del>/<ins> pair. The design's .card .price sets the type; these keep the
 * nested spans from adding their own.
 */
.card .price ins{text-decoration:none}
.card .price del{opacity:.55;margin-right:8px;font-size:.86em}
.card .price .amount{white-space:nowrap}

/* The loop's Add to cart is an <a> wearing .btn .add-cart, so it needs the link reset undone. */
.card .add-cart{text-decoration:none;justify-content:center}
.card .add-cart.loading{opacity:.6;pointer-events:none}

/*
 * WooCommerce swaps in its own "View cart" link beside the button after an AJAX add. The toast
 * carries that link now (site.js section 4), so the inline one is hidden and the button stays
 * where it is — the design's card has one control, and hiding it would stop you adding a second.
 */
.card .added_to_cart{display:none}

/* ============ notices ============ */

/* .woocommerce-notices-wrapper:empty is in shell.css — notices appear off the shop screens too. */

/*
 * ⚑ `display:flow-root` is the load-bearing declaration here. The button used to be FLOATED, and a
 * float does not grow its container — so on any notice whose text runs to a SINGLE line the button,
 * which is taller than one line of .86rem text, hung out through the bottom border. It looked
 * correct only on notices long enough to wrap to two lines, which is why it survived this long:
 * "Confirm your email address to check for past orders and link them to your account." wraps and
 * hides it, "No order has been made yet." does not. flow-root establishes a block formatting
 * context, which contains the float whatever its height.
 */
.woocommerce-message,
.woocommerce-info,
.woocommerce-error{
  display:flow-root;
  background:var(--sand-hi);border:1px solid var(--hair);border-top:2px solid var(--gold);
  border-radius:3px;color:var(--ink);
  font-size:.86rem;line-height:1.7;padding:16px 20px;margin:0 0 clamp(20px,2.4vw,30px);
  list-style:none;
}
.woocommerce-error{border-top-color:var(--error)}
.woocommerce-message::before,
.woocommerce-info::before,
.woocommerce-error::before{display:none}

/*
 * And with the box no longer at risk, the row can be laid out properly rather than floated: the
 * text centres against the button instead of sitting at the top of it, and on a narrow screen the
 * button drops onto its own line rather than squeezing the message.
 *
 * Applied only where there IS a button. `.woocommerce-error` is a <ul> of <li>s, and making that a
 * flex row would put its items side by side — so the flex rule is gated on the button's presence
 * rather than written for all three. If :has() is ever unavailable, flow-root above still holds
 * the float in.
 */
.woocommerce-message:has(> .button),
.woocommerce-info:has(> .button){
  display:flex;align-items:center;gap:18px;
}
/*
 * ⚑ Deliberately NOT flex-wrap:wrap. Flex breaks lines BEFORE it shrinks, so a message whose text
 * is just wide enough to fill the row sent its button onto a second line instead of re-wrapping
 * the sentence — which is what happened to the "Confirm your email address…" notice the moment
 * this became a flex row. With nowrap the text shrinks and wraps internally, the box grows, and
 * the button stays where it belongs. The narrow-screen stack is a media query further down, where
 * the decision is explicit rather than left to the layout algorithm.
 */
/*
 * WooCommerce prints the button BEFORE the message text, so `order` moves it to the end without
 * touching the markup. The text is an anonymous flex item and keeps the default order of 0.
 */
.woocommerce-message > .button,
.woocommerce-info > .button,
.woocommerce-error > .button{order:2;flex:none;margin-left:auto}

/* ============ buttons ============ */

/*
 * woocommerce-general paints every .button purple. Re-point them at the design's .btn instead of
 * overriding rule by rule — same box, same transition, same hover.
 */
.woocommerce .button,
.woocommerce button.button,
.woocommerce input.button,
.woocommerce a.button,
.woocommerce #respond input#submit{
  display:inline-flex;align-items:center;justify-content:center;gap:0;
  background:var(--btn-bg);color:var(--btn-fg);
  padding:15px 24px;font-family:'Poppins',system-ui,sans-serif;font-weight:700;
  font-size:.66rem;letter-spacing:.2em;text-transform:uppercase;line-height:1;
  border:1px solid var(--btn-bg);border-radius:6px;
  transition:background .35s,color .35s;cursor:pointer;text-decoration:none;white-space:nowrap;
}
.woocommerce .button:hover,
.woocommerce button.button:hover,
.woocommerce input.button:hover,
.woocommerce a.button:hover,
.woocommerce #respond input#submit:hover{background:transparent;color:var(--btn-hover-fg)}

.woocommerce .button:disabled,
.woocommerce .button.disabled{opacity:.4;pointer-events:none}

/* The secondary actions — "Update cart", "Apply coupon" — read as outlines, not as a second CTA. */
.woocommerce .button[name="update_cart"],
.woocommerce .coupon .button{background:transparent;color:var(--green)}
.woocommerce .button[name="update_cart"]:hover,
.woocommerce .coupon .button:hover{background:var(--green);color:var(--btn-fg)}

/* ============ forms ============ */

.woocommerce form .form-row{margin:0 0 18px;padding:0}
.woocommerce form .form-row label{
  display:block;font-size:.66rem;letter-spacing:.16em;text-transform:uppercase;
  color:var(--muted);margin:0 0 8px;
}

.woocommerce form .form-row input.input-text,
.woocommerce form .form-row textarea,
.woocommerce form .form-row select,
.woocommerce .select2-container .select2-selection--single{
  width:100%;background:var(--cream);color:var(--ink);
  border:1px solid var(--hair);border-radius:3px;
  padding:13px 15px;font-size:.9rem;line-height:1.4;height:auto;
  transition:border-color .3s,background .3s;
}
.woocommerce form .form-row input.input-text:focus,
.woocommerce form .form-row textarea:focus,
.woocommerce form .form-row select:focus{border-color:var(--green);background:var(--sand-hi);outline:none}
/* ---- invalid fields ---- */

/*
 * ⚑ Nothing styled a validation state until 9 Sep 2026. The form told the customer that something
 * was wrong — the notice at the top of the checkout names every offending field — and then gave no
 * indication of WHICH field on the field itself. On a fourteen-row billing form that is a hunt.
 *
 * ⚑ THE CLASS IS ON THE .form-row, NOT ON THE INPUT. WooCommerce's own checkout.js says so in a
 * comment ("The `.woocommerce-invalid` class lives on the `.form-row`"), and it is why measuring an
 * invalid input against a valid one shows byte-identical computed styles: the input never receives
 * a class at all. Every selector here therefore descends from the row.
 *
 * The states checkout.js sets: `woocommerce-invalid` plus one of `-invalid-required-field`,
 * `-invalid-email`, `-invalid-phone`, and `woocommerce-validated` when a field passes.
 *
 * Specificity is deliberate. `.woocommerce form .form-row input.input-text` is (3 classes,
 * 2 types) and its `:focus` variant adds a pseudo-class; adding `.woocommerce-invalid` to the row
 * makes these (4 classes, 2 types), which beats both outright rather than by file order — this
 * stylesheet has already produced five specificity ties.
 */
.woocommerce form .form-row.woocommerce-invalid input.input-text,
.woocommerce form .form-row.woocommerce-invalid textarea,
.woocommerce form .form-row.woocommerce-invalid select,
.woocommerce form .form-row.woocommerce-invalid .select2-container .select2-selection--single{
  border-color:var(--error);
  background:var(--error-tint);
}
/*
 * The error survives focus. Without this the plain :focus rule would repaint the border green the
 * moment the customer clicks into the field they are being asked to fix — which reads as "resolved"
 * before anything has been typed.
 */
.woocommerce form .form-row.woocommerce-invalid input.input-text:focus,
.woocommerce form .form-row.woocommerce-invalid textarea:focus,
.woocommerce form .form-row.woocommerce-invalid select:focus{
  border-color:var(--error);
  background:var(--sand-hi);
}
/* The label carries the colour too: on a narrow screen the label is often the only part of the row
 * on screen when the notice is read. */
.woocommerce form .form-row.woocommerce-invalid label{color:var(--error)}
.woocommerce form .form-row.woocommerce-invalid .required{color:var(--error)}

/*
 * ⚑ `woocommerce-validated` is deliberately NOT styled. checkout.js adds it to every field that
 * passes, so styling it paints most of a completed checkout green — loud on a cream page, and it
 * competes with the errors that actually need attention. Removing `woocommerce-invalid` already
 * returns a corrected field to neutral, which is the feedback that matters.
 */

.woocommerce .select2-container .select2-selection--single .select2-selection__rendered{
  line-height:1.4;padding:0;color:var(--ink);
}
.woocommerce .select2-container .select2-selection--single .select2-selection__arrow{height:100%;right:10px}
.woocommerce .select2-dropdown{border-color:var(--hair);border-radius:3px}
.woocommerce .select2-results__option--highlighted{background:var(--green)}

.woocommerce form .form-row-first,
.woocommerce form .form-row-last{width:100%;float:none}

/* The quantity stepper — one small box, not woocommerce-general's inline table cell. */
.woocommerce .quantity{display:inline-flex}
.woocommerce .quantity .qty{
  width:74px;text-align:center;background:var(--cream);color:var(--ink);
  border:1px solid var(--hair);border-radius:3px;padding:12px 6px;font-size:.9rem;
}

/* ============ single product ============ */

/*
 * The product page follows the client's references: a thumbnail rail and one large frame on the
 * left, a narrow buy panel that sticks on the right.
 *
 * The row sits inside the site's 1560px container. An earlier pass ran it FULL BLEED to the
 * window's left edge, which suited the two-up mosaic it had then — but a thumbnail rail flush
 * against the window edge has nothing to line up with, so the container came back and the story
 * band and related products went back to inheriting it from div.product.
 */
.woo--single{
  padding-bottom:clamp(30px,4vw,56px);

  /* Shared by the row, the gallery and the height cap below, so they cannot drift apart. */
  --pg-thumb:clamp(64px,6.4vw,96px);
  --pg-gap:clamp(10px,1vw,16px);
  --pg-colgap:clamp(28px,4vw,72px);
  --pg-summary:clamp(340px,26vw,470px);
  /*
   * EVERYTHING ABOVE THE STAGE, and it is a sum rather than a guess because the guess was wrong by
   * exactly the amount the image overflowed.
   *
   * ⚑ THIS READ `calc(var(--hdr-h) + 24px)` — 98px — while 150px actually sits above the image.
   * Measured at 1440x900: the announcement bar 25, the header 74, .woo's padding-top 34, the
   * breadcrumb 17. The image then ran 52px past the fold, which is 150 - 98 to the pixel. The
   * mechanism below was never wrong; it was being handed a number that left out three of the four
   * things it was supposed to account for.
   *
   * Each part names the rule it mirrors, so a change there is findable from here:
   *
   *   --pg-abar      .abar in shell.css, a flat 25px
   *   --hdr-h        the header's own token
   *   --pg-page-pad  .woo{padding-top} — the SAME clamp, not a copy of one of its ends
   *   --pg-crumbs    .prod-crumbs' line box: .66rem at the body's 1.65 line-height
   *
   * The trailing 16px is deliberate slack. "Fit within the screen" is satisfied by landing exactly
   * on the fold, but a photograph whose bottom edge is flush with the viewport reads as cut off
   * rather than complete — the gap is what says the image has ended.
   */
  --pg-abar:25px;
  --pg-page-pad:clamp(18px,2.4vw,34px);
  --pg-crumbs:calc(.66rem * 1.65);
  --pg-reserve:calc(var(--pg-abar) + var(--hdr-h) + var(--pg-page-pad) + var(--pg-crumbs) + 16px);
  --pg-stage-max:calc((100svh - var(--pg-reserve)) * 0.75);
  --pg-row:calc(var(--pg-stage-max) + var(--pg-thumb) + var(--pg-gap) + var(--pg-colgap) + var(--pg-summary));
}
.woo--single div.product{max-width:1560px;margin:0 auto;padding:0 var(--pad)}

/* Both of these are direct children of .woo--single, outside div.product's container. */
.woo--single>.woocommerce-notices-wrapper,
.woo--single .prod-crumbs{max-width:1560px;margin:0 auto;padding:0 var(--pad)}

/*
 * ⚑ THE PAGE IS CAPPED BY THE VIEWPORT'S HEIGHT, NOT ONLY ITS WIDTH — which is the difference
 * between a laptop and a monitor, and why this page looked right on one and wrong on the other.
 *
 * The slide is `width:100%` with `aspect-ratio:3/4`, so its HEIGHT is whatever its width implies.
 * Width came from the 1fr column, which came from the window's WIDTH — and a laptop is wide and
 * short. Measured before this rule, the stage was taller than the viewport at every common laptop
 * size, so the bag and the Add to Cart button could not be seen at once:
 *
 *     1920x1080   0.95   the monitor this was designed on, and the one size that was fine
 *     1920x860    1.19
 *     1536x864    1.25
 *     1440x780    1.29
 *
 * ⚑ And 1536x864 was WORSE than 1920, which is the tell that the sizing was wrong rather than
 * merely large: the summary column is `26vw`, so it shrinks faster than the row does and hands
 * the surplus to the gallery. The image got BIGGER as the screen got smaller.
 *
 * Two things were tried before this one, and both are worth knowing about:
 *
 *   - capping the GALLERY fixed the height and left a 300px hole between the image and the
 *     summary, because the 1fr column kept its width and the gallery merely stopped filling it;
 *   - capping the ROW fixed that, and then the breadcrumb above it — which has its own container —
 *     no longer lined up with the image.
 *
 * So the cap belongs to the page's container, where every part of the page already agrees on its
 * width. `min()` keeps the original 1560px ceiling for tall screens.
 *
 * `svh` rather than `vh`: on a phone `vh` is the height WITHOUT the browser's chrome, which is not
 * the space the page actually gets.
 *
 * Only on the two-column layout. Below 1025px the columns stack, the gallery is full width, and
 * its height follows the viewport's width — already in proportion on a phone.
 */
@media(min-width:1025px){
  .woo--single div.product,
  .woo--single>.woocommerce-notices-wrapper,
  .woo--single .prod-crumbs{
    max-width:min(1560px, calc(var(--pg-row) + var(--pad) * 2));
  }
}

/* ---- breadcrumb ---- */
/*
 * ⚑ THE VERTICAL PADDING THAT WAS HERE HAS NEVER APPLIED, and it is removed rather than fixed.
 * `.woo--single .prod-crumbs` above sets `padding:0 var(--pad)` — a SHORTHAND, which resets top
 * and bottom to 0, and at (0,2,0) it outranks this rule's (0,1,0) whatever the source order. So
 * `padding-top:clamp(14px,1.8vw,24px);padding-bottom:clamp(12px,1.6vw,20px)` computed to 0 from
 * the day it was written; the breadcrumb's 17px is its line box alone.
 *
 * Reviving it would push the gallery 44px further down and shrink every product image to pay for
 * it — a visual change nobody asked for, on a page whose current spacing is the approved one. So
 * the behaviour stays and the declarations go, because two lines that look like they set the
 * breadcrumb's spacing and do not are worse than no lines at all: --pg-crumbs above has to model
 * this element's height, and it was the first thing to trust them.
 */
.prod-crumbs{
  font-size:.66rem;letter-spacing:.14em;text-transform:uppercase;color:var(--muted);
}
.prod-crumbs a{color:var(--muted);transition:color .3s}
.prod-crumbs a:hover{color:var(--green)}
.prod-crumbs-sep{display:inline-block;margin:0 .6em;opacity:.5}

/*
 * The two-column row. .prod-top is emitted by the theme (see inc/woocommerce.php) purely so the
 * gallery and the summary have a shared parent — WooCommerce's own template puts .summary between
 * two hooks with no wrapper of its own.
 *
 * The grid lives HERE and not on div.product on purpose. When it was on div.product, everything
 * below the row was a grid item too, held in place with `grid-column: 1 / -1`, and the sticky
 * summary's containing block was the whole of div.product — so it slid down out of its row and
 * painted over the story band and the related products both. The story band has since gone; the
 * related products have not, and the trap would return the moment this grid moved up a level.
 */
.prod-top{
  display:grid;grid-template-columns:minmax(0,1fr) var(--pg-summary);
  column-gap:var(--pg-colgap);align-items:start;
}


/* ---- gallery: rail + stage ---- */

/*
 * Rendered by bebold_woo_gallery() and driven by section 5 of site.js. Everything interactive here
 * is an enhancement: the PHP emits every slide visible and the controls hidden, and `pg--ready`
 * — added by the script — is what collapses the stack to one slide and reveals the rail and the
 * arrows. With the script blocked the customer gets the whole shoot as a stacked column.
 */
/*
 * ⚑ The rail is ABSOLUTELY POSITIONED, and that is the whole point of this block.
 *
 * It used to be a flex item, and the note here used to read "the rail's length simply follows the
 * image count — at the eight this shoot has, it comes out a little shorter than the stage". The
 * client's second shoot supplies twelve and thirteen, and the rail then ran ~280px PAST the bottom
 * of the stage: a column of thumbnails hanging into empty space, with the page grown to fit it.
 *
 * Flexbox cannot fix that on its own. `align-items:stretch` sizes an item to the flex LINE, and the
 * line is sized by the tallest item — which is the rail, so it stretches to itself. Taking the rail
 * out of flow instead makes the stage the only thing that defines the height, `top:0;bottom:0` ties
 * the rail to exactly that, and the overflow scrolls. The gallery's left padding reserves the
 * column, so both share one pair of custom properties and cannot drift apart.
 *
 * The count is now irrelevant: 8, 13 or 30 images all render correctly. That is the real fix — the
 * old rule was not wrong so much as quietly dependent on a number nobody was tracking.
 */
/* Sizing tokens live on .prod-top, which is where the height cap is applied. */
.prod-gallery{
  --pg-thumb:clamp(64px,6.4vw,96px);
  --pg-gap:clamp(10px,1vw,16px);
  display:block;position:relative;
}
/* Only once the script has collapsed the stack — without it the rail is hidden and an indent
 * would be an indent around nothing. */
.prod-gallery.pg--ready{padding-left:calc(var(--pg-thumb) + var(--pg-gap))}

.prod-rail{display:none}
/*
 * ⚑ display:BLOCK, not grid — and that is a bug fix, not a preference.
 *
 * As a grid this overlapped its own thumbnails. The container has a DEFINITE height (top:0;bottom:0)
 * and the items carry `aspect-ratio:1`, so the row tracks were sized against that fixed height
 * while each item kept its square height from the aspect ratio: 84px images placed 64px apart,
 * every thumbnail bleeding over the one below it. It is only visible at full size — at the scale a
 * whole-page screenshot is reviewed, overlap and tight packing look identical, which is how it
 * survived a round of checks.
 *
 * A block container has no track sizing to fight: each button is a block whose height comes from
 * its own aspect-ratio, and the gap is a margin. There is no arrangement of image count and
 * container height that can make them overlap.
 */
.pg--ready .prod-rail{
  display:block;
  position:absolute;left:0;top:0;bottom:0;width:var(--pg-thumb);
  overflow-y:auto;overscroll-behavior:contain;
  /*
   * Scrollbar hidden at the client's request. Scrolling still works — wheel, touch, and keyboard:
   * the thumbnails are <button>s, so tabbing through them scrolls the rail on its own, and
   * site.js calls scrollIntoView({block:'nearest'}) whenever the active image changes, so the
   * arrows under the stage walk the rail too. Nothing here is reachable only by dragging a bar.
   */
  scrollbar-width:none;
  -ms-overflow-style:none;
}
.pg--ready .prod-rail::-webkit-scrollbar{width:0;height:0}

/* Thumbnails are ~12% of the stage's width, the proportion in the client's reference. */
.prod-thumb{
  display:block;
  width:100%;aspect-ratio:1;padding:0;overflow:hidden;cursor:pointer;
  margin:0 0 clamp(8px,.75vw,11px);
  background:var(--cream-alt);border:1px solid var(--hair);border-radius:4px;
  opacity:.55;transition:opacity .3s,border-color .3s;
}
.prod-thumb:last-child{margin-bottom:0}
.prod-thumb img{width:100%;height:100%;display:block;object-fit:cover}
/*
 * The video in the strip is sized exactly like the photograph it sits beside, so the rail does not
 * change shape when a product has a clip — and `pointer-events:none` because the strip entry is a
 * <button> and the click belongs to it, not to the media element inside it. object-fit:cover here,
 * contain on the slide: a 92px square wants filling, a 3:4 stage wants the whole frame.
 */
.prod-thumb-video{width:100%;height:100%;display:block;object-fit:cover;pointer-events:none;background:var(--sand-hi)}
.prod-thumb:hover,
.prod-thumb.is-active{opacity:1}
.prod-thumb.is-active{border-color:var(--green)}

.prod-stage-wrap{min-width:0;position:relative}
.prod-stage{display:grid;gap:clamp(8px,.8vw,12px);margin:0}
.pg--ready .prod-stage{display:block;gap:0}
.prod-slide{margin:0;overflow:hidden;background:var(--cream-alt);border-radius:6px}
.pg--ready .prod-slide{display:none}
.pg--ready .prod-slide.is-active{display:block}
/*
 * A fixed frame with object-fit, as in the reference: left at their natural heights the stage would
 * resize under the customer every time they changed image, and the sticky panel beside it would
 * jump with it.
 *
 * 3:4 rather than the 4:5 this started as. The first shoot mixed 3:2 lifestyle frames with 3:4
 * studio ones, so any single ratio cropped something and 4:5 was the least bad compromise. The
 * client's second shoot is 3:4 throughout — 1500x2000 and 1792x2400 — so matching it means the
 * photography is shown WHOLE, with nothing trimmed off a model's head or a bag's strap.
 */
.prod-slide img{width:100%;height:auto;aspect-ratio:3/4;object-fit:cover;object-position:center}

/*
 * CLICK TO ENLARGE — desktop only. site.js §5b adds `pg--zoomable` above 1025px and takes it away
 * below, so the cursor only ever promises something that will happen. The overlay itself is built
 * by the script and is absent from the DOM until the first click.
 */
.pg--zoomable .prod-slide:not(.prod-slide--video){cursor:zoom-in}

.prod-zoom{
  position:fixed;inset:0;z-index:200;
  display:flex;align-items:center;justify-content:center;
  padding:clamp(20px,4vh,56px);
  background:rgba(38,36,31,.92);
  /* The zoomed image is larger than this box on purpose; without this it spills over the page. */
  overflow:hidden;
}
.prod-zoom[hidden]{display:none}
/*
 * The photograph fills whatever is left after the padding and no more. `min()` on BOTH axes rather
 * than `max-width:100%;max-height:100%` so a portrait on a short window is bounded by the height
 * and a landscape on a narrow one by the width, with no arrangement that overflows either.
 */
.prod-zoom-img{
  display:block;width:auto;height:auto;
  max-width:100%;max-height:100%;
  object-fit:contain;border-radius:4px;
}
.prod-zoom-close{
  position:absolute;top:clamp(14px,2vh,26px);right:clamp(14px,2vw,26px);
  display:flex;align-items:center;justify-content:center;
  width:44px;height:44px;padding:0;
  background:none;border:0;color:var(--cream);cursor:pointer;
  border-radius:50%;transition:background .25s;
}
.prod-zoom-close:hover,
.prod-zoom-close:focus-visible{background:rgba(234,226,216,.16)}
/*
 * ⛑ THE SECOND STEP: FIT, THEN THE PHOTOGRAPH'S OWN PIXELS. Client's ask of 18 Sep 2026.
 * The zoomed image leaves the flex centring and is positioned against the whole overlay, so it can
 * use the padding too — that padding exists to frame a fitted picture, not to waste screen on a
 * zoomed one. `overflow:hidden` on the overlay is what stops it spilling over the page behind.
 *
 * No transition on the transform: panning follows the pointer, and an eased transform turns direct
 * manipulation into something that lags behind the hand.
 */
.prod-zoom.can-zoom .prod-zoom-img{cursor:zoom-in}
.prod-zoom.can-zoom.is-zoomed .prod-zoom-img{cursor:zoom-out}
.prod-zoom.is-zoomed .prod-zoom-img{
  position:absolute;left:50%;top:50%;
  max-width:none;max-height:none;border-radius:0;
}
.prod-zoom-img:focus-visible{outline:2px solid var(--cream);outline-offset:3px}
/* The affordance. Shown only where there is something to zoom into, which is the same rule the
   cursor follows — on a large monitor already showing the file at full size, neither appears. */
.prod-zoom-hint{
  position:absolute;left:50%;bottom:clamp(12px,2.4vh,26px);transform:translateX(-50%);
  margin:0;padding:7px 15px;border-radius:999px;
  background:rgba(0,26,14,.58);color:var(--cream);
  font-size:.72rem;font-weight:600;letter-spacing:.1em;text-transform:uppercase;
  pointer-events:none;opacity:0;transition:opacity .2s ease;
}
.prod-zoom.can-zoom .prod-zoom-hint{opacity:1}
@media (prefers-reduced-motion:reduce){
  .prod-zoom-hint{transition:none}
}
/* The page behind is inert while the overlay is up; without this it scrolls under the backdrop. */
body.has-zoom{overflow:hidden}

.prod-nav{
  display:none;position:absolute;gap:8px;
  right:clamp(12px,1.2vw,18px);bottom:clamp(12px,1.2vw,18px);
}
.pg--ready .prod-nav{display:flex}
.prod-nav-btn{
  width:42px;height:42px;display:grid;place-items:center;cursor:pointer;
  background:var(--cream);color:var(--green);
  border:1px solid var(--hair);border-radius:3px;
  transition:background .3s,color .3s,border-color .3s;
}
.prod-nav-btn:hover{background:var(--green);color:var(--cream);border-color:var(--green)}
/*
 * ⚑ ON A VIDEO SLIDE THE ARROWS MOVE UP, because the browser draws the video's own controls
 * exactly where they sit. Measured at 1440 before this rule: the 42px buttons, 17px off the
 * bottom, covered the bottom 23px of Chrome's 40px control bar - the fullscreen and
 * picture-in-picture buttons, unclickable.
 *
 * It is the bottom of the ELEMENT, not of the picture: `.prod-video` is object-fit:contain at
 * width:100%;height:100%, so the bar spans the full width of the 3:4 frame however the clip is
 * shaped. Moving the arrows is the fix; shrinking the video is not.
 *
 * +46px, so the buttons clear 63px of frame at 1440. Chrome and Firefox draw ~40px; Safari's
 * floating bar is taller and inset, around 56px all told, so this clears that too with a little
 * room. Only Chrome was measured here - if Safari ever shows a collision, this number is the one
 * to raise, and nothing else needs touching.
 *
 * Nothing needed below 900px: the arrows are already display:none there, where the stage is a
 * swipeable scroll-snap strip.
 */
.pg--video .prod-nav{bottom:calc(clamp(12px,1.2vw,18px) + 46px)}

/* ---- the sticky buy panel ---- */

/*
 * Sticky, and safely so: .prod-top is the containing block, so the panel stops at the foot of the
 * gallery instead of following the page. align-items:start above is what gives it room to move at
 * all — a stretched grid item fills its area and can never slide.
 *
 * NO max-height, and no internal scrollbar. On a 900px-tall window this panel is about 960px, so
 * the foot of it — the last accordion head — sits below the fold while the panel is pinned. That is
 * the normal behaviour of a tall sticky column and it resolves itself: the gallery is roughly
 * 1,000px taller than the panel, so as the stage scrolls past, the panel rides up with its
 * container and the foot comes into view.
 *
 * The alternative — max-height plus overflow-y:auto — was tried and removed. It puts a scrollbar
 * down the middle of a product page on every laptop, to solve a problem the reference page does not
 * solve either: its own panel is over 2,000px tall on the same sticky offset.
 */
.woo--single .summary{position:sticky;top:calc(var(--hdr-h) + 18px)}
/*
 * ⚑ THE NEGATIVE TOP MARGIN IS AN ALIGNMENT FIX, not spacing. The client reported on 10 Sep 2026
 * that the product name "looks like not vertically aligned with the rest of text".
 *
 * The boxes were never misaligned: measured at 2x, the name's ink starts within 1 CSS pixel of the
 * price and the body copy below it. What IS misaligned is the name against the top of the gallery
 * beside it. Both columns begin on the same grid line, but a 25.6px line at line-height 1.12 puts
 * half-leading plus the gap between ascender and cap-height above the letters, so the visible top
 * of "The.CP" landed 6.5px below the top edge of the product image — close enough to look like a
 * mistake and far enough to see.
 *
 * -.17em is that offset expressed in the type's own units, so it holds across the clamp() rather
 * than only at the width it was measured on — the gap is font metrics, so it scales with the type
 * and a px value would only have been right at one viewport. It trims leading that sits above the
 * capitals and nothing else; the descenders and the gap to the price are untouched.
 *
 * The coefficient is measured, not derived: 6.5px of drop at the 38.4px the clamp reaches on a
 * 1440 viewport. Re-derive it by capturing at 2x and comparing the first ink row of the title with
 * the top edge of the gallery — theme/tools/README.md has the snippet.
 */
.woo--single .summary .product_title{
  font-family:'Poppins',system-ui,sans-serif;font-weight:500;color:var(--green);
  font-size:clamp(1.6rem,2.6vw,2.4rem);line-height:1.12;letter-spacing:-.005em;
  margin:-.17em 0 0;
}
.woo--single .summary p.price,
.woo--single .summary span.price{
  display:block;font-size:1.25rem;color:var(--ink);letter-spacing:.02em;margin:14px 0 0;
}
.woo--single .summary p.price del{opacity:.55;margin-right:10px;font-size:.86em}
.woo--single .summary p.price ins{text-decoration:none}
.woo--single .summary .rule{margin:22px 0 0}


/*
 * ONE ROW: quantity, then Add to Cart taking whatever is left. The client asked for this on 10 Sep
 * 2026; it was stacked, with the stepper above a full-width button.
 *
 * `auto minmax(0,1fr)` rather than a fixed first column, so the row follows the stepper's own width
 * instead of restating it here — two places to change one number is how these drift. The
 * `minmax(0,…)` matters for the same reason it did on the orders table: a bare `1fr` is
 * `minmax(auto,1fr)` and would refuse to shrink below the button's label, pushing the row wider
 * than the sticky column at narrow widths instead of letting the button give way.
 *
 * `align-items:stretch` is what matches their heights, and it is why the button carries no height
 * of its own — the grid row is as tall as the stepper, and the button fills it. Setting a height on
 * either would mean maintaining two numbers that have to agree.
 *
 * WooCommerce prints the availability line inside the form and ahead of both; it spans the row.
 */
/*
 * ⚑ THE BUY ROW MOVED ONE LEVEL DOWN WHEN THE PRODUCTS BECAME VARIABLE, and the symptom was the
 * quantity stepper and the button stacking instead of sitting side by side.
 *
 * On a SIMPLE product the form's own children are the stepper and the button, so `form.cart` is
 * the grid. On a VARIABLE product WooCommerce wraps them in
 * `.single_variation_wrap > .woocommerce-variation-add-to-cart`, and the form's children become
 * the attributes table, a screen-reader alert, a gallery template and that wrapper. The grid was
 * still applied — it was just laying out the wrong four things, which is why the computed columns
 * read `132.344px 0px` rather than failing in any way that looked like a bug.
 *
 * So the row is declared on whichever element actually holds the two controls, and the form itself
 * goes back to being a block when it is a variations form. The descendant rules below need no
 * change: `.quantity` and `.single_add_to_cart_button` are inside `form.cart` either way.
 */
.woo--single form.cart:not(.variations_form),
.woo--single form.cart .woocommerce-variation-add-to-cart{
  display:grid;grid-template-columns:auto minmax(0,1fr);align-items:stretch;
  gap:12px;
}
.woo--single form.cart{margin:clamp(20px,2.4vw,26px) 0 0}
.woo--single form.variations_form.cart{display:block}
/*
 * Woo's wrappers carry margins of their own, which would double the gap the grid already sets.
 * `.woocommerce-variation` holds the variation's price and availability — both hidden above,
 * because this design shows the price in the summary, not under the button — so it must not
 * reserve space either.
 */
.woo--single form.cart .single_variation_wrap{margin:0}
.woo--single form.cart .woocommerce-variation.single_variation{margin:0}
.woo--single form.cart .woocommerce-variation.single_variation:empty{display:none}
.woo--single form.cart .quantity{justify-self:start}
/*
 * A shade less padding than a standalone button. Sharing a line with the stepper it no longer needs
 * to carry the block on its own, and the tracked-out label needs the room more than the frame does.
 */
.woo--single form.cart .single_add_to_cart_button{
  width:100%;justify-content:center;padding-left:18px;padding-right:18px;
}
/*
 * WooCommerce's own added_to_cart handler appends a "View cart" link after whichever button it was
 * handed. On the single page that adds a permanent row to the form the first time anyone adds
 * something — and the whole point of the AJAX submit in site.js §6 is that the page does NOT change
 * under the customer. The toast already carries that link.
 *
 * Hidden rather than prevented: stopping Woo from adding it means pre-seeding the element its
 * handler checks for, which is a worse dependency on its internals than one display rule.
 */
.woo--single form.cart .added_to_cart{display:none}
/*
 * The brief loading state while the request is in flight. woocommerce-general's own .loading rule
 * drops the button to opacity .25 and spins a pseudo-element, which on a full-width dark button
 * reads as the button vanishing. This keeps it legible and just makes it plainly busy.
 */
.woo--single form.cart .single_add_to_cart_button.loading{opacity:.65;cursor:progress}
/*
 * And no glyph in either state. woocommerce-general appends a spinner while .loading and a tick
 * once .added, both from its own icon font. The tick never clears, so after one add the button
 * reads "Add to cart ✓" for the rest of the visit — and the shop grid's own buttons never show it,
 * because they carry .btn rather than .button. The toast is the confirmation, on both.
 */
.woo--single form.cart .single_add_to_cart_button.loading::after,
.woo--single form.cart .single_add_to_cart_button.added::after{content:none;display:none}
/*
 * ⚑ THE AVAILABILITY LINE, WHICH NOBODY HAS EVER SEEN ON THIS SITE.
 *
 * It only appears once a product has stock management on with a quantity set, which the client has
 * not done yet. So it is styled from the markup rather than from the screen, and it has now been
 * written twice for that reason — worth knowing before trusting these selectors.
 *
 * ROUND ONE (Astra). Astra replaced WooCommerce's line with its own
 * `<p class="ast-stock-detail"><span class="ast-stock-avail">Availability:</span><span
 * class="stock">…</span></p>`, above the form. A `form.cart .stock` rule matched nothing, and the
 * "Availability:" label sat in Astra's body type, larger and darker than the value beside it.
 *
 * ROUND TWO (19 Sep 2026, Astra gone). WooCommerce's own markup is back, in two places and with no
 * label at all:
 *
 *   - simple products — `<p class="stock">` printed by add-to-cart/simple.php, above the form;
 *   - variable products — the same paragraph inside
 *     `<div class="woocommerce-variation-availability">`, written by the variation template above
 *     the buy row. Every product here is variable, so this is the one that matters.
 *
 * ⚑ AND THAT DIV WAS ON THE `display:none` LIST further down this file, hidden as a duplicate of
 * Astra's copy. Left alone, removing Astra would not have moved or restyled this line: it would
 * have deleted it, silently, on whatever future day the client first set a stock quantity. It is
 * unhidden there now, and the two rules have to stay in step.
 *
 * Same eyebrow treatment as before, minus a label WooCommerce does not print.
 */
.woo--single p.stock{
  font-size:.66rem;letter-spacing:.16em;text-transform:uppercase;color:var(--muted);
  margin:clamp(18px,2.2vw,24px) 0 0;
}
/* Inside the variations form it is ABOVE the buy row, so the gap belongs underneath it. */
.woo--single .woocommerce-variation-availability p.stock{
  margin:0 0 clamp(14px,1.8vw,18px);
}
.woo--single p.stock.out-of-stock{color:var(--error)}

/* ---- the three assurances ---- */
.prod-assure{
  list-style:none;margin:clamp(20px,2.2vw,26px) 0 0;padding:clamp(17px,2vw,22px) 0 0;
  border-top:1px solid var(--hair);display:grid;gap:11px;
}
.prod-assure li{display:flex;align-items:center;gap:14px;font-size:.82rem;line-height:1.5;color:var(--muted)}
/* The homepage's .ico-* masks, at a size that suits a list rather than a three-column band. */
.prod-assure-ico{
  width:30px;height:26px;flex:0 0 auto;background-color:var(--gold);
  -webkit-mask-repeat:no-repeat;mask-repeat:no-repeat;
  -webkit-mask-position:center;mask-position:center;
  -webkit-mask-size:contain;mask-size:contain;
}

/* ---- the collapsed panels ---- */
.prod-panels{margin:clamp(18px,2.2vw,24px) 0 0;border-top:1px solid var(--hair)}
.prod-panel{border-bottom:1px solid var(--hair)}
.prod-panel-head{
  display:flex;align-items:center;justify-content:space-between;gap:16px;
  padding:clamp(13px,1.5vw,15px) 0;cursor:pointer;list-style:none;
}
/* Both are needed: WebKit uses the pseudo-element, everyone else honours list-style. */
.prod-panel-head::-webkit-details-marker{display:none}
.prod-panel-title{font-size:.9rem;color:var(--green);letter-spacing:.01em}
/* A + that becomes a − , drawn from two bars so there is no glyph to go missing in a fallback font. */
.prod-panel-mark{position:relative;width:11px;height:11px;flex:0 0 auto}
.prod-panel-mark::before,
.prod-panel-mark::after{
  content:"";position:absolute;left:0;top:5px;width:11px;height:1px;background:var(--green);
  transition:transform .3s ease,opacity .3s ease;
}
.prod-panel-mark::after{transform:rotate(90deg)}
.prod-panel[open] .prod-panel-mark::after{transform:rotate(90deg) scaleX(0);opacity:0}
.prod-panel-body{padding:0 0 clamp(18px,2.2vw,24px)}
.prod-panel-p{font-size:.85rem;line-height:1.8;color:var(--muted);margin:0}
.prod-panel-lnk{margin-top:16px}
/* The long description. Scoped to its own wrapper rather than written as `.prod-panel-body p`,
 * which would out-specify .prod-specs-note — also a <p> in a panel body. */
.prod-panel-rich p{font-size:.85rem;line-height:1.8;color:var(--muted);margin:0 0 12px}
.prod-panel-rich p:last-child{margin-bottom:0}

/*
 * PANEL LISTS — the gold diamond from "An Intentional Approach to Oleatex" on What Sets Us Apart
 * (`.olist-b`), so a bullet means the same thing wherever it appears on the site. It replaced a
 * 7x1px dash, which was a different mark for the same job.
 *
 * ⚑ THE SELECTOR HAS TO REACH UNCLASSED MARKUP, and that is the whole reason this is two selectors
 * rather than one. `.prod-features` is the theme's own list, printed by the Care section. But the
 * Detail section became CLIENT-WRITTEN rich content, so its list arrives as a bare `<ul>` typed
 * into an editor — the client will never add a class, and nor should they have to. Before this,
 * that list fell back to browser-default discs with default indentation while Care two panels
 * below it had gold diamonds.
 *
 * Ordered lists keep their numbers: a diamond on a numbered list is not a bullet style, it is a
 * missing number. They only need the indent and type scale.
 */
.prod-features,
.prod-panel-rich ul{list-style:none;margin:0;padding:0;display:grid;gap:12px}
.prod-features li,
.prod-panel-rich ul li{position:relative;padding-left:26px;margin:0;font-size:.85rem;line-height:1.7;color:var(--ink)}
/*
 * ⚑ CENTRED ON THE TEXT'S INK, NOT ON THE LINE BOX — and those are NOT the same thing, which is
 * the whole reason this took three attempts.
 *
 * The two wrong answers, in order:
 *
 *   1. `top:.6em`, inherited from the Oleatex list. Wrong because these markers are `content-box`,
 *      so an 8x8 square with a 1px border is a TEN pixel box and .6em placed it as if it were 8.
 *   2. `calc((1.7em - 10px) / 2)` — centre the marker in the LINE BOX. Geometrically exact, and
 *      still visibly high, because a line box is not symmetric about its text: at 13.6px Poppins
 *      the baseline sits 16px down, the ink runs 11px up and 4px down from it, so the ink centre is
 *      12.5px while the line-box centre is 11.56px. Centring in the box put the diamond a pixel
 *      into the ascender zone, where it reads as floating above the words.
 *
 * The target is the ink centre: `baseline - (ascent - descent) / 2`. For Poppins at this weight
 * that is 1.176em - 0.257em = 0.92em from the top of the line, and the marker's own 10px is
 * subtracted separately because it does not scale with the font.
 *
 * ⚑ Verified BY EYE at 6x magnification, not only by arithmetic — the numbers said the line-box
 * version was 0.06px out and it still looked wrong. Three candidates were rendered side by side
 * (cap centre, x-height centre, ink centre) and the ink centre is the one that sits on the
 * lowercase body.
 *
 * Assumes line-height 1.7 (via the baseline term) and the Poppins metrics above. Change either and
 * re-measure; the derivation is written out so that is a five-minute job rather than a guess.
 */
.prod-features li::before,
.prod-panel-rich ul li::before{
  content:"";position:absolute;left:2px;top:calc(.92em - 5px);width:8px;height:8px;
  border:1px solid var(--gold);background:none;transform:rotate(45deg);
}
/* A list the client writes is usually followed by a paragraph; the <ul> itself carries no margin. */
.prod-panel-rich ul{margin-bottom:12px}
.prod-panel-rich ul:last-child{margin-bottom:0}
.prod-panel-rich ol{margin:0 0 12px;padding-left:22px}
.prod-panel-rich ol:last-child{margin-bottom:0}
.prod-panel-rich ol li{margin:0 0 8px;font-size:.85rem;line-height:1.7;color:var(--ink)}
.prod-specs{margin:0;display:grid;grid-template-columns:auto 1fr;column-gap:clamp(18px,2vw,30px);row-gap:10px;align-items:baseline}
.prod-specs dt{font-size:.66rem;letter-spacing:.14em;text-transform:uppercase;color:var(--muted);line-height:1.7}
.prod-specs dd{margin:0;font-size:.85rem;line-height:1.7;color:var(--ink)}
.prod-specs-note{font-size:.74rem;line-height:1.7;color:var(--muted);margin:16px 0 0;font-style:italic}

/* ---- below the row ---- */

/* "More from the collection" — the only thing under the gallery row since the story band was
 * removed. It is the last child of div.product, so its own bottom margin is the page's. */
.woo--single .related{margin-top:clamp(38px,4.6vw,66px)}
.woo--single .related>h2{
  font-family:'Poppins',system-ui,sans-serif;font-weight:500;color:var(--green);
  font-size:clamp(1.3rem,2vw,1.75rem);margin:0 0 clamp(20px,2.4vw,30px);text-align:center;
}
.woo--single .related .woo-grid{grid-template-columns:repeat(3,1fr)}

/* ============ cart, checkout and account ============ */

/*
 * These three are ordinary pages carrying a shortcode, so they arrive inside page.php's .wrap and
 * already have their title and rule. Only what WooCommerce prints inside .page-body is styled here.
 */
.page-body .woocommerce{margin-top:clamp(24px,3vw,38px)}

.woocommerce table.shop_table{
  width:100%;border-collapse:collapse;border:0;margin:0 0 clamp(24px,3vw,34px);
  background:transparent;border-radius:0;
}
.woocommerce table.shop_table th{
  text-align:left;font-size:.62rem;font-weight:400;letter-spacing:.18em;text-transform:uppercase;
  color:var(--muted);padding:0 14px 12px 0;border-bottom:1px solid var(--hair);
}
/*
 * The checkout's order table keeps its column headers — Product and Subtotal — and they are the
 * only two on the page, so they carry the weight. Bold and in ink rather than muted: at .62rem
 * uppercase, 400 in --muted reads as a caption rather than a heading.
 *
 * The tfoot's row labels — Subtotal, Shipment, Total — are bold too, at the client's ask on
 * 7 Sep 2026. They were left muted at first on the argument that running totals read better as
 * quiet labels against emphasised values; the client wanted the weight, so the whole panel now
 * shares one treatment. The AMOUNTS go to 700 with them, or a 500 value sits lighter than its own
 * 700 label, which looks like a mistake rather than a hierarchy.
 */
.woocommerce-checkout #order_review table.shop_table th{font-weight:700;color:var(--ink)}
.woocommerce-checkout #order_review table.shop_table tfoot td,
.woocommerce-checkout #order_review table.shop_table tfoot .amount,
.woocommerce-checkout #order_review table.shop_table tfoot strong{font-weight:700}
/*
 * Except the shipping OPTIONS. They live in the Shipment cell, so the rule above caught them and
 * "Standard delivery: S$5" came out bold — which reads as a total rather than as something to
 * choose. The label goes back to normal weight and the price inside it keeps the 700, so it reads
 * "Standard delivery: S$5" with the money emphasised, which is the useful half.
 */
.woocommerce-checkout #order_review table.shop_table tfoot ul#shipping_method label,
.woocommerce-checkout #order_review table.shop_table tfoot .woocommerce-shipping-destination,
.woocommerce-checkout #order_review table.shop_table tfoot p{font-weight:400}
.woocommerce table.shop_table td{
  padding:18px 14px 18px 0;border-top:0;border-bottom:1px solid var(--hair);
  font-size:.9rem;color:var(--ink);vertical-align:middle;
}
/*
 * The two picture-only columns need their width declared on the <th> as well. Left to itself the
 * table hands the remove column everything the others do not claim — 330px of empty space before
 * the thumbnail.
 */
.woocommerce table.shop_table th.product-remove,
.woocommerce table.shop_table td.product-remove{width:34px;padding-right:0}
.woocommerce table.shop_table th.product-thumbnail,
.woocommerce table.shop_table td.product-thumbnail{width:96px}
.woocommerce table.shop_table td.product-thumbnail img{width:80px;height:auto;border-radius:3px}
.woocommerce table.shop_table td.product-name a{color:var(--green)}
/* The × itself is shaped in the component-internals section; this is only its place in the table. */
.woocommerce table.shop_table .product-remove a.remove{background:transparent}
.woocommerce table.shop_table tfoot th,
.woocommerce table.shop_table tfoot td{border-bottom:0;padding-top:14px}

.woocommerce .cart_totals h2,
.woocommerce-checkout #order_review_heading,
.woocommerce-checkout .woocommerce-billing-fields h3,
.woocommerce-checkout .woocommerce-additional-fields h3{
  font-family:'Poppins',system-ui,sans-serif;font-weight:500;color:var(--green);
  font-size:1.12rem;margin:0 0 18px;
}
.woocommerce .wc-proceed-to-checkout .button{width:100%}

.woocommerce .cart .actions .button{white-space:nowrap}

/* ---- the cart page ---- */

/*
 * Redesigned 7 Sep 2026: line-item ROWS on the left, a sticky order summary on the right — the
 * same two-column shape as the product page and the checkout, so the three read as one system.
 * It replaced a plain WooCommerce table with a column of small-caps headers and a totals box
 * floating in a lot of empty space.
 *
 * ⚑ ALL OF IT IS CSS, and that is a constraint rather than a preference. The cart is
 * `[woocommerce_cart]` → templates/cart/cart.php, and that table is TEMPLATE BODY, not hooks —
 * there is no hook that reorders a cell. Copying the template is the one thing this theme does not
 * do (see the file header in inc/woocommerce.php), so the table is turned into a grid instead:
 * `display:block` on the table, `display:grid` on each row, and every cell placed explicitly.
 *
 * That also RETIRES a trap documented further up this file. The old comment warned that
 * `display:flex` on `td.actions` drops the cell out of the table layout algorithm, colspan stops
 * applying, and the row collapses into the first column. True while it was a table; there is no
 * table algorithm left to fall out of once the rows are grids, so the actions cell is flexed below
 * and the colspan is simply irrelevant.
 */
.woocommerce-cart .page-body .woocommerce{
  display:grid;
  grid-template-columns:minmax(0,1fr) clamp(300px,29vw,390px);
  column-gap:clamp(28px,4vw,64px);align-items:start;
}
.woocommerce-cart .page-body .woocommerce>.woocommerce-notices-wrapper{grid-column:1 / -1}
.woocommerce-cart form.woocommerce-cart-form{grid-column:1}
.woocommerce-cart .cart-collaterals{grid-column:2;display:block}

/* ---- the items ---- */
.woocommerce-cart table.shop_table.cart{display:block;border:0;margin:0;background:transparent}
.woocommerce-cart table.shop_table.cart thead{display:none}
.woocommerce-cart table.shop_table.cart tbody{display:block}

.woocommerce-cart table.shop_table.cart tr.cart_item{
  display:grid;
  grid-template-columns:96px minmax(0,1fr) auto auto 24px;
  column-gap:clamp(14px,1.7vw,24px);
  align-items:center;
  padding:clamp(16px,2vw,22px) 0;border-bottom:1px solid var(--hair);
}
.woocommerce-cart table.shop_table.cart tr.cart_item:first-child{padding-top:0}
.woocommerce-cart table.shop_table.cart tr.cart_item td{display:block;padding:0;border:0}

.woocommerce-cart table.shop_table.cart td.product-thumbnail{grid-column:1;grid-row:1 / span 2;width:auto}
.woocommerce-cart table.shop_table.cart td.product-thumbnail img{
  width:96px;height:96px;object-fit:cover;border-radius:3px;background:var(--cream-alt);
}
.woocommerce-cart table.shop_table.cart td.product-name{
  grid-column:2;grid-row:1;align-self:end;
  font-size:1.02rem;line-height:1.3;
}
.woocommerce-cart table.shop_table.cart td.product-name a{color:var(--green)}
/* The subtitle is appended to the name by inc/woocommerce.php, from pa_inspiration. */
.woocommerce-cart table.shop_table.cart .cart-item-sub{
  display:block;margin:5px 0 0;
  font-size:.66rem;letter-spacing:.16em;text-transform:uppercase;color:var(--muted);
}
/* Unit price under the name — muted, and labelled "each" by the same file, so that at a quantity
 * of one it cannot be mistaken for the line total sitting opposite it. */
.woocommerce-cart table.shop_table.cart td.product-price{
  grid-column:2;grid-row:2;align-self:start;
  margin-top:6px;font-size:.8rem;color:var(--muted);
}
.woocommerce-cart table.shop_table.cart td.product-quantity{grid-column:3;grid-row:1 / span 2}
.woocommerce-cart table.shop_table.cart td.product-subtotal{
  grid-column:4;grid-row:1 / span 2;justify-self:end;
  font-size:1.02rem;color:var(--ink);white-space:nowrap;
}
.woocommerce-cart table.shop_table.cart td.product-remove{
  grid-column:5;grid-row:1 / span 2;justify-self:end;width:auto;
}

/* ---- coupon and Update cart ---- */
/*
 * The actions row is one <td colspan="6"> in a <tr> of its own. Both become blocks, so the cell can
 * lay its two children out as a row: coupon left, Update cart right.
 */
.woocommerce-cart table.shop_table.cart tbody tr:has(td.actions){display:block;border:0;padding:0}
.woocommerce-cart table.shop_table.cart td.actions{
  display:flex;flex-wrap:wrap;gap:12px;align-items:center;justify-content:space-between;
  padding:clamp(18px,2.2vw,26px) 0 0;border:0;text-align:left;
}
.woocommerce-cart table.shop_table.cart td.actions .coupon{
  float:none;display:flex;gap:10px;align-items:center;margin:0;
}
.woocommerce-cart table.shop_table.cart td.actions .coupon .input-text{width:190px}
/*
 * With live quantities (site.js §8) the Update cart button has nothing left to do, so it goes —
 * but only when the script that made it redundant is actually running. The class is added to <body>
 * by that file, so with JavaScript blocked the button is still there and still the way to save a
 * quantity change.
 */
/*
 * ⚑ NOT `:input[name="update_cart"]`, which is what this said first. `:input` is a jQuery
 * pseudo-selector and not valid CSS — the browser discards the whole rule, silently, so the button
 * stayed visible with nothing to report. WooCommerce's own JS uses that selector, which is where it
 * was copied from. In WooCommerce 11 the control is a <button>; the input is there for older
 * markup and costs nothing.
 */
body.cart-qty-live .woocommerce-cart-form button[name="update_cart"],
body.cart-qty-live .woocommerce-cart-form input[name="update_cart"]{display:none}

/* ---- the order summary ---- */
.woocommerce-cart .cart_totals{
  position:sticky;top:calc(var(--hdr-h) + 18px);
  background:var(--sand-hi);border:1px solid var(--hair);border-radius:6px;
  padding:clamp(20px,2.4vw,28px);
}
/* The panel now carries the frame, so the table inside gives up its own. */
.woocommerce-cart .cart_totals table.shop_table{
  background:transparent;border:0;border-radius:0;margin:0;display:table;width:100%;
}
.woocommerce-cart .cart_totals table.shop_table th,
.woocommerce-cart .cart_totals table.shop_table td{
  padding:9px 0;border:0;vertical-align:top;
}
.woocommerce-cart .cart_totals table.shop_table th{
  width:auto;font-size:.66rem;letter-spacing:.14em;text-transform:uppercase;
  color:var(--muted);font-weight:400;text-align:left;
}
.woocommerce-cart .cart_totals table.shop_table td{text-align:right;font-size:.9rem;color:var(--ink)}
.woocommerce-cart .cart_totals tr.order-total th,
.woocommerce-cart .cart_totals tr.order-total td{
  border-top:1px solid var(--hair);padding-top:15px;
}
.woocommerce-cart .cart_totals .order-total .amount{font-size:1.12rem;color:var(--green)}
/*
 * The shipping options are a list in a cell, and they read better left-aligned with their label
 * than ranged right with the money.
 *
 * `table.shop_table` is in the selector to win a tie. Without it this is (0,3,1) against the
 * generic value rule's (0,3,2) — equal on classes, and the generic one has one more TYPE selector,
 * so it took the cascade and the radios' helper text and "Calculate shipping" stayed ranged right.
 */
.woocommerce-cart .cart_totals table.shop_table .woocommerce-shipping-totals td{text-align:left}


.woocommerce-checkout .woocommerce form.checkout{
  display:grid;grid-template-columns:minmax(0,1.1fr) minmax(0,1fr);
  grid-template-rows:auto 1fr;
  column-gap:clamp(28px,4vw,64px);row-gap:18px;align-items:start;
}
/*
 * woocommerce-general still floats these three into a 48%/48% pair — the checkout's two-column
 * layout is not confined to woocommerce-layout.css, which is why dropping that file is not enough.
 * Left floated inside a grid they collapse to a third of the width each.
 */
.woocommerce-checkout .woocommerce form.checkout #customer_details,
.woocommerce-checkout .woocommerce form.checkout #order_review_heading,
.woocommerce-checkout .woocommerce form.checkout #order_review{float:none;width:auto}

/* Billing spans both rows so the order summary can sit beside it under its own heading. */
.woocommerce-checkout .woocommerce form.checkout #customer_details{grid-column:1;grid-row:1 / span 2}
.woocommerce-checkout .woocommerce form.checkout #order_review_heading{grid-column:2;grid-row:1}
.woocommerce-checkout .woocommerce form.checkout #order_review{grid-column:2;grid-row:2}

/*
 * Woo's own .col2-set floats. On the checkout's billing/shipping pair the two halves simply stack;
 * #customer_login gets a real two-column treatment further down.
 */
.woocommerce .col2-set .col-1,
.woocommerce .col2-set .col-2{float:none;width:auto}
.woocommerce-checkout #order_review{
  background:var(--sand-hi);border:1px solid var(--hair);border-radius:6px;
  padding:clamp(20px,2.4vw,30px);
}
.woocommerce-checkout #order_review table.shop_table{margin-bottom:20px}
.woocommerce-checkout #payment{background:transparent;border-radius:0}
.woocommerce-checkout #payment ul.payment_methods{
  border-bottom:1px solid var(--hair);padding:0 0 16px;margin:0 0 18px;list-style:none;
}
/*
 * One gateway, no choice to present — so the chooser goes. `display:none` and not a clip: a clipped
 * radio is still focusable, and tabbing onto an invisible control that has no alternative to choose
 * is worse than not offering it. A display:none control still posts its value, which is what keeps
 * Place Order working.
 *
 * The class comes from inc/woocommerce.php and is applied by COUNTING the available gateways, so
 * this undoes itself the day a second one is enabled.
 */
/*
 * ⚑ IT NOW STATES THE METHOD RATHER THAN HIDING IT - 21 Sep 2026. This rule used to be
 * `display:none` on the whole list, which was right while the only gateway was a placeholder
 * nobody should use. The note beside the body_class filter in inc/woocommerce.php said what
 * would change: "it stops being acceptable the moment a real one is live with this still the
 * only method - at which point the method needs stating in words, not as a radio of one."
 *
 * AXS went live on 21 Sep and that moment arrived. The list shows; only the radio is hidden,
 * because a control offering one option is not a choice. The customer reads the method and its
 * logo, which is what they need, and WooCommerce still has its checked input in the DOM - the
 * place-order handler reads it, so hiding it visually must never become removing it.
 *
 * Two or more gateways and the body class is not applied at all, so the real chooser returns.
 */
body.checkout-single-gateway .woocommerce-checkout #payment ul.payment_methods li input[type="radio"]{
  position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);white-space:nowrap;border:0;
}

/*
 * The list itself, styled for the day there IS a choice. WooCommerce renders each gateway as an
 * <li> holding a radio, a <label> that is its SIBLING and not its parent, an optional logo <img>
 * that the gateway plugin injects into that label, and a div.payment_box for its instructions or
 * its card fields. The radio being a sibling is the detail that shapes this: the <li> has to do the
 * laying out, and it wraps so the payment_box drops onto its own full-width row underneath.
 *
 * Written now, while the store has one gateway and hides the list, so that switching on Stripe or
 * PayPal later is a store setting rather than a stylesheet job.
 */
.woocommerce-checkout #payment ul.payment_methods li{
  display:flex;flex-wrap:wrap;align-items:center;column-gap:10px;row-gap:0;
  margin:0;padding:0;list-style:none;
}
.woocommerce-checkout #payment ul.payment_methods li + li{
  margin-top:14px;padding-top:14px;border-top:1px solid var(--hair);
}
.woocommerce-checkout #payment ul.payment_methods li input[type="radio"]{margin:0;flex:none}
.woocommerce-checkout #payment ul.payment_methods li > label{
  display:flex;align-items:center;gap:10px;flex:1 1 auto;margin:0;cursor:pointer;
  font-family:'Poppins',system-ui,sans-serif;font-weight:400;font-size:.9rem;
  line-height:1.5;color:var(--ink);
}
/* A gateway's own logo, pushed to the far edge. Capped rather than sized — they arrive at any
 * dimension, and several gateways send more than one. */
.woocommerce-checkout #payment ul.payment_methods li > label img{
  max-height:22px;width:auto;margin:0 0 0 auto;
}
.woocommerce-checkout #payment ul.payment_methods li > label img + img{margin-left:6px}
/* Full-width row under the label it belongs to. */
.woocommerce-checkout #payment ul.payment_methods li > .payment_box{flex:0 0 100%}
.woocommerce-checkout #payment div.form-row{padding:0}

/*
 * The privacy notice. It had no rule of its own on this page, so it inherited the 16px body size
 * and sat as large as the order it explains. It is supplementary legal text — .78rem and muted,
 * which is small without going below what is comfortable next to a 16px base.
 */
.woocommerce-checkout .woocommerce-privacy-policy-text{margin:0 0 12px}
.woocommerce-checkout .woocommerce-privacy-policy-text p{
  font-size:.78rem;line-height:1.75;color:var(--muted);margin:0 0 10px;
}
/* Zeroed on the last paragraph, and the GAP moved to the wrapper — otherwise the terms checkbox
 * that follows sits hard against the text. */
.woocommerce-checkout .woocommerce-privacy-policy-text p:last-child{margin-bottom:0}

/*
 * "Ship to a different address?" is an <h3> that WooCommerce wraps around a CHECKBOX — so with no
 * rule it took the browser's h3 default and rendered at ~19px bold, heavier than the billing
 * heading above it and heavier than anything else on the form. It is a control, so it reads as one:
 * normal weight at .88rem.
 *
 * Not folded into the `.woocommerce-billing-fields h3` rule further up, which is for real section
 * headings — this one only shares a tag with them.
 */
.woocommerce-checkout #ship-to-different-address{
  margin:0 0 clamp(14px,1.8vw,20px);
  font-family:'Poppins',system-ui,sans-serif;font-weight:400;font-size:.88rem;
  line-height:1.5;color:var(--ink);
}
.woocommerce-checkout #ship-to-different-address label{margin:0;cursor:pointer}
.woocommerce-checkout #payment .place-order .button{width:100%}

.woocommerce-form-login,
.woocommerce-form-register,
.woocommerce-form-coupon{
  background:var(--sand-hi);border:1px solid var(--hair);border-radius:6px;
  padding:clamp(20px,2.4vw,28px);
}

/* ---- log in / register ---- */

/*
 * The client's reference is one panel split in two, login on the left and register on the right,
 * with a hairline down the middle. WooCommerce already emits exactly that structure —
 * `#customer_login.col2-set` holding `.col-1` and `.col-2` — so this is CSS only; the single line
 * of PHP added for it just puts a sentence at the top of the register column, which WooCommerce
 * leaves empty.
 *
 * The two forms each had their own bordered panel before (they still do on the checkout, where
 * they are collapsible drawers). Inside this grid they give that up to the parent, or the page
 * shows a box inside a box.
 */
.woocommerce #customer_login.col2-set{
  display:grid;grid-template-columns:1fr 1fr;
  background:var(--sand-hi);border:1px solid var(--hair);border-radius:6px;
  overflow:hidden;margin:0;
}
/*
 * The same panel with ONE of its two columns taken out. Login alone on /my-account/,
 * register alone at ?action=register — see bebold_myaccount_login_only() in
 * inc/woocommerce.php for why the fields are ABSENT rather than hidden.
 *
 * A separate rule rather than a tweak to the one above, because the grid is keyed on `.col2-set`
 * and neither single-column wrapper carries it: everything else in this block is keyed on
 * `#customer_login` and applies to all three shapes unchanged. `max-width` is what stops one form
 * stretching to the full 1560px container and turning a card into a banner.
 */
.woocommerce #customer_login.bb-login-only,
.woocommerce #customer_login.bb-register-only{
  display:block;max-width:520px;margin:0 auto;
  background:var(--sand-hi);border:1px solid var(--hair);border-radius:6px;overflow:hidden;
}

/*
 * "Create new account", under the Remember me / Forgot your password row.
 *
 * The margin clears `p.lost_password`, which is `position:absolute` against the form and so
 * contributes no height of its own — without it this link would sit underneath that text rather
 * than below it. Tied to the same line-height the two share, so it tracks the type scale instead
 * of needing a new number every time it moves.
 */
.woocommerce #customer_login .bb-register-link{
  margin:calc(1.2em + 10px) 0 0;font-size:.78rem;
}

/*
 * And "Back to login", under the Register button.
 *
 * Only the spacing is set here. Size, colour and centring already come from the rule below that
 * catches every non-field paragraph in this column, the same one that styles WooCommerce's
 * privacy-policy line, and matching it is the point: this is a secondary action sitting beside
 * that text, not a second button. `> p.bb-login-link` is what outranks it on the margin alone.
 */
.woocommerce #customer_login .col-2 form.register > p.bb-login-link{
  margin:22px 0 0;
}

/*
 * The reCAPTCHA tick, above the Register button.
 *
 * Google's widget is a fixed 304x78 iframe that cannot be styled from here, so this only places it.
 * Inert until keys are set: with none, the element is never printed and these rules match nothing.
 */
.woocommerce .bb-recaptcha{display:flex;margin:0 0 18px}
/* The register column is centred, as in the reference; the lost-password form is not. */
.woocommerce #customer_login .bb-recaptcha{justify-content:center}
/*
 * And it is WIDER THAN THE PANEL ON A PHONE. Measured, not guessed: at 375px both panels' inner
 * width is 286px against the widget's fixed 304px, so 18px of it - including part of Google's own
 * branding - was being clipped by `overflow:hidden`. Nothing scrolled sideways, which is exactly
 * why it would have gone unnoticed.
 *
 * `transform` rather than Google's own `data-size="compact"`: the widget renders once, on load,
 * from that attribute, so a media query cannot reach it. Scaling leaves the layout box at 304px
 * and shrinks only what is drawn, which is all that was overflowing.
 *
 * The origin differs with the alignment, and using one for both would re-clip the other: a centred
 * 304px box already starts 9px left of its 286px parent, so it has to shrink towards its middle,
 * while a left-aligned one has to shrink towards its left edge or it pulls away from the label
 * above it.
 */
@media (max-width:400px){
  .woocommerce #customer_login .bb-recaptcha .g-recaptcha{transform:scale(.88)}
  .woocommerce-lost-password .bb-recaptcha .g-recaptcha{transform:scale(.88);transform-origin:0 50%}
}
@media (max-width:345px){
  .woocommerce #customer_login .bb-recaptcha .g-recaptcha{transform:scale(.74)}
  .woocommerce-lost-password .bb-recaptcha .g-recaptcha{transform:scale(.74);transform-origin:0 50%}
}

.woocommerce #customer_login .col-1,
.woocommerce #customer_login .col-2{padding:clamp(24px,3.2vw,44px)}
.woocommerce #customer_login .col-2{border-left:1px solid var(--hair)}
/*
 * That hairline belongs to the column on its LEFT. With no column on its left it is just a
 * line down the inside edge of the panel, so on the register view it goes.
 */
.woocommerce #customer_login.bb-register-only .col-2{border-left:0}

.woocommerce #customer_login .woocommerce-form-login,
.woocommerce #customer_login .woocommerce-form-register{
  background:transparent;border:0;border-radius:0;padding:0;margin:0;
}

.woocommerce #customer_login h2{
  font-family:'Poppins',system-ui,sans-serif;font-weight:500;color:var(--green);
  font-size:clamp(1.12rem,1.6vw,1.35rem);line-height:1.2;margin:0 0 clamp(18px,2.2vw,26px);
}

/*
 * The register column is centred, as in the reference: it is a short piece of persuasion rather
 * than a form to work down. The email FIELD keeps its left-aligned label — centring a field label
 * over a full-width input reads as a mistake, and this one still has to be filled in.
 */
.woocommerce #customer_login .col-2 h2{text-align:center}
.woocommerce #customer_login .register-lead{
  font-size:.85rem;line-height:1.8;color:var(--muted);text-align:center;
  margin:0 0 clamp(20px,2.4vw,28px);
}
.woocommerce #customer_login .col-2 .woocommerce-privacy-policy-text p,
.woocommerce #customer_login .col-2 form.register > p:not(.form-row){
  font-size:.78rem;line-height:1.75;color:var(--muted);text-align:center;margin:0 0 16px;
}

/*
 * Both buttons full width. The reference has the login button full width and the register button
 * auto-width and centred, which reads as a primary and a secondary action — but on this page the
 * two are equally the point, and every other CTA on this shop (add to cart, proceed to checkout,
 * place order) is full width. Consistency wins over copying the asymmetry.
 */
.woocommerce #customer_login .woocommerce-form-login__submit,
.woocommerce #customer_login .woocommerce-form-register__submit,
.woocommerce #customer_login .col-2 button.button{width:100%}

/*
 * "Remember me" on the left, "Lost your password?" on the right, on one line under the button —
 * the reference's last row.
 *
 * WooCommerce puts the checkbox and the submit button inside the SAME <p class="form-row">, and
 * "Lost your password?" in a <p> after the form. So the button is pulled to the top of that row
 * with `order`, and the lost-password paragraph is taken out of flow and pinned to the bottom
 * right of the form. Out of flow, it no longer contributes height, so the form's bottom edge IS
 * the checkbox row's bottom edge and `bottom: 0` lands the two on the same line at any font size.
 * A negative margin would have done it too, and would have needed re-tuning every time the type
 * scale moved.
 */
.woocommerce #customer_login .woocommerce-form-login{position:relative}
/*
 * :not(.form-row-wide) matters. WooCommerce's field rows carry BOTH `form-row` and
 * `form-row-wide`, so a bare `p.form-row` catches them too — and flexing them put each label
 * beside its input. The text inputs are width:100% so they wrapped and looked fine; the password
 * input is wrapped in a <span> by Woo's own script, which has no width of its own, so it collapsed
 * to the width of the Show button and the password field vanished. Only the row holding the
 * checkbox and the submit button is `form-row` alone.
 */
.woocommerce #customer_login .woocommerce-form-login p.form-row:not(.form-row-wide){
  display:flex;flex-wrap:wrap;align-items:center;gap:12px;margin:0;
}
.woocommerce #customer_login .woocommerce-form-login p.form-row:not(.form-row-wide) .woocommerce-form-login__submit{
  order:-1;
}
/* And the wrapper gets a width regardless, so nothing can shrink it again. */
.woocommerce .password-input{width:100%}
.woocommerce #customer_login .woocommerce-form-login__rememberme{
  margin:0;font-size:.78rem;letter-spacing:0;text-transform:none;color:var(--muted);
}
.woocommerce #customer_login p.lost_password{
  position:absolute;right:0;bottom:0;margin:0;
  font-size:.78rem;line-height:inherit;
}
.woocommerce #customer_login p.lost_password a{color:var(--muted);text-decoration:underline;text-underline-offset:3px}
.woocommerce #customer_login p.lost_password a:hover{color:var(--green)}

/*
 * The Lost Password page reuses .woocommerce-form-login's panel, and has no rememberme row for the
 * absolute positioning above to sit against. It gets the panel treatment back.
 */
.woocommerce-lost-password .woocommerce form.woocommerce-ResetPassword{
  background:var(--sand-hi);border:1px solid var(--hair);border-radius:6px;
  padding:clamp(24px,3.2vw,44px);max-width:560px;
}
.woocommerce-lost-password .woocommerce form.woocommerce-ResetPassword .button{width:100%}

/* ---- order received / order details ---- */

/*
 * Redesigned 7 Sep 2026. Before, this page was a full-1560px-wide table with two columns of data —
 * the product on the left and its total 630px away — a loose inline row of order facts, and two
 * address boxes stacked down the page. It is a confirmation, read once, so it now reads as a
 * narrow column: the four facts in a panel, the order as line-item rows in the same treatment as
 * the cart and checkout, the addresses side by side, and a closing thank-you.
 *
 * ⚑ Constrained by MEASURE, not by the container. `.page-body .woocommerce` is inside page.php's
 * 1560px wrap, and a confirmation does not want that width — 900px keeps the label and its value
 * in the same glance. The closing section is deliberately inside it too: centred copy at 900px
 * reads as a considered ending, and full-bleed would need markup this page's template does not give.
 */
.woocommerce-order-received .page-body .woocommerce{max-width:900px}
/*
 * And that column is CENTRED in the page, asked for on 17 Sep 2026. At 1920 the 1560px wrap left
 * the 900px of content hugging its left padding edge with 590px of empty cream to the right, which
 * reads as an unfinished page rather than a narrow one.
 *
 * ⚑ THE WRAP IS NARROWED, NOT THE CONTENT INSIDE IT. page.php puts the <h1>, the gold rule and
 * .page-body one after another inside a single `.wrap`, so centring `.woocommerce` alone would
 * have slid the panel into the middle and left the title and its rule behind at the old left edge.
 * Narrowing the wrap moves all three together and every left edge still lines up.
 *
 * `main >` is load-bearing: the header and the footer use `.wrap` too, and an unscoped rule would
 * shrink both. The `+ var(--pad) * 2` adds back the wrap's own horizontal padding, so the CONTENT
 * column is still the 900px measure above rather than 900 minus 136.
 */
.woocommerce-order-received main > .wrap{max-width:calc(900px + var(--pad) * 2)}

/* ---- the four facts ---- */
/*
 * A panel rather than a rule-under-a-row. Auto-fit rather than a fixed four columns: WooCommerce
 * drops the Email row for a logged-in buyer and the Payment method row for a free order, so the
 * count is between three and five and a fixed grid leaves a hole in it.
 */
.woocommerce ul.order_details{
  list-style:none;margin:0 0 clamp(26px,3vw,38px);
  display:grid;grid-template-columns:repeat(auto-fit,minmax(150px,1fr));
  gap:clamp(16px,2vw,26px);
  background:var(--sand-hi);border:1px solid var(--hair);border-radius:6px;
  padding:clamp(20px,2.4vw,28px);
}
.woocommerce ul.order_details li{
  margin:0;padding:0;border:0;float:none;
  font-size:.62rem;letter-spacing:.18em;text-transform:uppercase;color:var(--muted);line-height:1.6;
}
.woocommerce ul.order_details li strong{
  display:block;margin-top:7px;
  font-size:1rem;font-weight:400;letter-spacing:.02em;text-transform:none;color:var(--ink);
}
/* The total is the number anyone actually looks for. */
.woocommerce ul.order_details li.total strong{font-size:1.12rem;color:var(--green)}

/*
 * ⚑ AN EMAIL IS ONE UNBREAKABLE TOKEN AND THE CELL IS 191px WIDE. Measured on the client's own
 * order rather than guessed: `keith@pixelmechanics.com.sg` inked 251px inside a 191px box, so it
 * spilled 60px - across the 26px gap and 34px into the TOTAL cell, printing "S$380" on top of
 * ".com.sg". Reported 17 Sep 2026. Nothing clipped it, because a grid item overflows visibly.
 *
 * Wrapping rather than a wider column, because the length is the customer's to choose and a
 * 40-character address would re-break whatever width were picked here. `anywhere` rather than
 * `break-word` so it may break INSIDE the token, which on an address is the only place to break.
 */
.woocommerce ul.order_details li strong{overflow-wrap:anywhere}

.woocommerce .woocommerce-thankyou-order-received{
  font-size:clamp(.95rem,1.2vw,1.05rem);color:var(--muted);margin:0 0 clamp(22px,2.6vw,32px);
}

/* ---- headings ---- */
/*
 * woocommerce-general gave each of these a near-white block and 20px of padding, which read as
 * grey banners down an otherwise cream page. That stylesheet is gone now, but the reset stays: it
 * costs nothing and it documents what the markup would otherwise do under any Woo CSS.
 */
.woocommerce .woocommerce-order-details__title,
.woocommerce .woocommerce-column__title,
.woocommerce .woocommerce-customer-details h2{
  background:transparent;padding:0;margin:0 0 16px;
  font-family:'Poppins',system-ui,sans-serif;font-weight:500;font-size:1.12rem;color:var(--green);
}
.woocommerce .woocommerce-order-details{margin:0 0 clamp(26px,3vw,40px)}

/* ---- the order, as line-item rows ---- */
/*
 * Same treatment as the checkout panel — bold labels, bold amounts — so the two screens agree. The
 * table is left a table here rather than becoming a grid: it is two columns of text with no
 * thumbnail and nothing to reorder, so there is nothing a grid would buy.
 */
.woocommerce table.order_details{width:100%;margin:0}
.woocommerce table.order_details thead th{
  font-weight:700;color:var(--ink);
  font-size:.62rem;letter-spacing:.18em;text-transform:uppercase;
  padding:0 0 12px;border-bottom:1px solid var(--hair);text-align:left;
}
.woocommerce table.order_details thead th.product-total{text-align:right}
.woocommerce table.order_details tbody td{
  padding:16px 0;border-bottom:1px solid var(--hair);
  font-size:.95rem;color:var(--ink);vertical-align:top;
}
.woocommerce table.order_details tbody td.product-name a{color:var(--green)}
/* The "× 2" is a <strong> beside the name; it is a quantity, not an emphasis. */
.woocommerce table.order_details tbody td.product-name .product-quantity{
  display:inline;margin-left:6px;font-weight:400;color:var(--muted);
}
.woocommerce table.order_details tbody td.product-total{text-align:right;white-space:nowrap}
.woocommerce table.order_details tfoot th,
.woocommerce table.order_details tfoot td{
  padding:10px 0;border:0;font-weight:700;
}
.woocommerce table.order_details tfoot th{
  width:auto;text-align:left;color:var(--ink);
  font-size:.62rem;letter-spacing:.18em;text-transform:uppercase;
}
.woocommerce table.order_details tfoot td{text-align:right;font-size:.95rem;color:var(--ink)}
/*
 * The last foot row is the total — but only because inc/woocommerce.php removes the Payment method
 * row from this table on the thank-you page. It was there, AFTER the total, so this emphasis landed
 * on "Direct bank transfer" and rendered it in 1.12rem green while the total sat quiet above it.
 * The rows carry no classes, and their count varies with shipping, discounts and fees, so neither
 * a selector nor a position could tell the total apart — removing the duplicate row was the fix,
 * and it also stops the page stating the payment method twice.
 */
.woocommerce table.order_details tfoot tr:last-child th,
.woocommerce table.order_details tfoot tr:last-child td{
  border-top:1px solid var(--hair);padding-top:14px;
}
.woocommerce table.order_details tfoot tr:last-child td{font-size:1.12rem;color:var(--green)}
/* The shipping method beside its price is context, not a number. */
.woocommerce table.order_details tfoot td small{font-weight:400;color:var(--muted)}

/* ---- addresses, side by side ---- */
/*
 * The grid goes on the INNER section, not on `.woocommerce-customer-details`. WooCommerce nests
 * them — `.woocommerce-customer-details > section.woocommerce-columns--addresses > .col-1 / .col-2`
 * — so gridding the outer element gave a two-column grid with exactly one child in it, and the
 * addresses stayed stacked at half width each.
 */
.woocommerce .woocommerce-customer-details .woocommerce-columns--addresses{
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:clamp(20px,3vw,44px);
}
.woocommerce .woocommerce-customer-details .woocommerce-column{width:auto;float:none}
.woocommerce .woocommerce-customer-details address{
  background:var(--sand-hi);border:1px solid var(--hair);border-radius:6px;
  padding:clamp(18px,2.2vw,24px);
  font-style:normal;font-size:.9rem;line-height:1.9;color:var(--ink);
}

/* ---- the closing thank-you ---- */
/* Rendered by bebold_thankyou_closing() on woocommerce_thankyou at priority 20, which is what puts
 * it below the order table AND the addresses. */
.order-thanks{
  margin:clamp(34px,4vw,56px) 0 0;padding:clamp(30px,3.6vw,48px) clamp(20px,3vw,40px);
  border-top:1px solid var(--hair);text-align:center;
}
.order-thanks .rule{margin:16px auto 0}
.order-thanks-lead{
  margin:clamp(18px,2.2vw,26px) 0 0;
  font-size:clamp(1.3rem,2.2vw,1.9rem);line-height:1.2;
}
.order-thanks-body{
  margin:14px auto 0;max-width:52ch;
  font-size:clamp(.88rem,1.05vw,.98rem);line-height:1.85;color:var(--muted);
}
.order-thanks-link{margin-top:clamp(18px,2.2vw,26px)}

/* ---- my account ---- */

/*
 * Redesigned 7 Sep 2026. Before, the whole account area was WooCommerce's default: a horizontal
 * strip of six endpoint links, then whatever the endpoint printed, at the page's full 1560px — an
 * orders table with the order number on the far left and its View button 1,100px away, address
 * blocks in the browser's default italic under browser-default bold <h3>s, and form fields 1,300px
 * wide. The dashboard's entire content was two sentences of boilerplate that duplicated the nav.
 *
 * ⚑ Scoped on `logged-in`, and that is not incidental: the SAME page is the login/register screen
 * when logged out. Without the guard, the grid below would place that screen's single panel into a
 * 190px navigation column.
 */
body.logged-in.woocommerce-account .page-body .woocommerce{
  display:grid;grid-template-columns:190px minmax(0,1fr);
  column-gap:clamp(26px,4vw,64px);row-gap:clamp(22px,3vw,34px);align-items:start;
}
/*
 * Nothing here names a grid-column or a grid-row. WooCommerce prints the nav first and the content
 * second, so auto-placement is correct already — which means the narrow-screen override is one
 * declaration and there is no row span to forget. The checkout is the cautionary tale: it placed
 * all three of its items explicitly, the narrow block reset only grid-column, and the panels
 * printed on top of the billing fields for days.
 */

/* ---- the nav rail ---- */
.woocommerce-MyAccount-navigation{position:sticky;top:calc(var(--hdr-h) + 24px)}
.woocommerce-MyAccount-navigation ul{
  list-style:none;margin:0;padding:0;border:0;
  display:grid;gap:2px;
}
.woocommerce-MyAccount-navigation li{margin:0;padding:0}
.woocommerce-MyAccount-navigation a{
  display:block;padding:10px 13px;border-radius:4px;
  font-size:.66rem;letter-spacing:.16em;text-transform:uppercase;color:var(--muted);
  transition:color .2s ease,background-color .2s ease;
}
.woocommerce-MyAccount-navigation a:hover{color:var(--green);background:var(--sand-hi)}
/*
 * The active row is marked with a gold edge as well as a fill. The fill alone reads as a hover
 * state, and on a page where one row is always active that ambiguity is worth two extra words of
 * CSS.
 */
.woocommerce-MyAccount-navigation li.is-active a{
  color:var(--green);background:var(--sand-hi);box-shadow:inset 2px 0 0 var(--gold);
}
/* Log out is an action, not a section, so it sits below a rule rather than in the list. */
.woocommerce-MyAccount-navigation li.woocommerce-MyAccount-navigation-link--customer-logout{
  margin-top:10px;padding-top:10px;border-top:1px solid var(--hair);
}

/* ---- the content column ---- */
/*
 * A measure, not a container. 840px is what keeps the orders table's five columns in one glance;
 * the forms are pulled in further below, because a first-name field 840px wide looks like a
 * mistake.
 */
body.logged-in.woocommerce-account .woocommerce-MyAccount-content{max-width:840px}
body.logged-in.woocommerce-account .woocommerce-MyAccount-content .woocommerce-address-fields,
body.logged-in.woocommerce-account .woocommerce-MyAccount-content form.edit-account{max-width:520px}

/* ---- the dashboard ---- */
/*
 * ⚑ WooCommerce's dashboard boilerplate, hidden. dashboard.php prints "Hello Aisha Rahman (not
 * Aisha Rahman? Log out)" and "From your account dashboard you can view your recent orders,
 * manage your shipping and billing addresses, and edit your password and account details." as body
 * text, then fires woocommerce_account_dashboard — so there is no hook to unregister and CSS is
 * the only route that does not involve copying the template in.
 *
 * Matched on the ABSENCE of a class rather than on nth-of-type. Both of WooCommerce's paragraphs
 * are unclassed and both of ours carry one, so this stays correct if WooCommerce ever changes how
 * many paragraphs it prints — where `:nth-of-type(1), :nth-of-type(2)` would start eating ours.
 *
 * Nothing is lost: the name is in the greeting below, and the three links are the three cards.
 */
body.woocommerce-dashboard .woocommerce-MyAccount-content > p:not([class]){display:none}

.acct-hello{margin:0;font-size:clamp(1.45rem,2.4vw,2.05rem);line-height:1.15}
.acct-lead{
  margin:10px 0 clamp(24px,3vw,36px);max-width:54ch;
  font-size:clamp(.9rem,1.1vw,1rem);line-height:1.8;color:var(--muted);
}

/*
 * The most recent order, which is the question someone opens an account area to answer. Rendered
 * by bebold_account_latest_order(), and absent entirely when there are no orders — a panel reading
 * "0 orders" is worse than no panel.
 */
.acct-latest{
  background:var(--sand-hi);border:1px solid var(--hair);border-radius:6px;
  padding:clamp(18px,2.2vw,26px);margin:0 0 clamp(20px,2.4vw,30px);
}
.acct-latest .eyebrow{margin:0 0 13px}
.acct-latest-row{display:flex;flex-wrap:wrap;align-items:center;gap:10px clamp(14px,2vw,24px)}
.acct-latest-no{
  font-family:'Poppins',system-ui,sans-serif;font-weight:500;font-size:1rem;color:var(--green);
}
.acct-latest-date{font-size:.86rem;color:var(--muted)}
.acct-latest-total{font-size:1rem;color:var(--ink)}
/* Pushed to the far edge, and it wraps to its own line before the row gets cramped. */
.acct-latest-link{margin-left:auto}

.acct-cards{
  display:grid;grid-template-columns:repeat(auto-fit,minmax(205px,1fr));
  gap:clamp(12px,1.6vw,18px);
}
.acct-card{
  display:flex;flex-direction:column;gap:8px;
  padding:clamp(17px,2.1vw,23px);
  background:var(--sand-hi);border:1px solid var(--hair);border-radius:6px;
  transition:border-color .25s ease,transform .25s ease;
}
.acct-card:hover{border-color:var(--gold-soft);transform:translateY(-2px)}
.acct-card-title{
  font-family:'Poppins',system-ui,sans-serif;font-weight:500;font-size:.98rem;color:var(--green);
}
.acct-card-body{font-size:.81rem;line-height:1.7;color:var(--muted)}
/* margin-top:auto keeps "Open" on the bottom edge whatever length the line above runs to. */
.acct-card-go{
  margin-top:auto;padding-top:6px;
  font-size:.6rem;letter-spacing:.18em;text-transform:uppercase;color:var(--green);
}

/* ---- the status pill ---- */
/*
 * The status is the only thing on an orders list anyone scans for, and WooCommerce printed it as
 * body text indistinguishable from the date beside it.
 *
 * Three tones, not seven statuses. The BASE is the in-flight tone, so pending, processing, on-hold
 * and any status a plugin adds later are all covered without being named — and only the two ends
 * of the range need a selector of their own.
 */
.order-pill{
  display:inline-block;padding:4px 11px 3px;border-radius:100px;border:1px solid;
  font-family:'Poppins',system-ui,sans-serif;font-weight:400;
  font-size:.58rem;letter-spacing:.14em;text-transform:uppercase;line-height:1.7;
  white-space:nowrap;
  color:var(--gold);border-color:var(--gold-soft);background:rgba(168,130,64,.09);
}
.order-pill[data-status="completed"]{
  color:var(--green);border-color:rgba(0,66,37,.28);background:rgba(0,66,37,.07);
}
.order-pill[data-status="cancelled"],
.order-pill[data-status="refunded"],
.order-pill[data-status="failed"]{
  color:var(--muted);border-color:var(--hair);background:transparent;
}

/* ---- the orders list ---- */
/*
 * Left as a table rather than turned into a grid, unlike the cart: five short columns with a header
 * row that earns its place, and nothing to reorder. What was wrong with it was the 1560px width,
 * which the measure above fixes on its own.
 */
.woocommerce table.account-orders-table{width:100%;margin:0;border:0;border-collapse:collapse}
.woocommerce table.account-orders-table thead th{
  padding:0 12px 12px 0;border-bottom:1px solid var(--hair);text-align:left;
  font-size:.62rem;letter-spacing:.18em;text-transform:uppercase;font-weight:700;color:var(--ink);
}
.woocommerce table.account-orders-table tbody th,
.woocommerce table.account-orders-table tbody td{
  padding:15px 12px;border-bottom:1px solid var(--hair);border-top:0;
  font-size:.92rem;font-weight:400;color:var(--ink);text-align:left;vertical-align:middle;
}
.woocommerce table.account-orders-table tbody th:first-child,
.woocommerce table.account-orders-table thead th:first-child{padding-left:0}
.woocommerce table.account-orders-table tbody td:last-child,
.woocommerce table.account-orders-table thead th:last-child{padding-right:0;text-align:right}
.woocommerce table.account-orders-table td.woocommerce-orders-table__cell-order-date{
  font-size:.86rem;color:var(--muted);white-space:nowrap;
}
.woocommerce table.account-orders-table th.woocommerce-orders-table__cell-order-number a{
  color:var(--green);
}
.woocommerce table.account-orders-table td.woocommerce-orders-table__cell-order-total{
  white-space:nowrap;
}
/*
 * Three filled green buttons stacked down a short list read as three calls to action on a page
 * whose job is to be looked at. Outlined and small, they read as what they are.
 */
.woocommerce table.account-orders-table td.woocommerce-orders-table__cell-order-actions a.button{
  display:inline-block;width:auto;margin:0 0 0 6px;padding:7px 15px;
  background:transparent;color:var(--green);border:1px solid rgba(0,66,37,.3);border-radius:3px;
  font-size:.6rem;letter-spacing:.16em;text-transform:uppercase;
}
.woocommerce table.account-orders-table td.woocommerce-orders-table__cell-order-actions a.button:hover{
  background:var(--green);color:var(--btn-fg);border-color:var(--green);
}

/* ---- view order ---- */
/*
 * ⚑ The sentence this page opens on — "Order #104 was placed on September 7, 2026 and is currently
 * Completed." — is HIDDEN, not restyled. WooCommerce wraps all three of those values in <mark>
 * elements, which the browser paints on highlighter yellow, and it states exactly the first three
 * facts of the panel that now sits above it. It is printed inline by view-order.php with no hook
 * around it, so CSS is the only way to drop it without copying the template in — and the reason
 * this is honest rather than crude is that nothing is lost: every fact it carried is stated above,
 * plus the total, which it never gave.
 */
body.woocommerce-view-order .woocommerce-MyAccount-content > p:first-of-type{display:none}
.acct-order-facts{margin:0 0 clamp(20px,2.4vw,28px)}
/* The pill is the value here, so the <strong> wrapper must not add a second weight to it. */
.acct-order-facts li.status strong{font-weight:400;line-height:1}
/*
 * Belt and braces on the yellow, in case any other screen prints one of these marks — the account
 * area is the only place in this build that does, but the default is loud enough to be worth a
 * blanket rule.
 */
.woocommerce mark.order-number,
.woocommerce mark.order-date,
.woocommerce mark.order-status{background:transparent;color:inherit;font-weight:inherit}

/*
 * ⚑ Scoping fix, not new work. `table.order_details tfoot tr:last-child td` sets the loudest number
 * on the page, and it was written for the thank-you screen where the last total row IS the total.
 * On View order the payment-method row is NOT removed — this is the only screen that states it —
 * so it is the last row, and it was rendering in 1.12rem green while the total sat quiet above.
 *
 * Selected by the ABSENCE of an .amount, not by position. Every other foot row states a number and
 * keeps its emphasis; the one row that states a METHOD stops being set like a number. Position is
 * what went wrong in the first place — "last row" is not "total", and it is not "payment method"
 * either once a discount or a fee is added.
 *
 * Naming both classes (`table.shop_table.order_details`) rather than relying on file order: against
 * the generic rule this would otherwise be an exact specificity tie, and this stylesheet has now
 * produced four of those.
 */
.woocommerce-view-order table.shop_table.order_details tfoot tr:not(:has(.amount)) th,
.woocommerce-view-order table.shop_table.order_details tfoot tr:not(:has(.amount)) td{
  font-size:.86rem;font-weight:400;color:var(--muted);
}

/* ---- addresses ---- */
.woocommerce-account .woocommerce-Addresses{
  display:grid;grid-template-columns:repeat(2,minmax(0,1fr));gap:clamp(16px,2.4vw,28px);
}
.woocommerce-account .woocommerce-Address{width:auto;float:none}
.woocommerce-account .woocommerce-Address-title{
  display:flex;align-items:baseline;justify-content:space-between;gap:12px;margin:0 0 12px;
}
.woocommerce-account .woocommerce-Address-title h2,
.woocommerce-account .woocommerce-Address-title h3{
  margin:0;background:transparent;padding:0;
  font-family:'Poppins',system-ui,sans-serif;font-weight:500;font-size:1rem;color:var(--green);
}
/* WooCommerce's edit link is a bare sentence ("Edit Billing address"); it reads as a control. */
.woocommerce-account .woocommerce-Address-title a{
  flex:none;font-size:.6rem;letter-spacing:.16em;text-transform:uppercase;color:var(--gold);
}
.woocommerce-account .woocommerce-Address-title a:hover{color:var(--green)}
/*
 * font-style:normal is the load-bearing declaration: <address> is italic by the browser's own
 * default, which is why these blocks were rendering in italic on a page with no italic anywhere
 * else. The thank-you page's addresses already carried this; it was never written for these.
 */
.woocommerce-account .woocommerce-Address address{
  margin:0;background:var(--sand-hi);border:1px solid var(--hair);border-radius:6px;
  padding:clamp(16px,2vw,22px);
  font-style:normal;font-size:.9rem;line-height:1.9;color:var(--ink);
}

/* ---- account details ---- */
/*
 * The password box was a raw <fieldset>: the browser's notched 2px groove border with the legend
 * cut into it, which is the one piece of default chrome on this whole site that still looked like
 * 1998. It becomes a panel, and the legend becomes an eyebrow.
 */
.woocommerce-account form.edit-account fieldset{
  margin:clamp(18px,2.2vw,26px) 0 0;padding:clamp(18px,2.2vw,24px);
  background:var(--sand-hi);border:1px solid var(--hair);border-radius:6px;
}
.woocommerce-account form.edit-account fieldset legend{
  float:left;width:100%;padding:0 0 12px;margin:0 0 18px;
  border-bottom:1px solid var(--hair);
  font-size:.62rem;letter-spacing:.18em;text-transform:uppercase;color:var(--green);
}
.woocommerce-account form.edit-account fieldset .form-row:last-child{margin-bottom:0}
/* WooCommerce's field hints are <span class="description"> and italic by nothing but habit. */
.woocommerce-account form.edit-account .form-row > span em{
  display:block;margin-top:7px;
  font-style:normal;font-size:.76rem;line-height:1.7;color:var(--muted);
}
.woocommerce-account form.edit-account .woocommerce-Button{margin-top:clamp(18px,2.2vw,26px)}

/* ---- the introductory line on Addresses ---- */
/*
 * ⚑ Scoped to the ENDPOINT, not to `.woocommerce-account`. Written the broad way it was a
 * specificity TIE with the view-order hide rule above — and, worse, it also matched the dashboard,
 * where the first <p> in the content is the greeting: the greeting would have been set in .88rem
 * muted, because three classes beat .acct-hello's one.
 */
body.woocommerce-edit-address .woocommerce-MyAccount-content > p:first-of-type{
  margin:0 0 clamp(16px,2vw,22px);font-size:.88rem;line-height:1.8;color:var(--muted);
}

.woocommerce-pagination ul.page-numbers{
  list-style:none;display:flex;justify-content:center;gap:8px;margin:clamp(26px,3vw,40px) 0 0;padding:0;border:0;
}
.woocommerce-pagination .page-numbers li .page-numbers{
  display:grid;place-items:center;width:38px;height:38px;border:1px solid var(--hair);border-radius:50%;
  font-size:.8rem;color:var(--green);background:transparent;
}
.woocommerce-pagination .page-numbers li .page-numbers.current{background:var(--green);color:var(--cream);border-color:var(--green)}

/* ============ component internals WooCommerce's stylesheet used to provide ============ */

/*
 * `woocommerce-general` is dequeued — see inc/woocommerce.php for why, at length. This section is
 * everything that was genuinely load-bearing in it, and nothing else.
 *
 * HOW THIS LIST WAS PRODUCED, and how to re-run it after a WooCommerce upgrade. Not by reading
 * 92KB of CSS: by diffing computed styles with the stylesheet toggled off, on every element of all
 * five shop screens, pseudo-elements included.
 *
 *   const fp = () => { …for each element under .woo / .page-body, record ~35 computed properties
 *                      plus ::before/::after where content !== 'none'… };
 *   const before = fp();
 *   document.querySelector('link[href*="woocommerce/assets/css/woocommerce.css"]').disabled = true;
 *   // …wait a frame, then fp() again and diff, grouping by class + property
 *
 * Two classes of difference are noise and must be filtered or the signal drowns: `text-align:left`
 * becoming `start` (identical in LTR), and `line-height` shifting to the theme's own — WooCommerce
 * sets line-height on nearly every form row and table cell. Everything left over is below.
 *
 * The shop ARCHIVE reported zero differences, because dropping the `products` class from the grid
 * container had already made it immune. The single product page reported almost none, and two of
 * the changes it did report were improvements: WooCommerce's `div.product p.price{font-size:1.25em}`
 * had been inflating the related products' card prices, since `div.product` wraps the whole page.
 *
 * NOT REPLACED, deliberately:
 *   - the WooCommerce icon font and every glyph hung off it. The notice icons were already hidden,
 *     and the truck beside "Calculate shipping" is not this brand's.
 *   - `.blockUI.blockOverlay`'s spinner, which is a background-image inside the plugin directory.
 *     jQuery blockUI still dims the cart inline while it updates, which is feedback enough.
 *   - clearfix ::before/::after on a dozen containers. Nothing in this theme floats.
 */

/* ---- the remove × in the cart ---- */
/*
 * ⚑ THIS CONTROL WAS INVISIBLE FOR MOST OF THE BUILD, and nobody noticed because it is one
 * character wide.
 *
 * Astra emptied the link and drew its own glyph from the stylesheet — the stylesheet this theme
 * dequeues. So the cart shipped an empty 20px box that removed the line when clicked, and the
 * sizing here had nothing to size. Dropping Astra on 19 Sep 2026 put WooCommerce's own `&times;`
 * back inside the link, which is what these rules were always written for.
 *
 * The design's × is discreet and turns green; WooCommerce's own is red, bold and circular.
 */
.woocommerce a.remove{
  display:block;width:1em;height:1em;line-height:1;text-align:center;
  font-size:20px;font-weight:400;color:var(--muted);text-decoration:none;border:0;
}
.woocommerce a.remove:hover{color:var(--green);background:transparent}

/* ---- form controls ---- */
/*
 * The .form-row block above only reaches inputs inside a form row. These do not have one: the
 * coupon field in the cart, and the account pages' own inputs. Without this they fall back to the
 * browser's default control — a 2px inset border and 2px of padding.
 */
.woocommerce input.input-text,
.woocommerce textarea,
.woocommerce .woocommerce-Input--text{
  display:block;background:var(--cream);color:var(--ink);
  border:1px solid var(--hair);border-radius:3px;
  padding:13px 15px;font-size:.9rem;line-height:1.4;
}
.woocommerce input.input-text:focus,
.woocommerce textarea:focus{border-color:var(--green);background:var(--sand-hi);outline:none}

/* ---- the password show/hide toggle ---- */
/*
 * ⚑ WooCommerce's own script INJECTS this button into every password field, and its stylesheet drew
 * it with an eye glyph from the WooCommerce icon font. With both gone it renders as a 16x6px grey
 * box under the field.
 *
 * This is the one thing the computed-style diff did not catch, and the reason is worth keeping: the
 * button does not exist when the page is snapshotted. It is added on DOM-ready by woocommerce.js,
 * after the probe has already walked the tree. Any audit of this kind is blind to script-injected
 * DOM unless it waits — the password toggle, the select2 box and the block overlay are all in that
 * category here.
 *
 * Rebuilt as a text control rather than a glyph, so it needs no icon font. The script toggles
 * `.display-password` on click, which is what swaps the label, and the <button> already carries
 * aria-label="Show password", so the ::after text is decoration over a properly labelled control.
 */
.woocommerce .password-input{display:block;position:relative}
.woocommerce .password-input input.input-text{padding-right:76px}
.woocommerce .show-password-input{
  position:absolute;right:0;top:50%;transform:translateY(-50%);
  width:auto;height:auto;margin:0;padding:0 15px;
  background:transparent;border:0;cursor:pointer;
  font-size:.6rem;letter-spacing:.16em;text-transform:uppercase;color:var(--muted);
}
.woocommerce .show-password-input:hover{color:var(--green)}
.woocommerce .show-password-input::after{content:"Show"}
.woocommerce .show-password-input.display-password::after{content:"Hide"}

/*
 * Radios and checkboxes sit against their labels rather than on the text baseline, and they are
 * the brand's green rather than the browser's blue. `accent-color` recolours the native control
 * without replacing it — so the focus ring, the keyboard behaviour and the platform's own hit area
 * all stay exactly as the OS draws them, which a hand-built radio always loses something of.
 */
.woocommerce input.input-radio,
.woocommerce input.input-checkbox,
.woocommerce .woocommerce-form__input-checkbox{
  vertical-align:middle;margin:-2px 8px 0 0;
}
.woocommerce input[type="radio"],
.woocommerce input[type="checkbox"]{accent-color:var(--green)}

/* The required marker: gold, and bold enough to see at 300 weight. */
.woocommerce .required,
.woocommerce .required_field{color:var(--gold);font-weight:700;text-decoration:none;border:0}
.woocommerce .optional{color:var(--muted)}

/* ---- shipping method lists ---- */
/*
 * These are plain <ul>s inside a table cell. Unstyled they take the browser's disc bullets and
 * 40px of indent, which is the most visible single thing lost with the stylesheet.
 */
.woocommerce ul#shipping_method,
.woocommerce ul.woocommerce-shipping-methods{list-style:none;margin:0;padding:0}
.woocommerce ul#shipping_method li,
.woocommerce ul.woocommerce-shipping-methods li{list-style:none;margin:0 0 6px;padding:0}
.woocommerce ul#shipping_method li:last-child,
.woocommerce ul.woocommerce-shipping-methods li:last-child{margin-bottom:0}
.woocommerce ul#shipping_method label,
.woocommerce ul.woocommerce-shipping-methods label{display:inline;text-transform:none;letter-spacing:0;font-size:.85rem;color:var(--ink);margin:0}
.woocommerce .woocommerce-shipping-destination{margin:0 0 10px}
.woocommerce .shipping-calculator-button{display:inline-block;margin-top:8px}

/* ---- totals ---- */
/* WooCommerce bolded these to 700 and 900; the design's emphasis is 500. */
.woocommerce table.shop_table tfoot .amount,
.woocommerce table.shop_table tfoot strong,
.woocommerce .cart_totals .order-total .amount{font-weight:500}

/* ---- checkout: the payment box and its arrow ---- */
/*
 * The arrow is a bordered pseudo-element pointing at the selected payment method. Its geometry came
 * from WooCommerce; only its colour was ours, which is why the previous rule here looked odd on its
 * own — a `border-bottom-color` with no border to colour.
 */
.woocommerce-checkout #payment div.payment_box{
  position:relative;margin:12px 0 0;padding:14px 16px;
  background:var(--cream);border-radius:3px;font-size:.84rem;line-height:1.7;color:var(--muted);
}
.woocommerce-checkout #payment div.payment_box::before{
  content:"";display:block;position:absolute;top:-9px;left:16px;
  width:0;height:0;border:9px solid transparent;border-top-width:0;
  border-bottom-color:var(--cream);
}
.woocommerce-checkout #payment div.payment_box p:last-child{margin-bottom:0}

/* ---- cart: spacing WooCommerce supplied ---- */
.woocommerce .wc-proceed-to-checkout{padding:14px 0 0}
.woocommerce-form-login,
.woocommerce-form-register,
.woocommerce-form-coupon{margin:0 0 clamp(20px,2.4vw,30px)}
.woocommerce .woocommerce-form__label-for-checkbox{display:inline-block}

/* select2's own arrow, now that nothing else recolours it. */
.woocommerce .select2-container--default .select2-selection--single .select2-selection__arrow b{
  border-top-color:var(--muted);
}

/*
 * ⚑ Base rules go ABOVE this banner. Appended after it they out-order these media queries at equal
 * specificity and silently kill the responsive layout — the same trap documented in shell.css.
 */

/* ============ responsive ============ */

@media(max-width:1024px){
  /* One column: the gallery is already first in the DOM, so it simply stacks above the summary. */
  .prod-top{grid-template-columns:1fr;row-gap:clamp(24px,3vw,36px)}
  .woo--single .summary{position:static;max-width:760px;margin:0 auto}

  /*
   * Stacked, the gallery becomes a horizontal snap strip and the rail and arrows go: a rail beside
   * a stage needs width this layout no longer has, and a thumbnail rail is a poor control on a
   * touch screen where swiping the image itself is the obvious gesture.
   *
   * The alternative was one slide at a time at full width, which is 469px tall on a phone — so the
   * eight would put 3,700px of photography between the customer and the price. The strip puts the
   * whole shoot in one screenful of height.
   *
   * It bleeds past the page padding so the frames run to the window edge, with scroll-padding so
   * the first one still lines up with the copy above it. Slides are the smaller of 80% and 420px,
   * so a phone shows one frame with the next peeking in and a tablet shows a filmstrip — that peek
   * IS the affordance for swiping, which is also why the strip keeps a thin scrollbar rather than
   * hiding it: a scroll container with no visible scrollbar is not reliably keyboard-operable.
   *
   * The snapping and the swipe are the browser's. site.js's gallery controller does nothing here.
   */
  .prod-gallery{display:block;margin:0 calc(var(--pad) * -1)}
  .prod-gallery.pg--ready{padding-left:0}
  .pg--ready .prod-rail,
  .pg--ready .prod-nav{display:none}
  .prod-stage,
  .pg--ready .prod-stage{
    display:flex;gap:0;overflow-x:auto;scroll-snap-type:x mandatory;
    scrollbar-width:thin;-webkit-overflow-scrolling:touch;
    padding:0 var(--pad);scroll-padding-left:var(--pad);
  }
  .prod-slide,
  .pg--ready .prod-slide,
  .pg--ready .prod-slide.is-active{
    display:block;flex:0 0 min(80%,420px);scroll-snap-align:start;border-radius:0;
  }
  /* The cart page's two columns stack, and the summary stops sticking. */
  .woocommerce-cart .page-body .woocommerce{grid-template-columns:1fr;row-gap:clamp(26px,3vw,40px)}
  .woocommerce-cart form.woocommerce-cart-form,
  .woocommerce-cart .cart-collaterals{grid-column:1}
  .woocommerce-cart .cart_totals{position:static}

  /*
   * The checkout's two columns stack. The ROW assignments have to go with the column ones, not just
   * the columns: #customer_details spans rows 1-2 so the summary can sit beside it, and in a single
   * column that span still stood — so "Your order" (row 1) printed on top of "Billing details" and
   * the whole summary panel (row 2) sat over the billing fields. Only ever visible below 1024px,
   * which is why 1440-only verification missed it. `grid-row:auto` puts all three back in source
   * order.
   */
  .woocommerce-checkout .woocommerce form.checkout{grid-template-columns:1fr;grid-template-rows:auto}
  .woocommerce-checkout .woocommerce form.checkout #customer_details,
  .woocommerce-checkout .woocommerce form.checkout #order_review_heading,
  .woocommerce-checkout .woocommerce form.checkout #order_review{grid-column:1;grid-row:auto}

  /*
   * The account area stacks: nav above content. ONE declaration, because nothing in that grid names
   * a column or a row — see the note beside it.
   */
  /*
   * ⚑ minmax(0,1fr), NOT 1fr. `1fr` is shorthand for `minmax(auto,1fr)`, and an `auto` minimum is
   * the content's min-content width — so the nav below, which is a horizontal scroller, sized the
   * COLUMN to its own full row of items instead of scrolling inside it. The whole page went with
   * it: the cards and the order panel ran off the right edge at 560px, with no scrollbar to
   * explain why. A grid column that contains an overflow container always needs the explicit 0.
   */
  body.logged-in.woocommerce-account .page-body .woocommerce{grid-template-columns:minmax(0,1fr)}
  .woocommerce-MyAccount-navigation{position:static}
  .woocommerce-MyAccount-navigation ul{
    grid-auto-flow:column;grid-auto-columns:max-content;overflow-x:auto;
    gap:4px;padding-bottom:4px;
  }
  .woocommerce-MyAccount-navigation li.woocommerce-MyAccount-navigation-link--customer-logout{
    margin:0 0 0 8px;padding:0 0 0 8px;border-top:0;border-left:1px solid var(--hair);
  }
  .woocommerce-MyAccount-navigation li.is-active a{box-shadow:inset 0 -2px 0 var(--gold)}
  .woocommerce .cart-collaterals{grid-template-columns:1fr}
  .woocommerce .cart-collaterals .cart_totals{grid-column:1}
}

/*
 * ⚑ TABLET GETS ITS OWN SLIDE WIDTH, because `min(80%,420px)` lands between two designs there.
 *
 * The rule above is written for two outcomes: a phone showing one frame with the next peeking in,
 * and a tablet showing a filmstrip. At 390px it gives 280px — one frame plus a peek, correct. But
 * the 420px ceiling means a 834px tablet shows 1.8 frames: too many to read as one image, too few
 * to read as a strip. It looks like a two-up that has been cut off.
 *
 * A share of the width rather than a ceiling gives the same SHAPE at every tablet size — 2.3
 * frames, so the third is visibly clipped and the swipe is obvious. Verified at 834 and at 1024.
 */
@media(min-width:761px) and (max-width:1024px){
  .prod-slide,
  .pg--ready .prod-slide,
  .pg--ready .prod-slide.is-active{flex:0 0 44%}
}

@media(max-width:760px){
  /*
   * Confirmation page. The addresses stack — on the INNER section, which is where the grid is; the
   * override used to name `.woocommerce-customer-details` and did nothing once the grid moved.
   */
  .woocommerce .woocommerce-customer-details .woocommerce-columns--addresses{grid-template-columns:1fr}
  .order-thanks{padding-left:0;padding-right:0}

  /*
   * My account → Addresses stacks too. Two cards side by side survive 760px; at 560 the headings
   * were breaking mid-phrase ("Billing / address") next to their own edit link.
   */
  .woocommerce-account .woocommerce-Addresses{grid-template-columns:minmax(0,1fr)}

  /*
   * A notice's button drops below its message and goes full width, as every other button on the
   * shop does at this size. `order:2` is what keeps it BELOW rather than above — WooCommerce prints
   * it first in the markup — so the column direction alone is enough and the markup stays untouched.
   */
  .woocommerce-message:has(> .button),
  .woocommerce-info:has(> .button){flex-direction:column;align-items:stretch;gap:14px}
  .woocommerce-message > .button,
  .woocommerce-info > .button{margin-left:0;text-align:center}

  /*
   * And the order table stays a TABLE here. The generic stacked-row treatment further down turns
   * every shop_table row into a grid and labels each cell from `data-title` — which is right for
   * the cart, where a row is six cells wide, and wrong for this one: its cells carry no data-title,
   * so it lost its header row, gained a two-column grid it did not want, and drew a broken segment
   * of border under each cell. Two columns of text read fine at 500px as what they are.
   */
  /*
   * `table.shop_table.order_details` — both classes, deliberately. With one class this was a
   * SPECIFICITY TIE against the generic `.woocommerce table.shop_table thead` below, and since
   * these rules sit earlier in the file the generic ones took the cascade and nothing changed.
   * Naming the second class wins it outright instead of depending on where in the file this lands.
   */
  .woocommerce table.shop_table.order_details thead{display:table-header-group}
  .woocommerce table.shop_table.order_details tr{display:table-row}
  .woocommerce table.shop_table.order_details th,
  .woocommerce table.shop_table.order_details td{display:table-cell}
  .woocommerce table.shop_table.order_details td::before{content:none}

  /*
   * Log in / register stacks, and the divider moves from the left edge of the second column to
   * its top edge — otherwise it draws a line down nothing.
   */
  .woocommerce #customer_login.col2-set{grid-template-columns:1fr}
  .woocommerce #customer_login .col-2{border-left:0;border-top:1px solid var(--hair)}
  .woo-grid,
  .woo--single .related .woo-grid{grid-template-columns:1fr}
  /*
   * The card footer's stacked layout MOVED to shell.css (14 Sep 2026), into its own
   * @media(max-width:760px) block. It lived here and so only ever reached shop screens, while the
   * homepage's Featured Collection band renders the same .card and kept the row — see the note
   * there. Nothing replaces it here; shell.css loads on the shop too, and no rule in this file
   * touches .card-info's direction or the button's width, so the cascade lands the same way.
   */

  /*
   * woocommerce-smallscreen is dropped, so the cart table's stacked-row treatment is rebuilt here:
   * each row becomes a card and every cell carries its own label from data-title.
   */
  /*
   * The cart page's item rows re-stack. The desktop rules for them are scoped
   * `.woocommerce-cart table.shop_table.cart tr.cart_item`, which out-specifies the generic
   * stacked-row treatment below — so they are undone explicitly rather than left to lose a
   * specificity contest they would win.
   */
  .woocommerce-cart table.shop_table.cart tr.cart_item{
    grid-template-columns:80px minmax(0,1fr) auto;
    column-gap:14px;row-gap:6px;align-items:start;
  }
  .woocommerce-cart table.shop_table.cart td.product-thumbnail{grid-row:1 / span 3}
  .woocommerce-cart table.shop_table.cart td.product-thumbnail img{width:80px;height:80px}
  .woocommerce-cart table.shop_table.cart td.product-name{grid-column:2;grid-row:1;align-self:start}
  .woocommerce-cart table.shop_table.cart td.product-price{grid-column:2;grid-row:2;margin-top:0}
  .woocommerce-cart table.shop_table.cart td.product-quantity{grid-column:2;grid-row:3;margin-top:6px}
  /* The × is a small action, so it goes to the top corner; the line total sits with the quantity
   * it belongs to, at the foot of the row. */
  .woocommerce-cart table.shop_table.cart td.product-remove{grid-column:3;grid-row:1;justify-self:end}
  .woocommerce-cart table.shop_table.cart td.product-subtotal{
    grid-column:3;grid-row:3;justify-self:end;align-self:center;
  }
  /* "PRICE S$280 each" says it twice. The unit price keeps the word and drops the label; every
   * other cell keeps its label, which is what makes a stacked row readable. */
  .woocommerce-cart table.shop_table.cart td.product-price::before{content:none}
  .woocommerce-cart table.shop_table.cart td.actions{flex-direction:column;align-items:stretch}
  .woocommerce-cart table.shop_table.cart td.actions .coupon{flex-wrap:wrap}
  .woocommerce-cart table.shop_table.cart td.actions .coupon .input-text{width:100%}
  .woocommerce-cart table.shop_table.cart td.actions .button{width:100%}

  /*
   * The orders list opts OUT of the generic two-column stacked treatment. Two columns is right for
   * a cart row, where the pairs are thumbnail/name and quantity/subtotal; here it paired STATUS
   * with TOTAL and left ACTIONS beside an empty cell, with a segment of hairline under each half.
   * One cell per line, with the order number as the row's heading.
   *
   * Both classes named so this out-specifies the generic rule outright rather than by file order.
   */
  .woocommerce table.shop_table.account-orders-table tr{
    grid-template-columns:minmax(0,1fr);gap:2px;padding:18px 0;
  }
  .woocommerce table.shop_table.account-orders-table th,
  .woocommerce table.shop_table.account-orders-table td{
    display:block;padding:2px 0;border:0;text-align:left;
  }
  .woocommerce table.shop_table.account-orders-table th.woocommerce-orders-table__cell-order-number{
    margin-bottom:5px;font-size:1rem;
  }
  .woocommerce table.shop_table.account-orders-table td.woocommerce-orders-table__cell-order-actions{
    margin-top:11px;
  }
  .woocommerce table.shop_table.account-orders-table td.woocommerce-orders-table__cell-order-actions::before{
    content:none;
  }
  /*
   * The action stays on the right, which is where a stacked row wants its one button. Stated
   * explicitly with an extra type selector: the blanket `text-align:left` above LOSES to the
   * desktop `tbody td:last-child{text-align:right}` rule (equal classes, one more type), so this
   * was already rendering correctly — by accident. Accidents of specificity do not survive edits.
   */
  .woocommerce table.shop_table.account-orders-table tbody tr td.woocommerce-orders-table__cell-order-actions{
    text-align:right;
  }
  .woocommerce table.shop_table.account-orders-table td.woocommerce-orders-table__cell-order-actions a.button{
    margin-left:0;
  }

  .woocommerce table.shop_table thead{display:none}
  .woocommerce table.shop_table tr{
    display:grid;grid-template-columns:auto 1fr;gap:4px 14px;align-items:center;
    padding:16px 0;border-bottom:1px solid var(--hair);
  }
  .woocommerce table.shop_table td{display:block;padding:2px 0;border:0}
  .woocommerce table.shop_table td.product-thumbnail{grid-row:1 / span 4;width:80px}
  .woocommerce table.shop_table td::before{
    content:attr(data-title) " ";
    font-size:.6rem;letter-spacing:.16em;text-transform:uppercase;color:var(--muted);margin-right:8px;
  }
  .woocommerce table.shop_table td.product-thumbnail::before,
  .woocommerce table.shop_table td.product-name::before,
  .woocommerce table.shop_table td.product-remove::before{content:none}
  .woocommerce table.shop_table tfoot tr,
  .woocommerce .cart_totals table.shop_table tr{grid-template-columns:1fr auto}
  .woocommerce .cart_totals table.shop_table td::before{content:none}

  .woocommerce .woocommerce-customer-details{grid-template-columns:1fr}
  .woocommerce .cart .actions{text-align:left}
  .woocommerce .cart .actions .coupon{float:none;flex-wrap:wrap}
  .woocommerce .cart .actions .button{width:100%;margin-top:10px}
  .woocommerce .cart .actions .coupon .input-text{width:100%}
}

/* ============================================================================================
 * The product summary, in the order the client asked for on 10 Sep 2026
 * ========================================================================================== */

/*
 * "Inspired by Curry Puff", under the price.
 *
 * Takes over the slot — and very nearly the type — that
 * .woocommerce-product-details__short-description used to hold. That block's rules were removed
 * with it: the short description is no longer rendered here at all, it is the opening of "The
 * Inspiration" panel instead, so the rules had nothing left to style.
 */
.woo--single .summary .prod-sub{
  font-size:clamp(.88rem,1.05vw,.98rem);line-height:1.8;color:var(--muted);margin:18px 0 0;
}

/* ---- colour, and the swatch grid ---- */
.prod-colour{margin:clamp(20px,2.4vw,26px) 0 0}
.prod-colour-label{
  font-size:.66rem;letter-spacing:.16em;text-transform:uppercase;color:var(--muted);margin:0 0 12px;
}
/* The colour's own name is the informative half, so it is the half that is not shouted. */
.prod-colour-value{
  display:inline;margin-left:6px;letter-spacing:.02em;text-transform:none;
  font-size:.82rem;color:var(--ink);
}
.prod-colour-grid{list-style:none;margin:0;padding:0;display:flex;flex-wrap:wrap;gap:10px}
/*
 * The chip is a 1px hairline box with the bag inside it, and the CURRENT colour is the one with a
 * dark border — the pattern in the reference the client sent. The border is on the chip rather than
 * an outline so it takes part in layout: an outline would overlap its neighbours at this gap.
 *
 * A fixed 54px square, and the image is `contain` rather than `cover`, because these are 3:4
 * studio shots of a whole bag: covering a square would crop the handle off a 54px thumbnail, which
 * is the same mistake the card's 4:3 frame made at a larger size.
 */
.prod-colour-chip{
  display:block;width:54px;height:54px;padding:3px;background:var(--sand-hi);
  border:1px solid var(--hair);transition:border-color .25s ease;
}
.prod-colour-chip img{width:100%;height:100%;object-fit:contain;display:block}

/*
 * A SOLID COLOUR SWATCH, set on the colour term — see bebold_colour_swatch() in
 * inc/woocommerce.php. This is what a swatch is normally for, and the chip changes shape to suit
 * it: the 3px padding that gives a photograph room to breathe would read as a white ring around a
 * flat colour, so `--solid` drops it and the fill goes to the hairline border.
 *
 * `background` is set inline, because the value is client data and there is no stylesheet to put
 * an arbitrary hex in. It is the one inline style on this page, and bebold_sanitize_hex() will not
 * return anything that is not /^#[0-9A-F]{6}$/.
 */
.prod-colour-chip--solid{padding:0}
.prod-colour-fill{display:block;width:100%;height:100%}
/* The chips became <button>s when colourways became variations, so they need the reset a
   button always needs - and the hover rule had to stop being scoped to `a.`, which silently
   left them with no hover state at all. */
.prod-colour-chip{background:none;font:inherit;color:inherit;cursor:pointer;-webkit-appearance:none;appearance:none}
.prod-colour-chip:hover,
.prod-colour-chip:focus-visible{border-color:var(--green)}
/* One gallery per colourway, all rendered, the default one marked active by the SERVER so
   this works with JS blocked. A class rather than [hidden], which loses to author `display`
   rules - Gotcha 65, three times over. */
/*
 * GALLERY VIDEO. The slide is sized exactly like a photograph so the stage does not jump when the
 * customer moves between them, and `object-fit:contain` keeps a 16:9 clip whole inside a 3:4 frame
 * rather than cropping the product out of its own video.
 */
.prod-slide--video{display:flex;align-items:center;justify-content:center;background:var(--sand-hi)}
.prod-video{display:block;width:100%;height:100%;max-height:100%;object-fit:contain;background:#000}

/*
 * A play badge on the strip, because a poster image is indistinguishable from a photograph and the
 * customer would otherwise find the video by accident. Drawn as a CSS triangle over the thumbnail:
 * an icon font or an SVG would be a request or a markup change for one shape.
 */
.prod-thumb--video{position:relative}
.prod-thumb-play{
  position:absolute;left:50%;top:50%;z-index:2;width:22px;height:22px;margin:-11px 0 0 -11px;
  border-radius:50%;background:rgba(11,59,46,.72);pointer-events:none;
}
.prod-thumb-play::after{
  content:"";position:absolute;left:50%;top:50%;
  margin:-5px 0 0 -3px;border-style:solid;border-width:5px 0 5px 8px;
  border-color:transparent transparent transparent #fff;
}
.prod-thumb--video.is-active .prod-thumb-play{background:rgba(11,59,46,.9)}

/*
 * `.prod-gallery-set` and `.prod-gallery-sets` were here. They hid all but the active colourway's
 * gallery, which stopped existing on 16 Sep 2026: there is one gallery per product now and the
 * colour swatch jumps to a slide inside it. Removed rather than left — a `display:none` rule
 * matching nothing is exactly what makes the next person doubt the markup they are reading.
 */
/* WooCommerce's own variation form: the select is the real control and stays in the DOM for
   keyboard users and for when site.js never loads; the swatch buttons above drive it. The
   TABLE is what gets hidden, not the select - hiding the select itself with display:none
   takes it out of the tab order and out of form validation. */
.variations_form table.variations{position:absolute;width:1px;height:1px;margin:-1px;padding:0;overflow:hidden;clip:rect(0 0 0 0);clip-path:inset(50%);border:0;white-space:nowrap}
/*
 * The description and the price are hidden because this design shows the price in the summary, not
 * under the button. AVAILABILITY IS NOT ON THIS LIST — it was, while Astra printed its own copy
 * above the form, and it is the only visible home that line has now. See the availability block
 * earlier in this file.
 */
.variations_form .woocommerce-variation-description,
.variations_form .woocommerce-variation-price{display:none}
.prod-colour-chip.is-current{border-color:var(--ink);border-width:1px;box-shadow:0 0 0 1px var(--ink) inset}

/* ---- the wishlist button ---- */
/*
 * A PLAIN TEXT CONTROL — heart, label, no frame — as the client asked for on 10 Sep 2026.
 *
 * It was a bordered outline button, which put two boxes of the same width directly under each
 * other and made saving for later look like a second, competing decision. Without the frame the
 * hierarchy is obvious: Add to Cart is the page's decision, this is a note to self.
 *
 * The trade a frameless button makes is that nothing but the cursor says it is clickable, so the
 * affordances it keeps matter more than usual — the label underlines on hover, the heart fills when
 * saved, and it is a real <button> so it focuses and answers the keyboard.
 *
 * The row is `hidden` in the markup and revealed by site.js §13, so with JavaScript off there is no
 * button rather than a button that cannot save anything.
 */
.prod-wish-row{
  display:flex;align-items:center;gap:clamp(16px,2vw,26px);flex-wrap:wrap;
  margin:clamp(14px,1.8vw,18px) 0 0;
}
/*
 * ⚑ THIS [hidden] GUARD IS REQUIRED, and leaving it out is not a small bug: the row is `hidden` in
 * the markup, but `display:flex` above is an AUTHOR rule and `[hidden]{display:none}` is only in
 * the UA stylesheet. The author rule wins, so the row showed regardless — with JavaScript off the
 * save button rendered as a control that could not save.
 *
 * Same trap as .cart-drawer[hidden] in shell.css, which carries the same note.
 *
 * There was a second guard here for .prod-wish-lnk, the "View wishlist →" link this row used to
 * reveal after a save. The client had it removed (review, 15 Sep 2026); the header heart is the
 * route to the list now.
 */
.prod-wish-row[hidden]{display:none}
/*
 * Sentence case at reading size, not the uppercase tracked-out label a .btn carries. The button
 * chrome is all removed explicitly — background, border and padding — because a UA stylesheet gives
 * a <button> all three and `border:0` alone would leave the padding behind.
 */
.prod-wish{
  display:inline-flex;align-items:center;gap:9px;
  padding:4px 0;background:none;border:0;color:var(--green);
  font-family:inherit;font-size:.82rem;line-height:1.5;letter-spacing:.01em;text-transform:none;
  cursor:pointer;transition:color .25s ease;
}
/*
 * ⚑ text-decoration, NOT a border — and this was the second half of a reported misalignment.
 *
 * The hover underline was `border-bottom:1px` plus `padding-bottom:1px`, which are LAYOUT: they
 * made the text span 2px taller at the bottom, so `align-items:center` centred the heart against a
 * box whose middle was 1px below the middle of the letters. Measured: icon mid and text-box mid
 * agreed exactly at 566.3, while the glyphs' own centre was 565.3.
 *
 * text-decoration occupies no space, so the boxes now centre on the type instead of on the type
 * plus its underline. The colour is what animates, which is why the underline is always present
 * and merely transparent.
 */
.prod-wish .prod-wish-text{
  text-decoration:underline;text-decoration-color:transparent;text-underline-offset:3px;
  transition:text-decoration-color .25s ease;
}
.prod-wish:hover .prod-wish-text{text-decoration-color:currentColor}
/* The ring goes on the control, and it needs an offset: with no frame there is no padding to sit
 * inside, so a flush outline would touch the letters. */
.prod-wish:focus-visible{outline:2px solid var(--green);outline-offset:4px}
/*
 * The heart, drawn in CSS rather than loaded as an icon: it is the only glyph on the page that has
 * to change appearance with state, and a mask lets the fill follow `currentColor` for free.
 *
 * ⚑ THE viewBox IS OFFSET BY .58, and that is a fix rather than a quirk. A heart is not vertically
 * symmetric — it is widest near the top and comes to a point at the bottom — so this path's ink
 * runs y 4.66 to 20.50 inside a 0-24 box, putting its centre at 12.58 and the drawing .58 units
 * (about a third of a pixel at 14px) BELOW the middle of its own frame. Raising the viewBox origin
 * by the same .58 moves the artwork up to sit on the centre line, so `align-items:center` on the
 * button lands the heart where the eye expects it.
 *
 * The filled state carries the same stroke as the outline. It does not need one to look filled;
 * it has one so both states have an IDENTICAL ink box and the heart cannot appear to jump or
 * resize when it is toggled.
 */
.prod-wish-ico{
  width:14px;height:14px;flex:0 0 auto;background-color:currentColor;
  -webkit-mask:var(--heart) center/contain no-repeat;mask:var(--heart) center/contain no-repeat;
  --heart:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 .58 24 24'%3E%3Cpath fill='none' stroke='%23000' stroke-width='1.8' d='M12 20.5S3.5 15 3.5 9.2A4.7 4.7 0 0 1 12 6.6a4.7 4.7 0 0 1 8.5 2.6c0 5.8-8.5 11.3-8.5 11.3Z'/%3E%3C/svg%3E");
  /*
   * The last third of a pixel, and the only part of this that flex cannot do for us. "Add to
   * wishlist" has no descenders, so its ink runs from cap-height to baseline while the line box
   * still reserves room below for the descenders the font could have had — which puts the label's
   * visible centre ABOVE the middle of its own box. align-items:center matches the boxes, so it
   * necessarily leaves the icon low.
   *
   * Measured at 4x device scale: the heart's ink centre sat .375px below the label's. In em, so it
   * holds if the label's size changes — the gap is font metrics, not a fixed distance.
   */
  transform:translateY(-.03em);
  transition:background-color .25s ease;
}
/* Saved: the heart fills. With the frame gone this is the only non-verbal signal left, which is
 * why the label changes too rather than relying on it alone. */
.prod-wish.is-saved .prod-wish-ico{
  --heart:url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 .58 24 24'%3E%3Cpath fill='%23000' stroke='%23000' stroke-width='1.8' d='M12 20.5S3.5 15 3.5 9.2A4.7 4.7 0 0 1 12 6.6a4.7 4.7 0 0 1 8.5 2.6c0 5.8-8.5 11.3-8.5 11.3Z'/%3E%3C/svg%3E");
}
/*
 * ⚑ Quietened to match. .lnk is uppercase, tracked out at .2em and sized for a section CTA — next
 * to a frameless sentence-case button it read as the louder of the two, which is backwards: this
 * link is a footnote that only appears once something has been saved. Overriding rather than
 * dropping the .lnk class keeps its arrow and its hover rule.
 */
/* ---- the sketch inside "The Inspiration" ---- */
/*
 * No plate, no blend mode, no background. The drawings arrive as pencil on white paper and
 * woo/tools/prepare-product-images.py converts that paper to real transparency, so the file itself
 * is ink on nothing and composites correctly straight onto the panel.
 *
 * ⚑ `mix-blend-mode: multiply` was here first and is deliberately gone. It is the usual fix for
 * line art on white, and it worked for two of the four — the other two are drawn on slightly
 * darker paper (mean 232 against 240) and kept a faint grey rectangle. Fixing the asset beats
 * blending around it, and the result no longer depends on what colour sits behind it.
 *
 * The width is capped in px rather than %, because the drawings are 237-355px as supplied and a
 * percentage would upscale the smallest of them on a wide summary column.
 */
.prod-sketch{margin:0 0 18px;padding:0}
/*
 * ⚑ ONE RULE FOR EVERY IMAGE IN EVERY PANEL, AND THE CLIENT CHOOSES THE SIZE. Keith, 17 Sep 2026:
 * let the drag handles in the editor actually work, and keep a cap so nothing can break the layout.
 *
 * THE MISSING `width:100%` IS THE WHOLE CHANGE. It used to be here, and it is what made the
 * editor's own sizing inert: dragging an image in the classic editor writes `width`/`height`
 * ATTRIBUTES, and an attribute loses to any CSS rule, so every image rendered at the cap no matter
 * what the client did. Measured before and after — dragged to 80 renders at 80, to 150 at 150, to
 * 400 at 200 (capped). The handles are no longer a control that does nothing.
 *
 * `min(200px,100%)` rather than a flat 200px so the cap can never exceed its column. It cannot
 * today — the panel measures 374px at 1440, 340px at 1025 and 335px on a phone, so 200 always fits
 * — but the panel's width is a clamp on a viewport unit and those numbers are not promises.
 *
 * This replaced three overlapping rules and a bespoke `prod-sketch-img` class. The sketch is now
 * an ordinary panel image: the build seeds it at the width the client asked for (110px, the width
 * of the words "The Inspiration" — see bb_product_description()) and they can change it from
 * there like any other. Nothing here knows what a sketch is.
 *
 * ⚠ The trade is that a dragged width is a PIXEL width and does not respond to screen size. At a
 * 200px ceiling inside a 335px column that is safe at every width this site has; it is still a
 * fixed choice the client is making, and the cap is the only thing standing between it and a
 * broken column.
 */
.prod-panel-rich img{display:block;max-width:min(200px,100%);height:auto;margin:0 0 18px}
/* wpautop wraps a lone <img> in a <p>; the figure and the paragraph carry the spacing, not the
   image inside them, or the two margins add up. */
.prod-panel-rich p img,
.prod-sketch img{margin:0}

/* ============ catalogue mode ============ */
/*
 * ⚑ ONE LINE, BECAUSE A MISSING BUTTON EXPLAINS NOTHING. With Add to Bag gone the page is
 * otherwise indistinguishable from one that is broken, and a visitor's first guess is the
 * unflattering one. inc/site-mode.php prints this on the shop and in the product summary at the
 * exact point the button would have been.
 *
 * ⚑ --green, not --muted. Muted measures 4.81:1 on this surface - over the 4.5 floor, but this
 * is 13px text and the line exists to be read. Green is 8.24:1 and is the brand's own.
 */
.bb-cat-note{
  margin:0 0 22px;padding:12px 16px;
  border:1px solid var(--hair);border-radius:8px;background:var(--cream-alt);
  color:var(--green);font-size:.82rem;line-height:1.7;
}
.woo--single .bb-cat-note{margin:6px 0 24px}

/* ------------------------------------------------------------------ register opt-in ----------- */
/* Replaces WooCommerce's privacy sentence on the registration form — see inc/account-optin.php.
   Set apart from the fields above it with a rule rather than a box, because it is a different kind
   of thing: everything above is required to make an account, this is a choice. */
.bb-optin{margin:clamp(20px,2.4vw,28px) 0 0;padding-top:clamp(16px,2vw,22px);border-top:1px solid var(--rule)}
.bb-optin__title{margin:0 0 6px;font-family:'Poppins',sans-serif;font-weight:600;color:var(--green)}
.bb-optin__lead{margin:0 0 14px}
.bb-optin__lead,.bb-optin__small{font-size:.92rem;line-height:1.6;color:var(--ink)}

/* A 44px row so the whole line is a tap target on a phone, not just the 16px box. */
.bb-optin__check{display:flex;align-items:flex-start;gap:10px;min-height:44px;cursor:pointer;margin:0 0 12px}
.bb-optin__check input{flex:0 0 auto;width:18px;height:18px;margin:3px 0 0;accent-color:var(--green);cursor:pointer}
.bb-optin__check span{font-size:.95rem;line-height:1.5}
.bb-optin__check input:focus-visible{outline:2px solid var(--gold);outline-offset:2px}

.bb-optin__small{margin:0;color:var(--muted);font-size:.84rem}
.bb-optin__small a{color:inherit;text-decoration:underline}
.bb-optin__small a:hover{color:var(--green)}

/* ------------------------------------------------------------------ birthday ----------------- */
/* Three boxes rather than <input type="date">: a native picker opens on today, which is decades
   from the answer, and shows DD/MM or MM/DD by the browser's locale without saying which. */
.bb-bday{margin:clamp(16px,2vw,22px) 0 0}
.bb-bday__label{margin:0 0 8px;font-family:'Poppins',sans-serif;font-weight:500;color:var(--ink);font-size:.92rem}

/* Day and Month sit side by side; Year is wider because it holds four digits. On a phone they
   wrap rather than shrink — three squeezed selects are harder to hit than two rows of them. */
.bb-bday__row{display:flex;flex-wrap:wrap;gap:10px}
.bb-bday__part{flex:1 1 92px;min-width:92px;margin:0}
.bb-bday__part:last-child{flex:1 1 110px}
.bb-bday__part select{width:100%;min-height:44px;padding:0 12px;
  font-family:'Poppins',sans-serif;font-size:.95rem;color:var(--ink);
  background:#fff;border:1px solid var(--rule);border-radius:2px;cursor:pointer}
.bb-bday__part select:focus-visible{outline:2px solid var(--gold);outline-offset:2px;border-color:var(--green)}

.bb-bday__hint{margin:8px 0 0;font-size:.84rem;line-height:1.5;color:var(--muted)}

/* The asterisk on the two Account details fields that are no longer required — see
   inc/account-fields.php. CSS as well as the script there, so it is gone before first paint
   rather than blinking out; if a WooCommerce update renames the field the selector stops
   matching and the asterisk comes back, which is visible rather than silent. */
.woocommerce-EditAccountForm label[for="account_first_name"] .required,
.woocommerce-EditAccountForm label[for="account_last_name"] .required{display:none}
