/* Shared header. Figma node 15:410: a 1200px container holding a right-aligned
   utility row above an 80px main row. No tinted full-width bar, no second
   desktop navigation row. */

/* On a category page the phone viewport scrolls several thousand pixels; without
   a sticky header the search box, cart and menu are unreachable for that whole
   distance. The mobile drawer sits at z-index 100, so the header stays below it. */
.site-header {
  position: sticky;
  top: 0;
  z-index: var(--header-z);
  background-color: var(--color-canvas);
}

.site-header__container,
.site-header__utility-container {
  width: min(100%, calc(var(--container-max) + 2 * var(--gutter)));
  margin-inline: auto;
  padding-inline: var(--gutter);
}

/* Logged-in storefront tools use the same warm utility language as the site
   instead of nopCommerce's default dark developer bar. */
.admin-header-links {
  min-height: var(--admin-bar-height);
  display: flex;
  align-items: center;
  justify-content: flex-start;
  flex-wrap: wrap;
  gap: var(--space-md);
  padding: var(--space-2xs) var(--gutter);
  background-color: var(--color-brand-soft);
  color: var(--color-ink);
  font-size: var(--font-size-xs);
  line-height: var(--line-height-snug);
}

.admin-header-links a {
  font-weight: var(--font-weight-bold);
  text-decoration: underline;
  text-underline-offset: var(--space-3xs);
}

.admin-header-links .impersonate {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  justify-content: center;
  gap: var(--space-xs);
}

.admin-header-links .finish-impersonation {
  min-height: var(--admin-bar-height);
  display: inline-flex;
  align-items: center;
  padding-inline: var(--space-md);
  border-radius: var(--radius-pill);
  background-color: var(--color-action);
  text-decoration: none;
}

/* ---- Utility row ---- */

.site-header__utility {
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--space-xl);
  padding-block: var(--space-xl) var(--space-md);
}

.site-header__catalog-links {
  display: inline-flex;
  align-items: center;
  gap: var(--space-md);
  padding-right: var(--space-xl);
  border-right: 1px solid var(--color-border);
}

.site-header__utility-link {
  font-size: var(--font-size-md);
  font-weight: var(--font-weight-bold);
  line-height: var(--line-height-tight);
  text-decoration: none;
}

.site-header__utility-link:hover {
  text-decoration: underline;
}

/* A link led by a glyph (the puzzle page's die) keeps it on the text's line. */
.site-header__utility-link--icon {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
}

/* The page being shown: its link keeps an amber underline, marked by the
   server with aria-current, so the highlight is there in the first response. */
.site-header__utility-link[aria-current="page"],
.catalog-navigation__links a[aria-current="page"]:not(.btn--quiet),
.catalog-navigation__tiles [aria-current="page"] .category-tile__name {
  text-decoration: underline;
  text-decoration-color: var(--color-action);
  text-decoration-thickness: var(--stroke-control);
  text-underline-offset: var(--space-3xs);
}

/* Two options separated by a hairline rule: the current one is ink, the other is
   the muted grey used for every other secondary action in the utility row. */
.language-switch {
  display: inline-flex;
  align-items: center;
}

.language-switch__option {
  display: inline-flex;
  align-items: center;
  padding-inline: var(--space-2xs);
  color: var(--color-muted);
  font-size: var(--font-size-xs);
  font-weight: var(--font-weight-medium);
  text-decoration: none;
}

.language-switch__option + .language-switch__option {
  border-left: 1px solid var(--color-border);
}

.language-switch__option--current {
  color: var(--color-ink);
  font-weight: var(--font-weight-bold);
}

/* The utility row and phone already sit in the desktop header; the drawer
   copies would be redundant once the viewport is wide enough for the header
   to show them itself. */
@media (min-width: 62rem) {
  .catalog-navigation__links,
  .catalog-navigation__language {
    display: none;
  }
}

/* ---- Main row ---- */

.site-header__main {
  height: var(--header-height);
  display: flex;
  align-items: center;
  gap: var(--space-2xl);
}

/* Brand: a solid cream tile spanning the full row height (136 x 80). */
.brand-mark {
  flex: 0 0 var(--logo-tile-width);
  position: relative;
  align-self: stretch;
  width: var(--logo-tile-width);
  display: flex;
  flex-direction: column;
  justify-content: center;
  padding-inline: var(--space-md);
  background-color: var(--color-brand-soft);
  color: var(--color-ink);
  text-decoration: none;
}

