/* ===========================================================================
   Storefront layout variants
   ===========================================================================

   Every rule here is keyed off a data-attribute on <html>, stamped by
   views/layout.ejs from services/themeRegistry.js. There is exactly ONE set of
   header markup; the owner's choice only ever changes CSS. If a variant ever
   seems to need its own markup, the axis is modelled wrong -- restructure the
   markup so CSS can express every value instead.

   Loaded only on storefront pages (see the link tag in layout.ejs), and
   deliberately a separate file from app.css: app.css is already ~12.5k lines
   and shared with the admin and landing page, so layout variants would be
   dead weight there. As one static file it is downloaded once and cached
   across every page and every shop, which is why shipping all variants to
   everyone costs far less than generating per-shop CSS would.

   DESKTOP ONLY. Everything lives inside a min-width: 992px query unless the
   axis is flagged mobile in themeRegistry.js. Below lg the storefront keeps
   exactly one header layout, so no combination of desktop choices can produce
   a broken phone header. The existing mobile flex rules in app.css are
   untouched by this file.

   The defaults (logo left, nav inline, search button, sticky) are the header
   as it shipped before the customizer, so a shop that never opens the
   customizer gets byte-identical rendering -- these rules simply do not match.
   =========================================================================== */


/* ─── header_logo = center ────────────────────────────────────────────────
   Absolute centering rather than the flex:1 trick app.css uses on mobile.
   flex:1 centres the brand between its siblings, so any difference between
   the nav width on the left and the button cluster on the right shows up as a
   visibly off-centre logo. Absolute positioning centres it on the container
   itself, which is what "centered" has to mean to survive review.

   Taking the brand out of flow means the bar's height is then driven by the
   31px buttons, so a shop with a taller logo would have it overflow the bar.
   --brand-box-h (emitted by layout.ejs from the owner's configured logo
   height) puts that height back as a min-height.                            */
@media (min-width: 992px) {
  [data-hdr-logo="center"] .navbar > .container {
    position: relative;
    /* The brand is the bar's tallest child, and taking it out of flow drops
       both its height AND Bootstrap's 5px/5px .navbar-brand padding -- the bar
       measured 10px shorter than in the default layout, so simply switching
       the logo to centered visibly shrank the header. The +10px is that
       padding, which keeps the bar the same height in every logo variant.
       Matches the estimate in layout.ejs (_brandBoxH + 10). */
    min-height: calc(var(--brand-box-h, 31px) + 10px);
  }

  [data-hdr-logo="center"] .navbar > .container > .navbar-brand {
    position: absolute;
    left: 50%;
    top: 50%;
    transform: translate(-50%, -50%);
    margin: 0;
    /* Guard against a wide logo colliding with the nav or the buttons. */
    max-width: 40%;
    justify-content: center;
  }

  [data-hdr-logo="center"] .navbar > .container > .navbar-brand img {
    min-width: 0;
    max-width: 100%;
    object-fit: contain;
    object-position: center;
  }

  /* The page nav loses its ms-3 gutter: with the brand out of flow the nav is
     now the first thing in the bar and should sit flush with the container. */
  [data-hdr-logo="center"] .navbar > .container > .shop-page-nav {
    margin-left: 0 !important;
    /* ...and it has to stop before the logo. The brand is out of flow here, so
       it reserves no space and nothing pushes the menu back: once the menu
       started growing to fill the bar its items ran straight through the
       wordmark.

       30% is the bound, not a guess: the brand is centred with max-width 40%,
       so its left edge is never further left than 50% - 20% = 30% of the
       container. A rem of clearance keeps the last item off it even at that
       worst case. A narrower logo simply leaves a gap, which reads fine
       because the menu is left-aligned; the scroller handles the rest. */
    max-width: calc(30% - 1rem);
  }
}


