/** Shopify CDN: Minification failed

Line 1146:42 Expected identifier but found whitespace
Line 1151:78 Unterminated string token

**/
@layer reset, tokens, base, layout, sections, utilities;

/* ===================================================================
   TOKENS - Volume I S3, S4, S5. Locked. Not merchant-editable by design.
=================================================================== */
@layer tokens {
  :root{
    --chalk:#F1F0EC; --jute:#DED8CC; --ink:#14161A;
    --indigo:#2F4260; --indigo-wash:#A9B6C6;

    /* Secondary text. Replaces per-component `opacity` on small copy, which
       silently failed WCAG AA (an opacity of .5 on --ink lands at 3.3:1).
       #5A5C60 keeps --ink's slight blue tint and clears 4.5:1 on BOTH
       surfaces: 5.9:1 on chalk, 5.4:1 on the jute wash.
       Hierarchy below this level comes from size/weight/letter-spacing,
       NOT from lighter colour - anything lighter cannot pass AA at 12-14px. */
    --ink-2:#5A5C60;

    /* Was hard-coded 3x as #9A3B2E across three stylesheets. Value unchanged
       (it already passes AA at 6.1:1) - only centralised. */
    --danger:#9A3B2E;

    /* Urgency is not an error, and the two must not share a colour. --danger
       means "something went wrong" in seven places — cart failures,
       add-to-cart errors, the drawer, the footer form — so a sales line
       wearing it would cost the error states their meaning. This is a
       brighter, less brown red that reads as pace rather than fault, and it
       is used on exactly one element. 5.73:1 on chalk against --danger's
       6.06:1: both clear the 4.5:1 floor, and they are far enough apart in
       hue and brightness to be told apart at a glance. */
    --urgent:#B3261E;

    /* Divider/hairline that is actually visible. --indigo-wash is 1.8:1 on
       chalk, below the 3:1 WCAG 1.4.11 floor for meaningful UI boundaries. */
    --edge:rgba(20,22,26,.13);
    --edge-strong:rgba(47,66,96,.32);
    /* Semantic, derived from the indigo primitive rather than invented: the
       unfilled half of a rating. --indigo-wash measured 1.81:1 on chalk, so the
       shape of "4.3 out of 5" could not be read at a glance; this lands at
       2.53:1 — legible as a form, still obviously empty beside a filled star.
       Its own token because --indigo-wash is also a border and a ::selection
       colour, and darkening that would change three unrelated places. */
    --star-off:rgba(47,66,96,.50);

    --font-display:'Gambetta','Iowan Old Style',Georgia,serif;
    --font-body:'Switzer',-apple-system,BlinkMacSystemFont,'Segoe UI',Roboto,sans-serif;

    --s-1:4px; --s-2:8px; --s-3:12px; --s-4:16px; --s-5:24px;
    --s-6:32px; --s-7:48px; --s-8:64px; --s-9:96px; --s-10:128px;

    --r-sm:4px; --r-md:8px; --r-lg:16px;

    /* Minimum comfortable touch target (WCAG 2.5.5 / iOS HIG). */
    --tap:44px;

    /* Two-part shadows: a tight dark edge for crispness plus a wide soft one
       for atmosphere. A single large blur reads as haze, not elevation. */
    --sh-1:0 1px 2px rgba(20,22,26,.06), 0 2px 8px rgba(20,22,26,.05);
    --sh-2:0 2px 4px rgba(20,22,26,.07), 0 12px 32px rgba(20,22,26,.10);
    --sh-3:0 4px 8px rgba(20,22,26,.09), 0 24px 56px rgba(20,22,26,.16);

    --ease:cubic-bezier(.22,.61,.36,1);
    --dur-fast:180ms;
    --dur-slow:520ms;

    --rhythm:var(--s-8);
    --header-h:56px;
    --announce-h:32px;
  }

  /* BREAKPOINTS - the canonical set. CSS cannot use custom properties inside
     a media query, so these are documented rather than tokenised. Use ONLY
     these five widths, so components never shift at mismatched widths:
       400px   small phone -> standard phone (tightened grid gaps)
       560px   phone    -> large phone (spec table, 2-up benefit grid)
       700px   two-col  -> three-col grids, footer columns, cart table
       900px   mobile   -> desktop (nav, PDP split, sticky bar off)
      1100px   three-col -> four-col grids, widest vertical rhythm
     A max-width query is always the next value down minus 1px - 399 / 559 /
     699 / 899 / 1099 - so the two sides of a boundary can never both match
     or both miss. Migration complete: the former 640/769/1200 one-offs are
     gone, and there is no width left in the theme outside this set. */
  @media(min-width:700px){ :root{ --rhythm:80px; } }
  @media(min-width:900px){ :root{ --rhythm:var(--s-9); } }
  @media(min-width:900px){ :root{ --header-h:72px; --announce-h:36px; } }
  /* Was 1200px. Folded onto 1100 so the widest rhythm arrives with the
     four-column grid rather than 100px after it. */
  @media(min-width:1100px){ :root{ --rhythm:var(--s-10); } }
}

/* ===================================================================
   RESET
=================================================================== */
@layer reset {
  *,*::before,*::after{ box-sizing:border-box; }
  html{ -webkit-text-size-adjust:100%; scroll-behavior:smooth; }
  body{ margin:0; }
  img,svg{ display:block; max-width:100%; }
  button,input,textarea,select{ font:inherit; color:inherit; }
  button{ background:none; border:0; cursor:pointer; }
  fieldset{ border:0; margin:0; padding:0; }
  ul{ margin:0; padding:0; list-style:none; }
  a{ color:inherit; }

  /* Removes the legacy 300ms tap delay mobile browsers add while waiting to
     see if a tap is a double-tap. Every control on the site felt ~a third of
     a second behind without this. */
  a,button,summary,label,input,select,textarea,[role="button"],[role="radio"]{
    touch-action:manipulation;
  }
}

/* ===================================================================
   BASE
=================================================================== */
@layer base {
  body{
    background:var(--chalk); color:var(--ink);
    font-family:var(--font-body); font-size:17px; line-height:1.65;
    -webkit-font-smoothing:antialiased;
    /* Sticky buy bar clearance, plus the iOS home-indicator inset. Stated once
       as a variable because the bar is not one height any more: off the
       product page it is 8 + 54 + 8 = 70px, and on it the trust line above the
       button adds 17. A reserve that disagrees with the bar either covers the
       last line of the page or leaves a gap under it, and the two drifting
       apart is exactly the kind of bug nobody sees until a footer link stops
       being tappable. Desktop pads to 16 either side and zeroes this below. */
    --bar-reserve:72px;
    padding-bottom:calc(var(--bar-reserve) + env(safe-area-inset-bottom));
  }
  body.template-product{ --bar-reserve:88px; }
  @media(min-width:900px){ body{ padding-bottom:0; } }
  body.nav-open,body.cart-open,body.zoom-open{ overflow:hidden; }

  h1,h2,h3,p,dl,dd,figure,table{ margin:0; }
  :focus-visible{ outline:2px solid var(--indigo); outline-offset:2px; border-radius:2px; }

  /* Any panel that scrolls independently (cart drawer, nav overlay, gallery
     strip) must not chain its scroll to the page behind it, or reaching the
     end of the panel starts scrolling the body underneath. */
  .cart-drawer-body,.nav-overlay,.gallery-main,.gallery-thumbs{ overscroll-behavior:contain; }

  .skip-link{
    position:absolute; left:-9999px; top:0; z-index:99;
    background:var(--indigo); color:var(--chalk);
    padding:var(--s-3) var(--s-4); border-radius:0 0 var(--r-sm) 0;
  }
  .skip-link:focus{ left:0; }

  ::selection{ background:var(--indigo-wash); color:var(--ink); }
}