.brand-mark__glyph {
  position: absolute;
  top: 0;
  right: var(--space-2xs);
  width: var(--logo-glyph-width);
  height: var(--logo-glyph-height);
}

.brand-mark__wordmark {
  display: flex;
  flex-direction: column;
  line-height: var(--line-height-tight);
}

/* Figma: both wordmark lines are Raleway 900 / 24 (node 4:19). */
.brand-mark__line1,
.brand-mark__line2 {
  font-size: var(--font-size-xl);
  font-weight: var(--font-weight-black);
}

/* ---- Search ---- */

/* The field takes whatever the row leaves after the brand, the phone number
   and the actions, so a store showing no number gives its room to the search
   and the actions always end on the container's edge. */
.site-header__search {
  position: relative;
  flex: 1 1 auto;
}

/* The field and its dismiss button. The button only appears in the phone
   drawer, so at every other width the row is the field alone. */
.site-search__bar {
  display: flex;
  align-items: center;
  gap: var(--space-sm);
}

.site-search__close.drawer-close {
  display: none;
}

.site-search__form {
  display: flex;
  flex: 1 1 auto;
  min-width: 0;
  align-items: center;
  gap: var(--space-md);
  border-bottom: 1px solid var(--color-border);
}

.site-search__submit {
  order: -1;
  flex-shrink: 0;
  border: 0;
  background: transparent;
  cursor: pointer;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0;
}

.site-search__input {
  flex: 1;
  min-width: 0;
  border: 0;
  background: transparent;
  font-size: var(--font-size-md);
  padding-block: var(--space-2xs);
  color: var(--color-ink);
}

.site-search__input::placeholder {
  color: var(--color-subtle);
  font-weight: var(--font-weight-medium);
}

/* ---- Search suggestions ----
   Figma has no suggestion frame (see CLAUDE.md, "Figma source-of-truth
   deviations"): the panel is the listbox primitive from controls.css holding
   book-card typography and the shared price component.

   While the field is in use the page dims behind it. The backdrop sits in the
   header's stacking context at 0, the field and the panel above it at 1, so
   only they stay lit — the same arrangement as the phone sort panel. */
.site-header__search-backdrop {
  z-index: 0;
}

/* The magnifier and the input are one field: lit together on one surface,
   with one focus ring around both. The surface extends past the field as a
   canvas-coloured halo (a box-shadow spread, so the resting layout and the
   underline do not move); on the white header it is invisible, over the
   backdrop it gives the field room. */
.site-header__search .site-search__form {
  position: relative;
  z-index: 1;
  border-radius: var(--radius-sm);
  background-color: var(--color-canvas);
  box-shadow: 0 0 0 var(--search-field-halo) var(--color-canvas);
}

.site-header__search .site-search__form:focus-within {
  outline: var(--focus-ring);
  outline-offset: var(--search-field-halo);
}

.site-header__search .site-search__input:focus-visible,
.site-header__search .site-search__submit:focus-visible {
  outline: none;
}

.search-suggest {
  position: absolute;
  z-index: 1;
  top: calc(100% + var(--search-field-halo) + var(--space-2xs));
  right: calc(-1 * var(--search-field-halo));
  left: calc(-1 * var(--search-field-halo));
  max-height: var(--search-suggest-max-height);
}

.search-suggest__group + .search-suggest__group {
  margin-top: var(--space-3xs);
  padding-top: var(--space-3xs);
  border-top: 1px solid var(--color-border);
}

.search-suggest__heading {
  margin: 0;
  padding: var(--space-2xs) var(--space-xs) var(--space-3xs);
  font-size: var(--font-size-3xs);
  font-weight: var(--font-weight-semibold);
  letter-spacing: var(--letter-spacing-caps);
  text-transform: uppercase;
  color: var(--color-muted);
}

.search-suggest__option {
  display: flex;
  align-items: center;
  gap: var(--space-xs);
  text-decoration: none;
}

.search-suggest__cover {
  flex: 0 0 var(--search-suggest-cover-width);
  width: var(--search-suggest-cover-width);
  aspect-ratio: var(--cart-cover-aspect);
  object-fit: cover;
  border-radius: var(--radius-sm);
  background-color: var(--color-surface);
}

.search-suggest__text {
  flex: 1 1 auto;
  min-width: 0;
  display: flex;
  flex-direction: column;
  gap: var(--space-3xs);
}

.search-suggest__title {
  font-size: var(--font-size-sm);
  font-weight: var(--font-weight-bold);
  line-height: var(--line-height-tight);
  overflow-wrap: anywhere;
}