/* ─── Base: the search panel is a flex child of the navbar ─────────────────
   The panel used to sit outside the nav entirely. It now lives inside the
   navbar container so a layout can put it either on its own row or in the bar,
   with nothing but a flex-basis change.

   DESKTOP ONLY. navbar-expand-lg sets flex-wrap: nowrap at lg and up, which
   has to be undone for anything to occupy its own row -- both the nav-row
   layout and the toggled-open search panel depend on it.

   This deliberately does NOT apply on phones. app.css pins the mobile bar to
   flex-wrap: nowrap, and that is load-bearing: with wrapping allowed, flexbox
   assigns items to lines BEFORE shrinking them, so a tall logo takes its full
   natural width and shoves the buttons onto a second row. With nowrap the
   brand shrinks instead (flex: 1 1 auto + min-width: 0 + max-width on the img)
   and the logo scales down to fit between the two button clusters, whatever
   height the owner configured. An unscoped wrap rule here silently broke that.

   Scoped to [data-hdr-layout], so it only ever applies to a storefront
   header, never to admin or landing. */
@media (min-width: 992px) {
  [data-hdr-layout] .navbar.navbar-expand-lg,
  [data-hdr-layout] .navbar.navbar-expand-lg > .container {
    flex-wrap: wrap;
  }

  /* Full-width row, last in the bar. This is the resting behaviour for every
     layout that uses the icon button: hidden until the visitor toggles it,
     then opening as a row underneath, which is what it did before the move. */
  [data-hdr-layout] .navbar > .container > .shop-search-panel {
    flex: 0 0 100%;
    order: 30;
  }
}

/* Phones keep the bar on one line, so the open search panel cannot take a row
   of its own. It is overlaid directly under the bar instead, which is where it
   visually appeared before the move anyway. .navbar .container is already
   position: relative at this width (app.css), so this needs no new context.
   It carries its own background because, out of flow, it no longer sits on the
   header's. */