/* ===================================================================
   LAYOUT PRIMITIVES
=================================================================== */
@layer layout {
  .wrap{ max-width:1240px; margin-inline:auto; padding-inline:var(--s-5); }
  @media(min-width:900px){ .wrap{ padding-inline:var(--s-6); } }

  .sec{ padding-block:var(--rhythm); }
  .sec--tight{ padding-block:var(--s-7); }
  @media(min-width:700px){ .sec--tight{ padding-block:var(--s-8); } }
  .sec--none{ padding-block:0; }
  .surf-jute{ background:rgba(222,216,204,.42); }

  /* The chapter marker. chalk and jute are both warm, so alternating them
     changes the texture of a section without ever telling the reader they have
     moved to a different part of the page — which on a long single-product
     homepage left a 1px divider doing all the structural work.

     This shifts temperature rather than lightness: cooler than either neutral,
     but sitting between them in value, so nothing in the foreground needs a
     second treatment. --ink lands at 15:1 here and --ink-2 at 5.5:1, in line
     with the 5.9 and 5.4 those two already hold on chalk and jute. */
  .surf-mist{ background:#E9ECF1; }

  .crease{ height:1px; background:var(--jute); position:relative; }
  .crease::after{
    content:''; position:absolute; inset:auto 0 0 0; height:12px;
    background:linear-gradient(to bottom,rgba(20,22,26,.045),transparent);
    pointer-events:none;
  }

  .eyebrow{
    font-size:12px; font-weight:500; letter-spacing:.08em; text-transform:uppercase;
    color:var(--indigo); margin-bottom:var(--s-4);
  }
  .h1{
    font-family:var(--font-display); font-weight:300;
    font-size:clamp(2.75rem,9vw,5.5rem); line-height:.98; letter-spacing:-.025em;
    text-wrap:balance;
  }
  .h2{
    font-family:var(--font-display); font-weight:300;
    font-size:clamp(2rem,5.5vw,3.5rem); line-height:1.05; letter-spacing:-.02em;
    max-width:18ch; text-wrap:balance;
  }
  .h3{ font-weight:500; font-size:clamp(1.375rem,3vw,1.75rem); line-height:1.25; letter-spacing:-.01em; }
  .lede{ font-size:clamp(1.0625rem,2.2vw,1.3125rem); line-height:1.55; max-width:45ch; margin-top:var(--s-5); }
  .body-copy{ max-width:65ch; text-wrap:pretty; }
  /* colour, not opacity - see --ink-2 note in the tokens layer */
  .micro{ font-size:13px; color:var(--ink-2); }
  .tok{ color:var(--indigo); }

  /* Guarantees a 44px hit area on a control that must stay visually smaller
     (icon buttons, inline text links in dense rows). The element keeps its
     painted size; only the touch surface grows. */
  .u-tap{ position:relative; }
  .u-tap::after{
    content:''; position:absolute; left:50%; top:50%; transform:translate(-50%,-50%);
    width:100%; height:100%; min-width:var(--tap); min-height:var(--tap);
  }

  .bc-todo{
    display:none;
    color:#B0158A; background:rgba(176,21,138,.1); border:1px dashed #B0158A;
    padding:0 4px; border-radius:3px; font-size:.85em;
  }
  html[data-design-mode] .bc-todo{ display:inline; }

  .img-cover{ width:100%; height:100%; object-fit:cover; display:block; }
  .img-missing{
    width:100%; height:100%; min-height:160px; display:flex; align-items:center; justify-content:center;
    background:repeating-linear-gradient(45deg,var(--jute),var(--jute) 10px,var(--chalk) 10px,var(--chalk) 20px);
    color:var(--ink); font-size:12px; text-align:center; padding:var(--s-4);
  }

  [data-reveal]{
    opacity:0; transform:translateY(16px) rotateX(-4deg); transform-origin:top center;
    transition:opacity var(--dur-slow) var(--ease), transform var(--dur-slow) var(--ease);
  }
  [data-reveal].in{ opacity:1; transform:none; }

  .cta{
    display:inline-flex; align-items:center; justify-content:center; gap:var(--s-3);
    background:var(--indigo); color:var(--chalk);
    font-weight:500; font-size:16px; text-decoration:none; border-radius:var(--r-md);
    min-height:52px; padding-inline:var(--s-6);
    box-shadow:var(--sh-1);
    transition:background var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease),
               box-shadow var(--dur-fast) var(--ease), opacity var(--dur-fast);
  }
  .cta:hover{ background:#263852; box-shadow:var(--sh-2); }
  /* Press feedback is the one motion that must survive reduced-motion: it is
     confirmation of input, not decoration. Kept to a transform so it never
     triggers layout. */
  .cta:active{ transform:scale(.985); box-shadow:var(--sh-1); }
  .cta:disabled,.cta[disabled]{ opacity:.45; cursor:not-allowed; box-shadow:none; }
  .cta:disabled:hover,.cta[disabled]:hover{ background:var(--indigo); }
  .cta-block{ width:100%; }
  .cta-outline{
    display:inline-flex; align-items:center; justify-content:center; gap:var(--s-3);
    background:transparent; color:var(--indigo); border:1px solid var(--indigo);
    font-weight:500; font-size:16px; text-decoration:none;
    border-radius:var(--r-md); min-height:52px; padding-inline:var(--s-6);
    transition:background var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease),
               color var(--dur-fast) var(--ease), border-color var(--dur-fast) var(--ease);
  }
  /* Gated like every other hover in the theme. Ungated, the tint stuck to the
     tapped button on touch until the user tapped somewhere else — the product
     card fixed this for the card itself and the button underneath it kept the
     bug. */
  @media(hover:hover) and (pointer:fine){
    .cta-outline:hover{ background:rgba(47,66,96,.06); }
  }
  .cta-outline:active{ transform:scale(.985); }

  /* The outline button had no disabled state at all: a sold-out "Sold out"
     button rendered in full indigo with cursor:pointer, identical in weight to
     a live "Add to cart" and tappable-looking to the end of time.
     Colour rather than the opacity fade .cta uses, because this variant is
     transparent — --indigo at 45% over chalk lands at 2.3:1 and turns the one
     piece of information the control still carries into grey mush. --ink-2 is
     5.9:1 on chalk, and dropping the hairline to --edge is what actually says
     "inactive". */
  .cta-outline:disabled,.cta-outline[disabled]{
    color:var(--ink-2); border-color:var(--edge); background:transparent;
    cursor:not-allowed; box-shadow:none;
  }
  .cta-outline:disabled:hover,.cta-outline[disabled]:hover{ background:transparent; }
  .cta-outline:disabled:active,.cta-outline[disabled]:active{ transform:none; }

  /* Pending is not unavailable. The button is disabled while the request is in
     flight, but it is coming back, so it keeps the brand colour and only swaps
     its word — otherwise "adding" and "sold out" look the same. */
  .cta-outline[aria-busy="true"],
  .cta-outline[aria-busy="true"]:disabled{
    color:var(--indigo); border-color:var(--indigo); cursor:progress;
  }

  .cta-block + .cta-block{ margin-top:var(--s-3); }

  /* Label swap for in-flight actions. Both words ship in the markup and CSS
     shows one, so the JS never rewrites the button's contents — which is what
     keeps the visually-hidden product name inside the label from being
     flattened into visible text on the way back. No spinner: at this size a
     rotating glyph is decoration, and the word carries the state on its own. */
  .btn-pending{ display:none; }
  [aria-busy="true"] > .btn-label{ display:none; }
  [aria-busy="true"] > .btn-pending{ display:inline; }

  /* ---- shared primitives -------------------------------------------------
     These are used by more than one section (price by the buy box, sticky bar
     and product card; the stepper by the buy box, cart drawer AND cart page),
     so they live here rather than being duplicated into each section file. */
  /* Price pair. baseline alignment rather than centre, so two different sizes
     sit on one reading line instead of floating against each other; wrap so a
     four-digit pair on a narrow phone breaks cleanly instead of overflowing. */
  .price-group{ display:inline-flex; align-items:baseline; gap:var(--s-2); flex-wrap:wrap; }
  .price-now{
    font-size:24px; font-weight:500; color:var(--ink);
    font-variant-numeric:tabular-nums; letter-spacing:-.01em;
  }
  /* One step down and secondary ink — the whole differentiation. Deliberately
     not --danger and not a badge: a struck figure already says "was", and
     colouring it would spend the page's only alarm colour on a price cut. */
  .price-was{
    font-size:15px; color:var(--ink-2); text-decoration:line-through;
    font-variant-numeric:tabular-nums;
  }
  /* The saving, as a figure rather than an argument. Same quiet pill the card
     badges and the verified-buyer badge use: indigo type, a firmer hairline, no
     fill. Deliberately not --danger and not a solid chip — the theme spends
     exactly one loud colour, on Add to Cart, and a discount that out-shouts the
     purchase button is working against the page it is on.

     Baseline-aligned with the pair via the row's align-items, and one step down
     in size, so it reads as a footnote to the price rather than as a third
     price. */
  .price-save{
    display:inline-flex; align-items:center;
    padding:2px var(--s-2); border-radius:100px;
    border:1px solid var(--edge-strong); color:var(--indigo);
    font-size:11px; font-weight:500; letter-spacing:.04em; text-transform:uppercase;
    white-space:nowrap;
  }

  /* Compact: the card and the cart line, where the pair has to live inside a
     row rather than own one. */
  .price-group--compact{ gap:var(--s-2); }
  .price-group--compact .price-now{ font-size:16px; letter-spacing:-.005em; }
  .price-group--compact .price-was{ font-size:13px; }

  /* Evidence about the title, so it is grouped with the title: 4px above,
     against the 8px that separates it from the price. Spacing carries the
     relationship, which is why the rating needs no rule, box or divider. */
  .pc-rating{
    margin:var(--s-1) 0 0;
    display:flex; align-items:center; gap:var(--s-2);
    font-size:12px; line-height:1.2; color:var(--ink-2);
  }
  /* Fractional stars: an unfilled track with a filled copy clipped over it, so
     4.6 draws as 4.6 stars. The previous row rounded to whole stars, printed
     five solid stars for anything at or above 4.5, and contradicted its own
     aria-label while doing it. The track's contrast is low by design — it is
     the empty half of a measure, and the accessible name plus the numeral
     beside it both carry the real value. */
  .pc-rating-stars{
    position:relative; display:inline-block; flex:0 0 auto;
    font-size:12px; line-height:1; color:var(--star-off);
  }
  .pc-rating-track{ display:block; }
  .pc-rating-fill{
    position:absolute; top:0; left:0; height:100%;
    overflow:hidden; white-space:nowrap; color:var(--indigo);
  }
  .pc-rating-avg{ font-weight:500; color:var(--ink); font-variant-numeric:tabular-nums; }
  .pc-rating-count{ font-variant-numeric:tabular-nums; }

  .stepper{ display:flex; align-items:center; border:1px solid var(--jute); border-radius:var(--r-md); }
  .qty-btn{
    width:var(--tap); height:var(--tap); display:flex; align-items:center; justify-content:center;
    color:var(--ink); transition:background var(--dur-fast) var(--ease);
  }
  .qty-btn:hover{ background:rgba(47,66,96,.06); }
  .qty-btn:disabled{ opacity:.35; cursor:not-allowed; }
  .qty-input{
    width:52px; height:var(--tap); text-align:center; border:0;
    border-left:1px solid var(--jute); border-right:1px solid var(--jute);
    -moz-appearance:textfield; appearance:textfield; background:transparent;
    font-variant-numeric:tabular-nums;
  }
  .qty-input::-webkit-outer-spin-button,
  .qty-input::-webkit-inner-spin-button{ -webkit-appearance:none; margin:0; }

  .cart-empty{ text-align:center; padding:var(--s-9) var(--s-4); color:var(--ink-2); }

  /* ---- photography plates (hero + closing CTA share these) --------------- */
  .plate{ position:absolute; inset:0; overflow:hidden; background:var(--jute); }
  .scrim{ position:absolute; inset:0; background:linear-gradient(to top,rgba(20,22,26,.62),rgba(20,22,26,.28) 38%,transparent 68%); }
  @media(min-width:900px){
    .scrim--side{ background:linear-gradient(to right,rgba(20,22,26,.66),rgba(20,22,26,.3) 44%,transparent 74%); }
  }
  .slot-tag{
    position:absolute; z-index:5; right:var(--s-4); bottom:var(--s-4);
    background:rgba(20,22,26,.55); color:var(--chalk); border:1px solid rgba(241,240,236,.22);
    border-radius:var(--r-sm); font-size:10px; letter-spacing:.05em; text-transform:uppercase;
    padding:5px 8px; line-height:1.5; max-width:200px; backdrop-filter:blur(6px);
  }
  html:not([data-design-mode]) .slot-tag{ display:none; }
}