.search-suggest__meta {
  font-size: var(--font-size-xs);
  line-height: var(--line-height-tight);
  color: var(--color-muted);
}

/* The price column keeps its width and the title wraps instead: a sold
   book's label stands where the price would, so it must not shrink either. */
.search-suggest__option .price,
.search-suggest__option .out-of-stock {
  flex: 0 0 auto;
  align-items: flex-end;
}

.search-suggest__option .price__current {
  font-size: var(--font-size-sm);
}

.search-suggest__empty {
  margin: 0;
  padding: var(--space-xs);
  font-size: var(--font-size-xs);
  color: var(--color-muted);
}

.search-suggest__all {
  margin-top: var(--space-3xs);
  font-weight: var(--font-weight-semibold);
}

/* ---- Phone and actions ---- */

.site-header__phone {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2xs);
  text-decoration: none;
  color: var(--color-ink);
  font-size: var(--font-size-md);
  white-space: nowrap;
}

.header-actions__list {
  display: flex;
  align-items: center;
  gap: var(--space-md);
  list-style: none;
  margin: 0;
  padding: 0;
}

/* Centre each entry on the row's midline: as inline boxes the icon links sat
   on the text baseline, a few pixels above the phone number and log out.
   :where() keeps this at class specificity, so the phone rule that hides
   the logout entry still wins. */
.header-actions__list > :where(li) {
  display: flex;
  align-items: center;
}

.header-actions__link {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  text-decoration: none;
  color: var(--color-ink);
}

/* The favourites heart is the toggle's own glyph, drawn at the size and in the
   grey of the header's cart and account icons beside it. */
.header-actions__link .heart-icon {
  width: var(--header-icon-size);
  height: var(--header-icon-size);
  color: var(--color-subtle);
}

/* Placement only. Appearance is .btn--quiet in controls.css: the same quiet
   text action used for "continue shopping" and "clear filters". Log in and
   log out are the same kind of action, so they look the same. */
.header-actions__login,
.header-actions__logout {
  white-space: nowrap;
}

/* Cart count: a filled amber pill anchored to the top-right of the cart icon.
   Grows from a circle to an oval once the count reaches double digits. Its
   geometry is in em of its own number (18px circle, 5px padding, 6px and 8px
   offsets at the 12px count), so the pill always fits the figure it holds,
   whatever the page's density. */
.header-actions__badge {
  position: absolute;
  top: -0.5em;
  right: -0.6667em;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.5em;
  height: 1.5em;
  padding-inline: 0.4167em;
  border-radius: var(--radius-pill);
  background-color: var(--color-action);
  color: var(--color-ink);
  font-size: var(--font-size-3xs);
  font-weight: var(--font-weight-bold);
  line-height: 1;
}

.header-actions__badge.is-updated {
  animation: cart-badge-bump var(--motion-tile) var(--ease-out);
}

@keyframes cart-badge-bump {
  50% { transform: scale(1.28); }
}

.header-actions__link:hover .header-actions__badge {
  color: var(--color-ink);
}

/* ---- Mobile menu ---- */

.mobile-menu-toggle {
  display: none;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  gap: var(--space-3xs);
  width: var(--tap-target);
  height: var(--tap-target);
  border: 0;
  background: transparent;
  cursor: pointer;
}

.mobile-menu-toggle__bar {
  display: block;
  width: 1.5rem;
  height: 0.125rem;
  background-color: var(--color-ink);
}

.catalog-navigation {
  display: none;
}

.catalog-navigation__header {
  display: flex;
  justify-content: flex-end;
  margin-bottom: var(--space-sm);
}

/* The native MainMenu widget's own top-level collapse toggle is redundant
   with our mobile-menu-toggle button, which already governs visibility of
   the whole #catalog-navigation panel; hide it without touching the
   per-item submenu toggles (.menu__item-toggle) that still need it. */
.catalog-navigation > .menu-container > .menu__toggle {
  display: none;
}

.catalog-navigation .menu {
  display: flex;
  flex-direction: column;
  gap: 0;
  list-style: none;
  margin: 0;
  padding: var(--space-xs) 0;
}

.catalog-navigation .menu__item {
  min-height: var(--tap-target);
  display: flex;
  align-items: center;
}

.catalog-navigation .menu__item a {
  color: var(--color-ink);
  text-decoration: none;
  font-weight: var(--font-weight-medium);
}

/* The way into the catalog, under the home page row: the home page's own
   listing tiles side by side, a soft surface that stands out from the plain
   rows without competing with the amber actions. */