@media (max-width: 991.98px) {
  [data-hdr-layout] .navbar > .container > .shop-search-panel {
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    z-index: 1031;
    padding: .5rem .75rem .65rem;
    /* Matches the bar it hangs from, not the page behind it. */
    background: var(--navbar-bg-solid, #fff);
    border-bottom: 1px solid var(--navbar-border);
  }
}
[data-hdr-layout] .shop-search-shell {
  width: 100%;
}


/* ─── Header surfaces: one solid background ────────────────────────────────
   NOT a layout variant. This applies to every storefront header regardless of
   which layout the shop picked, and it is here rather than in app.css because
   it is storefront-header-specific and app.css is already ~12.5k lines.

   The search input has always been --card (#fff light, #222 dark) and that is
   the colour being standardised on. Two surfaces did not match it:

     * the magnifier prefix carries Bootstrap's .bg-transparent utility, so in
       LIGHT mode it showed the translucent navbar through it while the input
       beside it was solid white. Dark mode already looked right because an
       app.css rule happens to override it there, which is exactly why the
       mismatch only showed up in one theme.
     * the header buttons were transparent, so they picked up whatever was
       scrolling underneath the blurred bar.

   Opaque on purpose: --card is a solid hex in both themes, and nothing here
   applies an alpha. Hover states are deliberately left to Bootstrap. */
[data-hdr-layout] .navbar .shop-search-panel .input-group-text {
  /* .bg-transparent is `background-color: transparent !important`, so this
     needs !important to win -- specificity alone cannot beat it. */
  background-color: var(--card) !important;
}

[data-hdr-layout] .navbar .btn.btn-outline-secondary {
  background-color: var(--card);
}

/* Hover and open states have to be restated at matching specificity. The rule
   above is 0,4,0 and Bootstrap's own `.btn-outline-secondary:hover` is only
   0,2,0, so setting a resting background silently killed the hover fill in
   LIGHT mode -- the button just stayed white. Dark mode kept working purely
   because its hover rule in app.css carries !important, which is the kind of
   asymmetry that makes a bug look theme-specific when it is really about
   specificity.

   Deferring to Bootstrap's own custom properties rather than hardcoding a grey
   keeps this in step with the button variant if it is ever restyled. */
[data-hdr-layout] .navbar .btn.btn-outline-secondary:hover,
[data-hdr-layout] .navbar .btn.btn-outline-secondary:focus-visible {
  background-color: var(--bs-btn-hover-bg);
}
[data-hdr-layout] .navbar .btn.btn-outline-secondary:active,
[data-hdr-layout] .navbar .btn.btn-outline-secondary.show {
  background-color: var(--bs-btn-active-bg);
}


/* ─── Base: the cart is a direct child of the navbar ───────────────────────
   It used to live inside the .ms-auto cluster. These rules put it back
   visually: `order` places it immediately before the cluster (exactly where it
   was in DOM order) and the right margin reproduces the cluster's own gap-2,
   so every layout except search-first renders byte-identically to before. */
[data-hdr-layout] .navbar > .container > .shop-cart-btn {
  order: 11;
  flex: 0 0 auto;
  margin-right: .5rem;
}
/* app.css puts the cluster at order 11; the cart now holds that slot. */
[data-hdr-layout] .navbar > .container > .ms-auto {
  order: 12;
}
/* The word "Cart" only appears in the search-first second row, where there is
   room for it. Everywhere else the icon carries the meaning. */
[data-hdr-layout] .shop-cart-label {
  display: none;
}


/* ─── header_nav = row ─────────────────────────────────────────────────────
   The page links drop to a full-width second row under the bar.

   navbar-expand-lg sets flex-wrap: nowrap at this breakpoint, so both the
   navbar and its container have to be put back to wrap before flex-basis:100%
   can push the nav onto its own line.

   Height here is load-bearing: layout.ejs adds 35px to the first-paint
   --navbar-height estimate for this variant. That is the 21px line box plus
   the 7.2px + 7.2px of padding/margin below. Change these values and change
   the estimate in layout.ejs to match, or the first paint will jump.        */
@media (min-width: 992px) {
  /* Forces the flex line break. A pseudo-element on a flex container IS a flex
     item, so it can take an order and sit between the first row (orders up to
     12) and the second (20+). This is what lets the nav and the cart SHARE the
     second row: with the nav at flex-basis 100% it would fill the line by
     itself and push the cart onto a third. */
  [data-hdr-nav="row"] .navbar > .container::before {
    content: '';
    flex: 0 0 100%;
    height: 0;
    order: 15;
    margin-top: .45rem;
    /* The divider lives here rather than on the nav. The nav no longer spans
       the line (it shares it with the cart), so a border on the nav would stop
       short of the right edge. */
    border-top: 1px solid var(--navbar-border);
  }

  /* .625rem, not the .45rem the divider above uses, because this is the gap
     that has to match the navbar's 10px bottom PADDING -- the only thing under
     the second row. The row's items were 7.2px below the divider and 10px above
     the header edge, so they read about 2px high in their own band. Growing the
     margin cannot fix that by itself (the navbar sizes to its content, so the
     padding below moves down with it); matching the two numbers is what
     centres it.

     Both second-row items carry the same value. Nudging only the cart would
     have broken its alignment with the links, which do sit level with it. */
  [data-hdr-nav="row"] .navbar > .container > .shop-page-nav {
    order: 20;
    /* basis 0, not auto: this container WRAPS (that is how the second row
       exists), and a wrapping flex container assigns items to lines using
       their base size before it shrinks anything. With the menu's basis set to
       its content width, a long menu filled the line and pushed the cart onto
       a third row of its own. Basis 0 lets the cart keep its place and the
       menu take what is left, scrolling for the rest. 1px rather than 0:
       a zero basis measured as a zero-width nav with the cart still wrapped,
       while one pixel gives 856px and the cart alongside. */
    flex: 1 1 1px;
    justify-content: flex-start;
    margin-left: 0 !important;
    margin-top: .625rem;
  }

  /* Cart joins the links on the second row, pushed to the far right. It stays
     a button here -- only the label appears, because the row has space for it. */
  [data-hdr-nav="row"] .navbar > .container > .shop-cart-btn {
    order: 21;
    /* The menu does the pushing now, via flex-grow. An auto margin here would
       absorb ALL the line's free space before flex-grow is even considered,
       which left the menu at zero width the moment its basis became 0. Same
       result on screen -- cart hard right -- reached the other way round. */
    margin-left: 0;
    margin-right: 0;
    /* Same value as .shop-page-nav above -- see the note there. */
    margin-top: .625rem;
    display: inline-flex;
    align-items: center;
    gap: .4rem;
    /* app.css pins .shop-cart-btn to a 31px square with zero padding, because
       it is normally one of a closed set of icon-only header buttons. This is
       the one place it carries a label, so the box has to grow to fit it --
       otherwise the text spills outside the button's border. Height stays 31px
       so it still lines up with the rest of the header.
       !important is required to beat the pinned box, which is itself
       !important. */
    width: auto !important;
    padding: 0 .65rem !important;
  }

  [data-hdr-nav="row"] .shop-cart-label {
    display: inline;
  }
}


/* ─── header_search = inline ───────────────────────────────────────────────
   The search field sits IN the bar, beside the logo, permanently visible.

   It reuses the existing panel rather than adding a second search UI, so the
   typeahead, the results dropdown and the keyboard handling in navigation.js
   all work untouched and there is nothing to drift out of sync. The panel just
   stops being a full-width row and becomes an ordinary flex item.

   The display rule has to beat Bootstrap's .d-none (display:none !important),
   hence !important on an ID selector to win on specificity. navigation.js
   still toggles .d-none as visitors open and close; here that has no visible
   effect, which is the intent.

   Desktop only. A phone header has no room for a permanent field, so below lg
   every layout keeps the icon button and the panel stays a toggled row.      */
@media (min-width: 992px) {
  [data-hdr-search="inline"] .navbar > .container > .shop-search-panel {
    display: block !important;
    /* Between the brand (order 0) and the button cluster (order 12), and
       ahead of the nav row (order 20) so the links still land underneath. */
    order: 5;
    flex: 0 1 420px;
    min-width: 180px;
    margin: 0 1rem;
    /* Undo the row styling: in the bar it is a field, not a panel. */
    border-top: 0;
    padding: 0;
  }

  /* Centring the field on the BAR, not merely between its neighbours.
     The brand and the button cluster are given an equal flex share of the
     leftover space, so the fixed-width field lands dead centre regardless of
     how wide the shop name or the button cluster happen to be. Auto margins
     would only centre it between the two, leaving it visibly off-centre
     whenever those differ, which they always do.
     Done in flow rather than with absolute positioning (as the centred LOGO
     uses) because this bar has a second row: an absolutely positioned field
     would have to be told where row one ends, and that moves with the
     owner's configured logo height. */
  [data-hdr-search="inline"] .navbar > .container > .navbar-brand {
    flex: 1 1 0;
    min-width: 0;
    /* Bootstrap gives .navbar-brand a 1rem margin-right and nothing on the
       left. With equal flex shares either side that asymmetry is the whole
       error: it pushed the field half a margin (8px) right of centre. The
       field's own 1rem margins already provide the gap. */
    margin-right: 0;
  }
  [data-hdr-search="inline"] .navbar > .container > .ms-auto {
    flex: 1 1 0;
    min-width: 0;
    justify-content: flex-end;
  }

  [data-hdr-search="inline"] .navbar > .container > .shop-search-toggle {
    display: none !important;
  }

  /* ── Search field size: wide ─────────────────────────────────────────────
     Snaps the bar to the CATALOG PAGE's own column grid instead of centring a
     fixed-width field. The catalog row splits the same .container this header
     uses into col-lg-3 + col-lg-9, so 25% is exactly where the sidebar ends
     and the product cards begin -- no magic pixel value, and it tracks every
     breakpoint and container max-width for free.

       logo    a 25% column, capped like the sidebar it sits above
       field   starts at that boundary, fills to the button cluster
       buttons content width only, so the field ends where they begin

     The three flex declarations have to change together: leaving .ms-auto on
     `1 1 0` would let it claim half the leftover space and the field would
     stop well short of the account button. */
  [data-hdr-search="inline"][data-hdr-searchw="wide"] .navbar > .container > .navbar-brand {
    /* 25% is the col-lg-3 share, and the +3.75px is a quarter of the catalog
       row's 15px gutter (--bs-gutter-x on .shop-catalog-row). The row pulls
       itself out by half a gutter and pads each column back in by half, so the
       first CARD sits 3.75px right of a plain 25% of the header's container.
       Without the correction the field lands 3.7px left of the cards it is
       supposed to line up with -- measured, not guessed. */
    flex: 0 0 calc(25% + 3.75px);
    max-width: calc(25% + 3.75px);
    min-width: 0;
  }
  /* Keeps a tall or wide logo inside its column rather than pushing the
     boundary right and taking the field with it.
     object-fit is the load-bearing half: the logo carries an inline
     `height:Npx; width:auto` from the owner's size slider, so max-width alone
     clamped the width while the height stayed put and squashed the artwork the
     moment the configured size outgrew the column. `contain` letterboxes it
     inside the same box instead, so the aspect ratio survives any slider
     value. */
  [data-hdr-search="inline"][data-hdr-searchw="wide"] .navbar > .container > .navbar-brand img {
    /* The COLUMN is 25% + 3.75px so the field beside it starts level with the
       product cards. The logo is capped 15px tighter than that, because the
       thing it should not outgrow is the sidebar CARD, not the column: the row
       pads each column in by half its 15px gutter, so the categories and
       filters boxes are exactly 15px narrower than the column and share its
       left edge. Measured: column 327.8, card 312.8, both starting at 144.5. */
    max-width: calc(100% - 15px);
    object-fit: contain;
    object-position: left center;
  }
  [data-hdr-search="inline"][data-hdr-searchw="wide"] .navbar > .container > .shop-search-panel {
    flex: 1 1 auto;
    /* The left margin would push the field past the column boundary it is
       meant to sit on. */
    margin-left: 0;
    /* .5rem, not the 1rem the centred variant uses: it matches the 8px gap the
       button cluster already puts between account, theme and language, so the
       field reads as one more item in that row rather than a separate block. */
    margin-right: .5rem;
  }
  [data-hdr-search="inline"][data-hdr-searchw="wide"] .navbar > .container > .ms-auto {
    flex: 0 0 auto;
  }

  /* Match the button row exactly. btn-sm resolves to 31px (.25rem padding top
     and bottom + a .875rem/1.5 line box + 1px borders), and a 38px default
     form-control beside 31px buttons reads as misaligned. */
  [data-hdr-search="inline"] #shopSearchPanel .input-group {
    position: relative;
    height: 31px;
  }
  [data-hdr-search="inline"] #shopSearchInput,
  [data-hdr-search="inline"] #shopSearchPanel .input-group-text {
    height: 31px;
    min-height: 31px;
    padding-top: 0;
    padding-bottom: 0;
    font-size: .875rem;
  }
  /* Room for the clear button, which overlays the right end of the field. */
  [data-hdr-search="inline"] #shopSearchInput {
    padding-right: 1.75rem;
    border-top-right-radius: var(--bs-border-radius, .375rem);
    border-bottom-right-radius: var(--bs-border-radius, .375rem);
  }

  /* Clear button, iOS-style: a small circle INSIDE the field, and only once
     the visitor has actually typed something.
     :placeholder-shown is what makes "only when there is text" pure CSS -- it
     is true while the field is empty, so the button is revealed by its
     absence. No JS, no input listener, nothing to keep in sync. Clicking it
     already clears the field: navigation.js's existing close handler resets
     the value and hides the results. */
  [data-hdr-search="inline"] #shopSearchClose {
    position: absolute;
    top: 50%;
    right: .35rem;
    transform: translateY(-50%);
    z-index: 5;
    display: none;
    align-items: center;
    justify-content: center;
    width: 17px;
    height: 17px;
    padding: 0;
    border: 0;
    border-radius: 50%;
    /* Neutral grey with a light glyph, the iOS shape. Deliberately a literal
       rgba rather than a theme var: it has to read as a filled circle against
       BOTH the light and dark field backgrounds, and --border is tuned to be
       barely visible, which left it looking like a stray x. */
    /* !important because app.css paints every button inside the search panel
       with `.shop-search-panel .btn-outline-secondary { background-color:
       var(--card) }`, and this element is one of those buttons. Without it the
       circle renders card-white on a card-white field and reads as a bare x. */
    background: rgba(130, 130, 138, .8) !important;
    color: #fff !important;
    font-size: .55rem;
    line-height: 1;
  }
  [data-hdr-search="inline"] #shopSearchClose:hover,
  [data-hdr-search="inline"] #shopSearchClose:focus {
    background: rgba(130, 130, 138, 1) !important;
    color: #fff !important;
  }
  [data-hdr-search="inline"] #shopSearchInput:not(:placeholder-shown) + #shopSearchClose {
    display: inline-flex;
  }

  /* app.css pushes the right-hand cluster over with `margin-left: auto` on the
     SEARCH TOGGLE, and gives .ms-auto `margin-left: 0 !important` because the
     toggle already did the job. Hiding the toggle therefore removes the only
     thing holding the cart/account cluster to the right edge, and it collapses
     to the left of the bar. Hand the auto margin back to the cluster itself. */
  [data-hdr-search="inline"] .navbar > .container > .ms-auto {
    margin-left: auto !important;
  }

  /* Checkout hides the search entirely: a search box on the payment step is a
     conversion leak, not a feature. */
  [data-hdr-search="inline"] header.shop-checkout-mode .shop-search-panel {
    display: none !important;
  }
}