/* ===================================================================
   UTILITIES
=================================================================== */
@layer utilities {
  .visually-hidden{
    position:absolute !important; width:1px; height:1px; padding:0; margin:-1px;
    overflow:hidden; clip:rect(0,0,0,0); white-space:nowrap; border:0;
  }
}

/* ===================================================================
   IMAGE FIT UTILITIES - unlayered so callers can rely on them winning
   over layered defaults. Set by snippets/responsive-image.liquid via
   the `fit` parameter.
=================================================================== */
.img-contain{
  width:100%; height:100%; object-fit:contain; object-position:center; display:block;
}

/* ===================================================================
   REDUCED MOTION - intentionally outside the layer system so it wins.
   Note the deliberate exception: .cta:active keeps its press scale,
   because that is input confirmation rather than decoration.
=================================================================== */
@media(prefers-reduced-motion:reduce){
  *,*::before,*::after{
    animation-duration:.01ms !important; transition-duration:.01ms !important;
    scroll-behavior:auto !important;
  }
  [data-reveal],.hero-anim{ opacity:1 !important; transform:none !important; }
}

/* ===================================================================
   ALWAYS-PRESENT CHROME
   header, announcement bar, footer, cart drawer and sticky buy bar are
   rendered by layout/theme.liquid on every single page. Loading them as
   five separate <link>s cost five requests per page and ~40% of gzip
   efficiency (small files each compress against their own dictionary),
   so they are inlined here. Genuinely optional sections stay in their
   own files and load only when that section is on the page — which is
   required for OS2.0, where merchants can move sections between
   templates at will.
=================================================================== */
@layer sections {
  /* ---- masthead ---------------------------------------------------
     The announcement bar and the header, pinned as one object. Everything
     that used to be negotiated between them — stacking, offset, when each
     one pins — is now a property of this single element, so there is no gap
     to open, nothing to flicker and no second scroll position.

     Templates carrying a hero keep the existing behaviour, where the header
     floats transparent over the photograph and goes solid on scroll: that is
     the homepage's whole opening move and pinning the pair in flow would push
     the hero down and take it away. Everywhere else — the PDP included — the
     whole masthead is sticky and the page scrolls underneath it.

     :has() is the accurate test and the @supports fallback below covers the
     browsers without it, the same pattern the header already uses for the
     flash-of-transparent fix. */
  .masthead{
    position:relative; z-index:20;
    transition:box-shadow var(--dur-fast) var(--ease);
  }
  body:not(:has(.hero)) .masthead{ position:sticky; top:0; }
  @supports not selector(:has(*)){
    body:not(.template-index) .masthead{ position:sticky; top:0; }
  }
  /* Elevation belongs to the pair, not to either half. It arrives only once
     the page has actually moved, so at rest the masthead sits flat on the
     page and nothing is casting a shadow onto content it is not covering. */
  .masthead.is-pinned{ box-shadow:0 1px 0 var(--edge), 0 6px 20px rgba(20,22,26,.06); }

  /* ---- announcement bar ------------------------------------------ */
  /* The bar and the header are one masthead, not two slabs that happen to
     touch. z-index drops below the header's so that when the header pins, the
     bar scrolls up behind it instead of over it — at 30 it out-stacked the
     header it sits on top of. The hairline gives the band a defined bottom
     edge rather than letting a tan block end abruptly against a transparent
     header over a dark hero. */
  .announce{
    height:var(--announce-h); display:flex; align-items:center; justify-content:center;
    background:var(--jute); font-size:12px; font-weight:500; letter-spacing:.06em; text-transform:uppercase;
    padding-inline:var(--s-3); position:relative; z-index:10;
    border-bottom:1px solid var(--edge);
    white-space:nowrap; overflow:hidden;
  }
  .announce .inner{ display:flex; align-items:center; overflow:hidden; text-overflow:ellipsis; white-space:nowrap; max-width:100%; }
  /* Colour rather than opacity, per the theme's own rule about small type:
     .5 on --ink lands at 3.3:1, and this glyph is painted, not decorative. */
  .announce .sep{ color:var(--ink-2); margin-inline:var(--s-2); flex:0 0 auto; }
  .announce .wide{ display:none; }
  /* Was 769px, which matched nothing else in the theme. The three-item string
     measures ~585px including its padding, so 700 clears it with ~115px to
     spare and the overflow ellipsis above still covers longer merchant copy. */
  @media(min-width:700px){
    .announce{ padding-inline:var(--s-6); letter-spacing:.05em; }
    .announce .wide{ display:inline; } .announce .narrow{ display:none; }
    .announce .sep{ margin-inline:var(--s-3); }
  }

  /* Stands down on the product page below the two-column breakpoint, where the
     sticky buy bar states the same facts one line above the Add to Cart. Two
     things are bought with it: the duplication goes, and the product page gets
     back 32px of *permanent* height — the masthead is sticky, so this band was
     not merely first-screen furniture, it was a strip held out of the viewport
     for the entire visit on the one template where the photograph is already a
     full screen-width tall.

     display:none rather than a height collapse, so it leaves the accessibility
     tree with the pixels and nothing announces a region that is not there. The
     class only exists when the merchant leaves the setting off, so switching it
     back on is a checkbox and no CSS is overridden. */
  @media(max-width:899px){
    body.template-product .announce--stand-down{ display:none; }
  }
}
.nav-desktop{ display:none; }
.nav-list{ display:flex; gap:var(--s-6); align-items:center; margin:0; padding:0; list-style:none; }
.nav-item{ position:relative; }
.nav-link{ display:inline-flex; align-items:center; min-height:var(--tap); gap:6px; text-decoration:none; font-size:15px; padding-block:var(--s-2); }
.nav-link:hover{ text-decoration:underline; text-underline-offset:4px; }
.nav-caret{ transition:transform var(--dur-fast) var(--ease); }
.nav-has-children:hover .nav-caret,
.nav-has-children:focus-within .nav-caret{ transform:rotate(180deg); }