.catalog-navigation__tiles {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-sm);
  margin-block: var(--space-xs);
}

.catalog-navigation__tiles .category-tile.category-tile--plain {
  min-width: 0;
}

/* The desktop header's utility links, phone and logout, listed under the
   native main menu with the same row rhythm. */
.catalog-navigation__links {
  list-style: none;
  margin: var(--space-sm) 0 0;
  padding: var(--space-sm) 0 0;
  border-top: 1px solid var(--color-border);
}

.catalog-navigation__links li {
  min-height: var(--tap-target);
  display: flex;
  align-items: center;
}

.catalog-navigation__links a:not(.btn--quiet) {
  color: var(--color-ink);
  text-decoration: none;
  font-weight: var(--font-weight-medium);
}

/* A drawer row is tapped as a row: the link fills it edge to edge and for its
   whole height, so a thumb landing beside the label still opens the page.
   `justify-content` is set here because a row is left-aligned even when the
   link is a shared control that centres its label elsewhere (.btn--quiet). */
.catalog-navigation .menu__item a,
.catalog-navigation__links a {
  display: flex;
  flex: 1;
  align-items: center;
  justify-content: flex-start;
  min-height: var(--tap-target);
}

@media (hover: hover) and (pointer: fine) {
  .catalog-navigation__links a:not(.btn--quiet):hover,
  .catalog-navigation .menu__item a:hover,
  .header-actions__link:hover,
  .site-header__phone:hover {
    color: var(--color-action);
  }
}

@media (max-width: 61.9375rem) {
  .site-header__utility,
  .site-header__phone {
    display: none;
  }

  .catalog-navigation__language {
    margin-top: var(--space-lg);
    padding-top: var(--space-lg);
    border-top: 1px solid var(--color-border);
  }

  .site-header__main {
    height: auto;
    flex-wrap: wrap;
    gap: var(--space-sm);
    padding-block: var(--space-sm);
  }

  /* Menu, brand, then cart and account pushed to the end; the search box
     wraps onto its own full-width row beneath them. Phone and logout are
     reachable from the menu drawer. */
  .site-header__search {
    order: 1;
    flex: 1 0 100%;
  }

  .header-actions__logout-item {
    display: none;
  }

  .site-header__main > .header-actions {
    margin-inline-start: auto;
  }

  /* The tile is at least a tap target tall, so it matches the full header
     (where the menu button sets the row height) in a minimal header whose
     row holds nothing but the brand. */
  .brand-mark {
    flex-basis: var(--logo-tile-width-compact);
    width: var(--logo-tile-width-compact);
    min-height: var(--tap-target);
    padding-inline: var(--space-sm);
  }

  .brand-mark__line1,
  .brand-mark__line2 {
    font-size: var(--font-size-brand);
  }

  .mobile-menu-toggle {
    display: flex;
  }

  .header-actions__list {
    gap: var(--space-md);
  }

  .catalog-navigation {
    display: block;
    position: fixed;
    inset: 0;
    z-index: var(--drawer-z);
    background-color: var(--color-canvas);
    padding: var(--gutter);
    overflow-y: auto;
    transform: translateX(-100%);
    visibility: hidden;
  }

  .catalog-navigation--open {
    transform: translateX(0);
    visibility: visible;
  }

  @media (prefers-reduced-motion: no-preference) {
    .catalog-navigation {
      transition: transform var(--motion-drawer) var(--ease-out);
    }
  }
}

/* ---- Phone: the search is a drawer ----
   On a phone the on-screen keyboard covers the lower half of the viewport, so
   a field sitting in the header row leaves its suggestions behind the keyboard.
   Focusing the field therefore hands the same block to a drawer: it takes the
   screen, puts the field at the top of it and gives the suggestions everything
   below.

   The drawer is sized to the VISUAL viewport - the part the keyboard leaves -
   which only the script can measure, so it arrives as the two custom properties
   below (docs/style-guide.md rule 9's runtime-value exception). Until then, and
   whenever there is no keyboard, the fallbacks fill the screen. */