/* ─── header_sticky = static ───────────────────────────────────────────────
   The header stops being fixed and scrolls away with the page.

   The clearance that content uses to sit below a fixed bar is NOT unwound
   here. --navbar-height has many consumers (main's padding-top, sticky
   sidebar offsets, scroll-margin for anchors) and overriding each one would
   be a list that rots. Instead public/js/header-height.js publishes
   --navbar-height as 0 whenever it measures a static header, so every
   consumer corrects at once. This rule only changes the positioning.

   Applies at every width: a phone header that scrolls away is a real choice,
   and this axis is flagged mobile:true in themeRegistry.js.                 */
[data-hdr-sticky="static"] body > header {
  position: static;
  top: auto;
  /* Keeps the announcement bar's transition from animating a static bar. */
  transition: none;
}

/* With the bar in flow the blur has nothing behind it to blur and costs a
   compositing layer for no reason. Same colour as the see-through bar, opaque
   -- not --bg, which is the page behind it and a different colour. */
[data-hdr-sticky="static"] body > header {
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
  background: var(--navbar-bg-solid, #fff);
}


/* ─── header_bg = solid (sub-option of "stays visible") ────────────────────
   By default a sticky header is see-through: --navbar-bg is 85% opaque and a
   12px backdrop blur frosts whatever scrolls beneath it. This turns that off
   so content passes cleanly behind an opaque bar instead.

   Only offered while the header is sticky, because a static bar scrolls away
   and never has anything behind it to show through -- the rule above already
   makes that case solid.

   No effect on touch devices either way: app.css already forces a solid
   background under `(hover: none) and (pointer: coarse)`, because
   backdrop-filter breaks position:fixed during momentum scroll there. That is
   why this axis is flagged desktop-only in themeRegistry. */
[data-hdr-bg="solid"] body > header {
  /* The SAME colour as the see-through bar, just without the transparency.
     Using --bg here tinted the header a different colour (--bg is the PAGE
     background, --navbar-bg is the bar's own), so switching to Solid looked
     like it changed the design rather than only removing the blur. */
  background: var(--navbar-bg-solid, #fff);
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