.subnav{
  position:absolute; top:100%; left:0; min-width:200px; margin:0; padding:var(--s-3) 0;
  list-style:none; background:var(--chalk); border:1px solid var(--jute); border-radius:var(--r-md);
  box-shadow:0 8px 28px rgba(20,22,26,.10);
  opacity:0; visibility:hidden; transform:translateY(6px);
  transition:opacity var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease), visibility var(--dur-fast);
  z-index:30;
}
.nav-has-children:hover .subnav,
.nav-has-children:focus-within .subnav{ opacity:1; visibility:visible; transform:none; }
.subnav a{
  display:flex; align-items:center; min-height:var(--tap); padding:var(--s-2) var(--s-5); font-size:14px; text-decoration:none;
  color:var(--ink); white-space:nowrap;
}
.subnav a:hover{ background:rgba(47,66,96,.06); }
.subnav a[aria-current="page"]{ color:var(--indigo); font-weight:500; }

@media(min-width:900px){
  .nav-desktop{ display:block; margin-left:var(--s-9); }
  .hdr .cart-icon{ margin-left:auto; }
}

/* Mobile overlay groups */
.nav-m-group{ border-bottom:1px solid var(--jute); }
.nav-m-group summary{
  list-style:none; display:flex; align-items:center; justify-content:space-between;
  font-family:var(--font-display); font-size:28px; padding-block:var(--s-3); cursor:pointer;
}
.nav-m-group summary::-webkit-details-marker{ display:none; }
.nav-m-caret{ transition:transform var(--dur-fast) var(--ease); flex:0 0 auto; }
.nav-m-group[open] .nav-m-caret{ transform:rotate(90deg); }
.nav-m-children{ padding:0 0 var(--s-4) var(--s-4); display:flex; flex-direction:column; gap:var(--s-1); }
.nav-m-children a{ font-family:var(--font-body); font-size:17px; color:var(--ink-2);
                   display:flex; align-items:center; min-height:var(--tap); padding-block:var(--s-2); }


/* Header shell — merged in from the former theme-sections-1.css so the
   header owns all of its own styling in one file. */