@media (max-width: 36.25rem) {
  /* The field is away, not gone: the row it left keeps its height so the page
     behind the drawer does not jump. */
  .site-header__search.is-search-open {
    min-height: var(--search-field-height);
  }

  .site-search--open {
    position: fixed;
    top: 0;
    right: 0;
    left: 0;
    z-index: var(--drawer-z);
    display: flex;
    flex-direction: column;

    /* The sheet is white exactly as far as it has something to show, and the
       dimmed page carries on below it. A white that ran to the keyboard gave
       the list no visible end: with three results and half a screen of blank
       white under them, nothing said whether that was all of them or the rest
       was below the fold. So the ground here is left transparent for the
       backdrop that is already behind the drawer - the same panel-over-dimmed-
       page the field has on a desktop (style-guide rule 2) - and the bar and
       the list carry the white themselves. */
    background-color: transparent;

    /* The sheet itself never scrolls - the list inside it does - and a drag
       that lands on it is its own, never the page's. `overscroll-behavior`
       only governs scroll containers, so `overflow` has to make one of the
       sheet for the containment to mean anything; search-suggest.js cancels
       the drags that never reach a scroll container at all. */
    overflow: hidden;
    overscroll-behavior: contain;

    /* The sheet stops where the keyboard starts, so everything inside lands
       above the keyboard by flexbox arithmetic alone, with no coordinates to
       get wrong. search-suggest.js measures the keyboard and fills in the
       height; until it has one (and on a browser that never reports one) the
       reserve below stands, so the list is clear of the keyboard from the
       first frame.

       The strip behind the keyboard is deliberately NOT the sheet's. While the
       keyboard is up, iOS Safari tints its bottom bar from the fixed element it
       hit-tests just above the foot of the layout viewport - behind the
       keyboard. A transparent, full-screen sheet there was sampled as rendered
       pixels (the dim), and that grey was retained for as long as the sampled
       element had a renderer, which this block, being the header's search
       field, always does. Leaving that strip to .page-backdrop instead gives
       Safari a dimming layer (fixed, translucent, childless - WebKit's own
       category), which goes out of the render tree when the search closes and
       takes its colour with it.

       That holds with the keyboard gone, too, hence the floor. Once Safari has
       a colour for that edge it keeps sampling it, keyboard or not, and a sheet
       reaching the foot when the keyboard was dismissed became the element the
       colour was on record against - still grey after the search closed, until
       the next navigation.

       The clearance goes with the keyboard, and the script zeroes it when
       there is none: it is there for the form accessory bar iOS floats above
       the keyboard without counting, so with the keyboard gone it would be a
       band of dead space between the last row and the browser's own chrome. */
    bottom: max(calc(var(--search-keyboard-height, var(--keyboard-reserve)) + var(--search-keyboard-clearance, var(--keyboard-clearance))), var(--edge-sample-inset));
  }

  /* Drawer chrome, and only that: in the header row the field is the row, and
     padding here would push the header open around a field that has not moved. */
  .site-search--open .site-search__bar {
    padding: var(--gutter) var(--gutter) var(--space-md);
    background-color: var(--color-canvas);
  }

  .site-search--open .site-search__close.drawer-close {
    display: inline-flex;
  }

  /* Safari draws its own clear button inside a search field. In the drawer the
     ✕ beside the field already does that job, and two of them a thumb apart
     read as two different actions. */
  .site-search--open .site-search__input::-webkit-search-cancel-button {
    display: none;
  }

  /* In the drawer the panel is the body, not a popup over the page: it drops
     the floating card's frame and takes whatever the field row and the
     keyboard reserve leave, scrolling inside it. */
  .site-search--open .search-suggest {
    position: static;
    flex: 0 1 auto;
    min-height: 0;
    max-height: none;
    padding: 0 var(--gutter) var(--gutter);
    border: 0;
    border-radius: 0;
    box-shadow: none;
    background-color: var(--color-canvas);
    overscroll-behavior: contain;
  }

  /* "The list carries on below." It is the last of the white, under the list
     it belongs to, and it goes as soon as the last row has been reached - so
     the white ending is itself the answer to whether there is more. */
  .search-suggest__more {
    display: flex;
    align-items: center;
    justify-content: center;
    padding-block: var(--space-3xs) var(--space-xs);
    background-color: var(--color-canvas);
    color: var(--color-subtle);
  }

  /* The sheet has no entrance and no exit: it is here, or the field is back in
     the header row. This block IS that field, so fading the sheet fades the
     field with it - for the length of the fade the field hangs half-transparent
     at the sheet's position, over the brand mark and the cart it left behind,
     with the whole page washed out behind it. The one thing that does move is
     the list, which gives way to the keyboard at the keyboard's own pace
     instead of snapping the moment the measurement lands. */
  @media (prefers-reduced-motion: no-preference) {
    .site-search--open {
      transition: bottom var(--motion-drawer) var(--ease-out);
    }
  }
}