@layer sections {
  /* ---- header ------------------------------------------------------ */
  /* Three columns rather than flex auto-margins, so the wordmark is centred by
     construction on mobile instead of by whatever happens to be in the side
     slots. It was 22px off centre, and the cause was invisible from the markup:
     .hdr-search is meant to be desktop-only, but .hdr-icon{display:flex} sits
     later in this same layer at equal specificity and quietly won, so a search
     icon nobody designed for was occupying the mobile header and pushing the
     wordmark left. Both problems close here — the grid centres the middle
     column regardless, and the rule below actually hides the icon. */
  .hdr{
    position:absolute; top:var(--announce-h); left:0; right:0; z-index:20;
    height:var(--header-h); padding-inline:var(--s-5);
    display:grid; grid-template-columns:var(--tap) 1fr var(--tap); align-items:center;
    color:var(--chalk);
    transition:background var(--dur-fast) var(--ease), color var(--dur-fast) var(--ease),
               border-color var(--dur-fast) var(--ease), text-shadow var(--dur-fast) var(--ease);
    border-bottom:1px solid transparent;
  }
  /* top:0. The base rule holds the header at --announce-h so it sits under the
     bar over a hero, and that value was never reset when it went fixed — so a
     pinned header floated 32px down the viewport with page content scrolling
     through the strip above it. --jute for the bottom edge was the theme's own
     documented mistake too: it is 1.2:1 on chalk, which is no edge at all. */
  .hdr.is-solid{
    position:fixed; top:0; background:var(--chalk); color:var(--ink); border-color:var(--edge);
  }

  /* The header is transparent by default and only turns solid once theme.js
     runs, so every template without a hero flashed white-on-white text before
     the script attached. This resolves it at paint time for those templates.
     Written as progressive enhancement: where :has() is unsupported the rule
     is simply dropped and the previous JS-driven behaviour still applies. */
  /* static, because the masthead above is what pins now. This rule used to say
     sticky and could never work: .hdr's parent is the section wrapper Shopify
     emits, which is exactly header-height, and a sticky element cannot travel
     outside its own parent — so the header scrolled away the instant the page
     moved, on every template without a hero. */
  body:not(:has(.hero)) .hdr{
    position:static; background:var(--chalk); color:var(--ink); border-color:var(--edge);
  }
  body:not(:has(.hero)) .hdr a{ color:var(--ink); text-shadow:none; }
  body:not(:has(.hero)) .hdr .mark{ color:var(--indigo); }

  /* Where :has() is missing the rule above evaporates and the flash comes back,
     which on this traffic is not a rare corner — older Android WebViews are a
     real share of India mobile. So the same result is derived from the body
     class the layout already prints, which needs no selector support at all.

     Scoped to @supports so the two can never both apply: the template name is
     exact today because hero-editorial ships on templates/index.json alone, but
     it would be wrong the day a merchant drops a hero onto another template.
     Browsers with :has() judge by the hero actually being there and stay right
     through that change; the rest get the answer that is correct today. */
  @supports not selector(:has(*)) {
    body:not(.template-index) .hdr{
      position:static; background:var(--chalk); color:var(--ink); border-color:var(--edge);
    }
    body:not(.template-index) .hdr a{ color:var(--ink); text-shadow:none; }
    body:not(.template-index) .hdr .mark{ color:var(--indigo); }
  }

  /* Search entry point — desktop only. On mobile it lives at the top of the
     nav overlay so the header keeps its balanced burger / wordmark / cart. */
  /* .hdr .hdr-search, not .hdr-search: the bare class ties with .hdr-icon and
     loses on source order, which is how a desktop-only control ended up in
     every mobile header. */
  @media(max-width:899px){ .hdr .hdr-search{ display:none; } }
  @media(min-width:900px){
    .hdr-search{ margin-left:auto; }
    .hdr-search + .cart-icon{ margin-left:0; }
  }

  /* Current-page indication. aria-current was already emitted by the nav
     markup but had no visual treatment, so sighted users had no idea where
     they were in the menu. */
  .nav-link[aria-current="page"]{ text-decoration:underline; text-underline-offset:6px; text-decoration-thickness:1.5px; }
  .nav-overlay-links > a[aria-current="page"]{ color:var(--indigo); }

  .nav-overlay-search{
    position:relative; display:flex; align-items:center;
    margin-top:var(--s-6); border:1px solid var(--jute); border-radius:var(--r-md);
    background:var(--chalk);
  }
  .nav-overlay-search:focus-within{ border-color:var(--indigo); }
  .nav-overlay-search-icon{
    display:flex; align-items:center; justify-content:center;
    width:var(--tap); height:var(--tap); color:var(--ink-2); flex:0 0 auto;
  }
  .nav-overlay-search input{
    flex:1; min-width:0; height:var(--tap); border:0; background:transparent;
    padding-inline:0 var(--s-4); font-size:16px; /* 16px stops iOS zooming on focus */
  }
  /* Replaced, not deleted. The browser default drew a box around a field that
     already fills the row; this is the same 2px indigo ring and offset the
     theme's other 16 :focus-visible rules use, so a keyboard user meets the
     same shape here as everywhere else. WCAG 2.4.7. */
  .nav-overlay-search input:focus-visible{
    outline:2px solid var(--indigo); outline-offset:2px; border-radius:var(--r-sm);
  }
  .hdr.is-solid a{ color:var(--ink); text-shadow:none; }
  .hdr.is-solid .mark{ color:var(--indigo); }
  .hdr a{ text-decoration:none; text-shadow:0 1px 12px rgba(20,22,26,.34); }
  /* justify-self rather than margin-inline:auto — the grid column is already
     centred, so the wordmark only has to centre inside it. */
  .hdr .mark{ font-family:var(--font-display); font-size:20px; letter-spacing:-.03em; justify-self:center; }
  .hdr-icon{
    width:var(--tap); height:var(--tap); display:flex; align-items:center; justify-content:center;
    position:relative; flex:0 0 auto; border-radius:50%;
    transition:background var(--dur-fast) var(--ease);
  }
  /* The header's controls had no hover and no press state at all — the one
     part of the site where nothing answered a pointer. Kept to a wash of the
     current ink so it works over a photograph and over chalk alike, and gated
     behind a real pointer like every other hover in the theme. */
  /* A wash of the current ink, drawn behind the glyph. currentColor is what
     makes one rule work in both header states: chalk over a photograph, ink
     over the solid bar. */
  .hdr-icon::before{
    content:''; position:absolute; inset:0; border-radius:50%;
    background:currentColor; opacity:0;
    transition:opacity var(--dur-fast) var(--ease);
  }
  .hdr-icon svg{ position:relative; z-index:1; }
  .hdr-icon .badge{ z-index:2; }
  @media(hover:hover) and (pointer:fine){
    .hdr-icon:hover::before{ opacity:.10; }
  }
  .hdr-icon:active{ transform:scale(.94); }
  /* The 44px box starts at the padding edge, so the glyph inside it sat 12px
     inboard of where every other left edge on the page begins. Pulling the
     outer two boxes back by that 12px puts the icons optically flush with the
     content column without touching the target size.

     justify-self pins each one to the outer edge of its grid column first;
     without it a fixed-width item sits at the column start and the negative
     margin on the right-hand one moves nothing. Ignored on desktop, which is
     a flex row. */
  .hdr > .hdr-icon:first-child{ justify-self:start; margin-left:calc(var(--s-3) * -1); }
  .hdr > .hdr-icon:last-child{ justify-self:end; margin-right:calc(var(--s-3) * -1); }
  /* The item count is real information, not decoration — 10px in an 16px
     circle was the smallest type on the site. */
  .hdr-icon .badge{
    position:absolute; top:1px; right:1px; min-width:18px; height:18px; padding:0 4px;
    border-radius:100px; background:var(--indigo); color:var(--chalk);
    font-size:11px; line-height:18px; text-align:center; font-weight:500;
    font-variant-numeric:tabular-nums;
  }
  .hdr.is-solid .hdr-icon .badge{ box-shadow:0 0 0 2px var(--chalk); }
  .hdr nav{ display:none; }
  .no-flexbox-gap-fallback > * + *{ margin-left:var(--s-6); }
  @media(min-width:900px){
    /* position:absolute is already the base value and the non-hero rules above
       deliberately override it to fixed. Restating it here only re-fought that
       decision at 900px. */
    /* Desktop is a row, not three columns: the wordmark leads, the nav follows
       it, and the icons are pushed to the far edge by the cart's auto margin. */
    .hdr{ height:var(--header-h); padding-inline:var(--s-6); display:flex; }
    .hdr .mark{ font-size:22px; justify-self:auto; }
    .hdr nav{ display:flex; gap:var(--s-6); margin-left:var(--s-9); font-size:15px; }
    .hdr nav a:hover{ text-decoration:underline; text-underline-offset:4px; }
    .hdr .cart-icon{ margin-left:auto; }
    .hdr .burger{ display:none; }
  }

  /* mobile nav overlay */
  .nav-overlay{
    position:fixed; inset:0; z-index:50; background:var(--chalk);
    display:flex; flex-direction:column; padding:var(--s-5);
    transform:translateX(-100%); transition:transform var(--dur-slow) var(--ease);
    visibility:hidden;
  }
  .nav-overlay.on{ transform:translateX(0); visibility:visible; }
  .nav-overlay-top{ display:flex; align-items:center; justify-content:space-between; height:var(--header-h); }
  .nav-overlay-top .mark{ font-family:var(--font-display); font-size:20px; color:var(--indigo); letter-spacing:-.03em; }
  .nav-overlay-links{ margin-top:var(--s-8); display:flex; flex-direction:column; gap:var(--s-6); }
  .nav-overlay-links a{ font-family:var(--font-display); font-size:28px; text-decoration:none; color:var(--ink); }
  .nav-overlay-foot{ margin-top:auto; padding-top:var(--s-7); border-top:1px solid var(--jute); font-size:14px; }
  .nav-overlay-foot a{ display:flex; align-items:center; min-height:var(--tap); text-decoration:none; padding-block:var(--s-2); }
  @media(min-width:900px){ .nav-overlay{ display:none; } }
}
@layer sections {
  /* ---- footer ------------------------------------------------------------- */
  .ftr{ background:var(--jute); padding-block:48px 24px; }
  .ftr-brand{ margin-bottom:32px; max-width:280px; }
  .ftr-mark{ font-family:var(--font-display); font-size:22px; letter-spacing:-.03em; color:var(--indigo); display:block; }
  .ftr-tag{ font-size:14px; color:var(--ink-2); margin-top:6px; line-height:1.5; }
  .ftr-grid{ display:grid; grid-template-columns:1fr 1fr; gap:24px 16px; }
  @media(min-width:700px){ .ftr-grid{ grid-template-columns:repeat(3,1fr); gap:32px; max-width:640px; margin-left:auto; } }
  @media(min-width:900px){ .ftr-top{ display:flex; justify-content:space-between; gap:64px; align-items:flex-start; } .ftr-brand{ margin-bottom:0; } }
  .ftr h3{ font-size:11px; font-weight:500; letter-spacing:.09em; text-transform:uppercase; margin:0 0 var(--s-3); color:var(--ink-2); }
  .ftr a{ color:var(--ink); text-decoration:none; display:block; padding-block:var(--s-3); font-size:14px; min-height:var(--tap); }
  .ftr a:hover{ color:var(--indigo); text-decoration:underline; text-underline-offset:4px; }
  .ftr address{ font-style:normal; font-size:14px; line-height:1.6; color:var(--ink-2); }
  .ftr-base{
    margin-top:32px; padding-top:16px; border-top:1px solid rgba(20,22,26,.1);
    font-size:12px; color:var(--ink-2); display:flex; flex-wrap:wrap; gap:var(--s-1) var(--s-4);
  }
}
@layer sections {
  /* ---- cart drawer --------------------------------------------------------- */
  .cart-drawer-scrim{
    position:fixed; inset:0; z-index:55; background:rgba(20,22,26,.4);
    opacity:0; visibility:hidden; transition:opacity var(--dur-fast) var(--ease), visibility var(--dur-fast);
  }
  .cart-drawer-scrim.on{ opacity:1; visibility:visible; }
  .cart-drawer{
    position:fixed; top:0; right:0; bottom:0; z-index:56; width:min(420px,100vw);
    background:var(--chalk); display:flex; flex-direction:column;
    /* visibility, not just transform. Translating the panel off-screen leaves
       its links and buttons in the tab order, so keyboard users could Tab into
       an invisible drawer and lose their place entirely. */
    transform:translateX(100%); visibility:hidden;
    transition:transform var(--dur-slow) var(--ease), visibility var(--dur-slow);
    box-shadow:var(--sh-3);
    max-width:100vw;
  }
  .cart-drawer.on{ transform:translateX(0); visibility:visible; }
  .cart-drawer-head{ display:flex; align-items:center; justify-content:space-between; padding:var(--s-5); border-bottom:1px solid var(--jute); }
  .cart-drawer-head h2{ font-family:var(--font-display); font-size:20px; font-weight:400; }
  .cart-drawer-body{ flex:1; overflow-y:auto; padding:var(--s-5); }
  .cart-line{ display:grid; grid-template-columns:72px 1fr auto; gap:var(--s-4);
              padding-block:var(--s-5); border-bottom:1px solid var(--jute); }
  .cart-line-img{ width:72px; height:72px; border-radius:var(--r-sm); overflow:hidden; background:var(--chalk); border:1px solid var(--jute); }
  .cart-line-title{ font-size:15px; font-weight:500; }
  .cart-line-price{ font-size:14px; color:var(--ink-2); margin-top:2px; }
  /* The unit price is context here — the line total to its right is the figure
     being totted up — so the pair steps down rather than carrying the row. */
  .cart-line-price .price-now{ font-size:14px; }
  .cart-line-price .price-was{ font-size:12px; }
  .cart-line-stepper{ display:flex; align-items:center; gap:var(--s-3); margin-top:var(--s-3); }
  .cart-line-remove{ font-size:13px; text-decoration:underline; text-underline-offset:3px;
                     color:var(--ink-2); margin-top:var(--s-2); min-height:var(--tap);
                     display:inline-flex; align-items:center; }
  .cart-line-remove:hover{ color:var(--danger); }
  /* .cart-empty itself is a shared primitive defined in the layout layer —
     the cart page uses it too. Only the drawer-specific parts live here. */
  .cart-empty-title{ font-family:var(--font-display); font-size:20px; color:var(--ink); }
  .cart-empty-note{ font-size:14px; color:var(--ink-2); margin-top:var(--s-2); }
  .cart-empty-cta{ margin-top:var(--s-6); }

  .cart-line-variant{ font-size:13px; color:var(--ink-2); margin-top:2px; }
  /* Rendered by theme.js since the first version of the drawer, but never
     given a style — it inherited body size and out-weighed the line title. */
  .cart-line-total{ font-size:14px; font-weight:500; font-variant-numeric:tabular-nums; white-space:nowrap; }

  /* Placeholder shown while /cart.js is in flight, so opening the drawer does
     not present an empty panel that then pops full. */
  .cart-skeleton{ display:flex; flex-direction:column; gap:var(--s-5); }
  .cart-skeleton div{
    height:72px; border-radius:var(--r-sm);
    background:linear-gradient(90deg,var(--jute) 25%,rgba(222,216,204,.45) 37%,var(--jute) 63%);
    background-size:400% 100%; animation:cartShimmer 1.4s ease infinite;
  }
  @keyframes cartShimmer{ from{ background-position:100% 0; } to{ background-position:0 0; } }
  @media(prefers-reduced-motion:reduce){
    .cart-skeleton div{ animation:none; background:var(--jute); }
  }

  /* Safe-area inset so the Checkout button clears the iOS home indicator. */
  .cart-drawer-foot{
    padding:var(--s-5);
    padding-bottom:calc(var(--s-5) + env(safe-area-inset-bottom));
    border-top:1px solid var(--edge);
  }
  /* Sits above the total so the reason arrives before the number it
     explains. Same size and ink as the buy box's error line. */
  .cart-drawer-error{ font-size:13px; color:var(--danger); margin-bottom:var(--s-3); }
  .cart-drawer-error[hidden]{ display:none; }

  /* ---- total price summary ---------------------------------------------
     The breakdown a shopper opens when the figure at the bottom does not
     obviously follow from the figures above it — which is precisely what a
     cart-level discount does, since it moves the total and leaves every line
     untouched.

     Every [hidden] below is spelled out. The attribute's user-agent rule is
     display:none, but any display declared in a class rule beats it on
     specificity, so a row set to display:flex and then hidden stays on screen.
     The error line above needed the same rule for the same reason. */
  .cart-summary{ margin-bottom:var(--s-3); }

  .cart-summary-panel{ padding-block:var(--s-2) var(--s-1); }
  .cart-summary-panel[hidden]{ display:none; }

  .cart-summary-rows{ margin:0; }
  .cart-summary-row{
    display:flex; justify-content:space-between; align-items:baseline;
    gap:var(--s-4); padding-block:5px;
  }
  .cart-summary-row[hidden]{ display:none; }
  .cart-summary-row dt{ font-size:13px; color:var(--ink-2); }
  .cart-summary-row dd{
    margin:0; font-size:13px; color:var(--ink);
    font-variant-numeric:tabular-nums; white-space:nowrap;
  }

  /* The saving carries indigo and the rest of the column stays ink. It is the
     one line here a shopper is pleased to find, and indigo is what this theme
     spends when something is worth looking at — no green, no badge, no red
     borrowed from somebody else's storefront. */
  .cart-summary-cut{ color:var(--indigo); }
  .cart-summary-free{
    font-size:11px; font-weight:500; letter-spacing:.06em; text-transform:uppercase;
  }

  /* To Pay closes the column, so it takes the rule above it and a step up in
     weight — the same move the drawer already makes between a label and the
     number it labels. */
  .cart-summary-row--pay{
    margin-top:var(--s-2); padding-top:var(--s-3);
    border-top:1px solid var(--edge);
  }
  .cart-summary-row--pay dt{ font-size:14px; font-weight:500; color:var(--ink); }
  .cart-summary-row--pay dd{ font-size:15px; font-weight:500; }

  /* The bar is a real button: full tap height, and the whole row is the target
     rather than the chevron alone. font:inherit because a <button> otherwise
     drops to the UA font and would be the one place in the drawer not set in
     the theme's face. */
  .cart-summary-bar{
    display:flex; align-items:center; justify-content:space-between;
    gap:var(--s-3); width:100%; min-height:var(--tap);
    padding:var(--s-2) 0; margin:0;
    background:none; border:0; cursor:pointer;
    text-align:left; color:inherit; font:inherit;
  }
  .cart-summary-bar-l{ display:flex; flex-direction:column; gap:1px; min-width:0; }
  .cart-summary-bar-label{ font-size:13px; color:var(--ink-2); }
  .cart-summary-bar-value{
    font-size:18px; font-weight:500; font-variant-numeric:tabular-nums;
  }
  .cart-summary-bar-r{ display:flex; align-items:center; gap:var(--s-2); flex-shrink:0; }
  .cart-summary-bar-save{ font-size:12px; color:var(--indigo); white-space:nowrap; }
  .cart-summary-bar-save[hidden]{ display:none; }

  /* The set's chevron points right, so it is rotated into place rather than a
     second glyph being added for the sake of one control. */
  .cart-summary-chevron{
    display:flex; color:var(--ink-2);
    transform:rotate(90deg); transition:transform 160ms var(--ease);
  }
  [aria-expanded="true"] .cart-summary-chevron{ transform:rotate(-90deg); }
  @media(prefers-reduced-motion:reduce){
    .cart-summary-chevron{ transition:none; }
  }

  /* 375px with a four-figure total and "You Save" alongside is the tightest
     this row gets. The label column is allowed to shrink and the figures are
     not, so the total and the saving stay whole and legible rather than the
     row finding its width by wrapping a price. */
  @media(max-width:380px){
    .cart-summary-bar-save{ font-size:11px; }
    .cart-summary-row{ gap:var(--s-3); }
  }

  /* ---- cart badge acknowledgement -------------------------------------
     What moves: the count chip. Why: state indication — the badge is the only
     confirmation an add landed, and a silent number swap is the one moment in
     the flow with no feedback at all. Frequency: occasional, the tier that
     earns standard motion. One beat, 140ms, peak 1.12, no loop, no bounce.
     Transform only, so nothing around it reflows.

     Driven by a class theme.js toggles rather than a keyframe living on the
     element, so two adds in quick succession retrigger cleanly instead of the
     second being swallowed by the first. */
  .hdr-icon .badge{ transform-origin:50% 50%; }
  .badge.is-bumped{ animation:cartBadgeBump 140ms var(--ease); }
  @keyframes cartBadgeBump{ 0%{ transform:scale(1); } 50%{ transform:scale(1.12); } 100%{ transform:scale(1); } }

  /* ---- cart line removal ----------------------------------------------
     There is no exit animation here any more, and that is a decision rather
     than an omission.

     The row used to collapse to a zero-height grid track on its way out. It
     caused two production bugs in a row — a rejected removal left an invisible
     row sitting over a subtotal that still counted it, and the collapse was
     found by frame-by-frame trace never to have interpolated at all, snapping
     209px to 121px and leaving a hole. Both were fixable, and both were fixed,
     but the shape of the problem did not change: an exit animation has to hold
     the real cart state back for the length of the animation, and any state
     that is held back is state that can be got wrong. Two hundred milliseconds
     of polish is not worth a third way for a cart to lie about what is in it.

     What remains is .is-removing, which is a state and not a journey. It dims
     the row and stops it taking input so a tap is acknowledged, it changes no
     geometry so nothing below it moves, and it asserts nothing about the
     outcome. It cannot go stale: it is applied before the request and every
     mutation ends in a paint from Shopify's authoritative cart that either
     rebuilds the row without it or removes the row entirely. */
  .cart-line.is-removing{
    opacity:.5; pointer-events:none;
    transition:opacity 120ms var(--ease);
  }

  /* Motion out, meaning intact. The dim is a state, so under reduced motion it
     arrives rather than fades. Nothing in theme.js waits on a transition, so
     switching these off changes how the drawer looks and nothing about what it
     does. */
  @media(prefers-reduced-motion:reduce){
    .badge.is-bumped{ animation:none; }
    .cart-line.is-removing{ transition:none; }
  }

  .cart-drawer-note{ font-size:12px; color:var(--ink-2); margin-top:var(--s-3); text-align:center; }
}
@layer sections {
  /* ---- sticky buy bar -------------------------------------------------------- */
  /* One button, and nothing else. The band has to read as a material the page
     is sliding under rather than something stuck on top of it, so the wash is
     low-opacity — .96 was effectively opaque and wasted the blur behind it —
     and the separation is a soft shadow plus a hairline rather than a rule
     alone.

     --buybar-lift is 0px on a live storefront and theme.js only raises it when
     Shopify's theme-preview chrome is on screen. That chrome is a ~98px bar
     pinned to this exact edge at a z-index no theme can outrank, so a bar at
     bottom:0 renders perfectly and is invisible to anyone reviewing a preview
     link. Costs production nothing; it is what makes this reviewable at all. */
  /* 8px above and below the button rather than 12. The band is the last thing
     between the price and the bottom of a landing screen, and four pixels at
     each end is the difference between the price clearing it and touching it.
     The button itself is untouched at 54px, well past the 44px floor. */
  .buybar{
    position:fixed; left:0; right:0; bottom:var(--buybar-lift,0px); z-index:40;
    padding:var(--s-2) var(--s-4);
    padding-bottom:calc(var(--s-2) + env(safe-area-inset-bottom));
    background:rgba(241,240,236,.72);
    backdrop-filter:blur(20px) saturate(180%); -webkit-backdrop-filter:blur(20px) saturate(180%);
    box-shadow:0 -1px 0 var(--edge), 0 -12px 32px rgba(20,22,26,.10);
    opacity:0; visibility:hidden; transform:translateY(100%);
    /* Rises slower than it fades, so it reads as arriving rather than blinking
       on. One curve, the theme's own, and nothing bounces. */
    transition:transform 340ms var(--ease), opacity 200ms var(--ease), visibility 340ms;
  }
  .buybar.on{ opacity:1; visibility:visible; transform:translateY(0); }

  /* No travel where motion is unwelcome — the bar still needs to arrive, so it
     keeps the fade and drops the slide rather than appearing instantly. */
  @media(prefers-reduced-motion:reduce){
    .buybar,.buybar.on{ transform:none; }
    .buybar{ transition:opacity 160ms linear, visibility 160ms; }
  }

  /* Below the two-column breakpoint the product page's bar is not a scroll
     trigger at all — it is the page's footer CTA, present from the first paint
     and never withdrawn. On a phone the buy box is one screen out of a very
     long page, and a control that has to be scrolled back to is a control that
     is missing for most of the visit. It is also the only Add to Cart there:
     the buy box's own button is hidden below 900px, so this is not competing
     with anything.

     Stated in CSS rather than by adding .on from script, for two reasons: it
     paints with the first stylesheet instead of waiting on theme.js, so there
     is no frame where the footer is absent and nothing arriving late; and it
     holds even if the script never runs. body already reserves the bar's height
     at every mobile width, so making it permanent moves nothing.

     Scoped to the product template because off the PDP this bar is a "View
     product" link, not a purchase — a permanent one would be an ad pinned over
     every page. .on still gets toggled underneath and resolves to these same
     values, so the two never disagree. */
  @media(max-width:899px){
    body.template-product .buybar{ opacity:1; visibility:visible; transform:none; }
  }

  /* The one thing that takes it away, at any width. The cart drawer, the nav
     overlay and the zoom lightbox each own the whole screen and lock the body
     behind them; an Add to Cart floating over a cart is a second, contradictory
     exit from a surface that already has its own. Same weight as .buybar.on and
     after both it and the rule above, so it wins in every combination.

     Opacity and visibility only, no transform: the reduced-motion block above
     zeroes the travel and is earlier in the cascade, and a rule that quietly
     reinstates a slide for people who asked for none is worse than no exit
     animation at all. */
  body.nav-open .buybar,
  body.cart-open .buybar,
  body.zoom-open .buybar{ opacity:0; visibility:hidden; }

  .buybar-form{ margin:0; }

  /* ---- the trust line ---------------------------------------------------
     One line, above the button, and quieter than everything around it: 11px,
     secondary ink, the theme's own tick at 12px. It sits above rather than
     below because below the button is where the home indicator lives on an
     iPhone, and because the reading order that works is reassurance and then
     action — a shopper's eye lands on the button last and leaves from it.

     Deliberately weightless. This is not a feature list and it is not selling
     anything; it is the row a shopper's eye passes over on the way to the
     button, and if it competes with the button for attention it has failed.
     Nothing in it is a link or a control, so it costs no tab stops and adds
     no targets next to the one target that matters.

     nowrap on the items, not the list: the facts break between each other but
     never inside one, so "Cash on / delivery" cannot happen. */
  .buybar-trust{
    display:flex; justify-content:center; align-items:center;
    gap:var(--s-1) var(--s-4);
    margin:0 0 var(--s-1); padding:0; list-style:none;
    font-size:11px; line-height:1.2; letter-spacing:.01em;
    /* --ink, not the --ink-2 every other caption in the theme uses, and this is
       the one place that exception is right. The band is a 72% wash with a blur
       behind it, not a surface: what passes under it on this template is the
       product photography, which is dark. Measured on the worst case — the wash
       over a black frame, #AEADAA — --ink-2 lands at 2.99:1, under the 4.5:1
       floor for 11px text, while --ink holds 8.07:1. On chalk it is 15.9:1.
       The row still reads as subordinate: it is 11px against a 16px button in
       reversed indigo, and weight is doing that job rather than contrast. */
    color:var(--ink);
    /* Merchant copy is not under this file's control. The setting asks for two
       or three words per fact; this is what happens when it gets a sentence —
       the line clips at the bar's edge instead of widening it and pushing the
       button off centre. A guard, not a layout mechanism. */
    overflow:hidden;
  }
  .buybar-trust li{ display:flex; align-items:center; gap:var(--s-1); white-space:nowrap; }
  .buybar-trust svg{ flex:0 0 auto; color:var(--indigo); }

  /* Three facts below 560px and no more. The fourth is not dropped because it
     is unimportant — it is dropped because a line that wraps to two is no
     longer a strip, and the buy box states all four in full above 900px.

     560 rather than a number measured off the default strings: four facts
     including a dispatch promise measure ~434px, which overflowed a 430px
     phone by 2px on each side when this was set at 429. It is the theme's own
     next breakpoint up, and it leaves room for merchant copy that is a word
     longer than the default rather than being tuned to the exact strings that
     ship. Order in the markup is priority order, so the dispatch promise —
     first when it is set — is never the one that goes. */
  @media(max-width:559px){ .buybar-trust li:nth-child(n+4){ display:none; } }

  /* The button is the bar. With nothing to share the width with it runs the
     full measure, inset by one step on each side so it reads as an object
     resting on the surface rather than a slab welded to the screen edges.
     54px, comfortably past the 44px floor, because this is the single most
     important target on the page and the only one a thumb reaches for
     without looking. */
  .buybar-cta{
    display:flex; width:100%; min-height:54px;
    align-items:center; justify-content:center;
    padding-inline:var(--s-5);
    font-size:16px; font-weight:500; letter-spacing:-.005em;
    background:var(--indigo); color:var(--chalk); border-radius:var(--r-md);
    border:0; cursor:pointer; text-decoration:none; box-shadow:var(--sh-2);
    transition:background var(--dur-fast) var(--ease), transform var(--dur-fast) var(--ease),
               box-shadow var(--dur-fast) var(--ease);
  }
  .buybar-cta:hover{ background:#263852; box-shadow:var(--sh-2); }
  .buybar-cta:active{ transform:scale(.985); box-shadow:var(--sh-1); }
  .buybar-cta:disabled{ opacity:.45; cursor:not-allowed; }

  /* ---- the mark ---------------------------------------------------------
     A bag with a heart in it, 20px, ahead of the label.

     Decoration with a job. On a phone this bar is on screen for the entire
     visit, and a bag says "this is the control that buys it" before a word of
     it has been read. The heart is the register rather than a second message:
     this is a shop for people buying for a baby, and one small solid shape
     says so more quietly than any line of copy would.

     Geometry is untouched by construction. The button is min-height:54px and
     this is 20px sitting inside it, so the bar's height, the button's height,
     its radius, its colour, its width and its position are all exactly what
     they were. The label re-centres around the pair; that is the whole visible
     difference.

     display:block kills the inline baseline gap under an svg — left inline it
     carries descender space and the flex line measures taller than the glyph.

     A shade under full strength. The mark is the first thing the eye lands on
     and the last thing it should stay on; at 1.0 a solid heart beside 16px
     type pulls harder than the words it is introducing. */
     Sized here as well as on the element. Every other icon in the theme takes
     its box from the width/height the snippet writes, which is fine inside a
     block that would not care if it were wrong. This one is a flex child of a
     stretch-aligned button: an svg that arrives without usable dimensions has
     no intrinsic size to fall back on, so it fills the line box and takes the
     bar's whole layout with it. Two declarations to make that unreachable. */
  .buybar-cta > svg{
    flex:0 0 auto; display:block; width:20px; height:20px;
    margin-right:var(--s-2); opacity:.92;
  }

  /* The beat.

     Two soft beats once every nine seconds, on the heart alone: about a second
     and a half of movement in a nine-second cycle, and stillness for the rest.
     That ratio is the design. Something that moves continuously stops being
     seen within a minute and costs a frame budget forever; something that
     moves at you is a nag on a band that never leaves the screen. This is
     meant to be caught once, peripherally, while the shopper is reading
     something else.

     transform on one small filled path, so the compositor handles it on its
     own layer: nothing reflows, the svg's box does not change, and the label
     beside it does not move. The bag itself never moves at all.

     transform-box:fill-box because an SVG child resolves transform-origin
     against the viewBox otherwise — 50% of a 20x20 viewBox is not the middle
     of a heart drawn in its lower half, and the heart would swing instead of
     beat.

     Six seconds of delay: nothing is in motion while the page is still
     settling, and the first beat lands long after the first paint. */
  .buybar-cta .icon-heart{
    transform-box:fill-box; transform-origin:50% 50%;
    animation:buybarHeart 9s var(--ease) 6s infinite;
  }
  @keyframes buybarHeart{
    0%,82%,100%{ transform:scale(1); }
    86%{ transform:scale(1.22); }
    90%{ transform:scale(1.04); }
    94%{ transform:scale(1.15); }
    98%{ transform:scale(1); }
  }

  /* Still under a finger, and still while the add is in flight — the same
     states that stop the breath below, for the same reason.

     animation:none rather than animation-play-state:paused. A paused animation
     still wins the cascade for transform, so pausing would freeze the heart
     mid-beat at whatever scale it had reached; removing it lets the path fall
     back to the size it is drawn at. */
  .buybar-cta:hover .icon-heart,
  .buybar-cta:active .icon-heart,
  .buybar-cta:focus-visible .icon-heart,
  .buybar-cta[aria-busy="true"] .icon-heart{ animation:none; }

  /* The breath.

     A shadow that swells and settles once every seven seconds, and nothing
     else: no scale, no bounce, no colour change. The button never moves, so
     there is no geometry to shift and nothing for a thumb to chase.

     Drawn on a pseudo-element and animated on opacity rather than animating
     box-shadow directly. A box-shadow keyframe repaints the button on every
     frame of every cycle, for the whole life of the page, on the one element
     that is permanently on screen — that is a Core Web Vitals cost for a
     decoration. Opacity on a sibling layer is composited, so this runs off the
     main thread and costs nothing measurable.

     Held at rest for 80% of the cycle. The point is that it is noticed
     peripherally, once, and never becomes a thing that is happening. */
  .buybar-cta{ position:relative; }
  .buybar-cta::after{
    content:''; position:absolute; inset:0; border-radius:inherit;
    box-shadow:0 4px 10px rgba(20,22,26,.10), 0 18px 44px rgba(20,22,26,.18);
    opacity:0; pointer-events:none;
    animation:buybarBreathe 7s var(--ease) 4s infinite;
  }
  @keyframes buybarBreathe{ 0%,80%,100%{ opacity:0; } 90%{ opacity:1; } }

  /* Stops the instant it is touched, hovered or focused, and does not resume
     while the pointer is on it. Something that keeps breathing under a finger
     reads as an animation playing at you rather than as an invitation. */
  .buybar-cta:hover::after,
  .buybar-cta:active::after,
  .buybar-cta:focus-visible::after,
  .buybar-cta[aria-busy="true"]::after{ animation-play-state:paused; opacity:0; }

  /* In flight. The label swap to "Adding…" is handled by the shared
     [aria-busy] rule; this is the surface behind it settling back and holding
     still, so the button reads as working rather than as available. */
  .buybar-cta[aria-busy="true"]{
    cursor:progress; background:#263852; box-shadow:var(--sh-1);
  }

  /* The mark stays; only its motion goes. A reduced-motion preference is a
     request about movement, not about iconography, and removing the bag would
     change the button for the one group whose button should change least. */
  @media(prefers-reduced-motion:reduce){
    .buybar-cta::after{ animation:none; opacity:0; }
    .buybar-cta .icon-heart{ animation:none; }
  }

  .buybar-error{
    margin:var(--s-2) 0 0; font-size:13px; line-height:1.4;
    color:var(--danger); text-align:center;
  }

  /* Desktop is not exempt: the in-page buy box is sticky only for as long as
     the PDP's first section lasts, and past it there is no way to buy on the
     longest page in the theme without scrolling back up.

     The band still spans the window, because a blurred strip that stops short
     of the edges reads as a floating toast rather than as the page's own
     footer. The button does not: a 1200px-wide Add to Cart is a slab, not a
     control, so above the two-column breakpoint it settles to a generous but
     finite measure and centres. */
  @media(min-width:900px){
    .buybar{ padding-block:var(--s-4); }
    .buybar-cta{ max-width:420px; margin-inline:auto; min-height:56px; font-size:17px; }
  }
}
