﻿/* ==========================================================================
   Blissporium Design System — Utilities & Components
   Layout utilities, buttons, forms, cards, and shared UI components.
   Mobile-first. Breakpoints: sm 480 / md 768 / lg 1024 / xl 1280 / 2xl 1536
   ========================================================================== */

/* ==========================================================================
   1. LAYOUT — Container, Grid, Flex
   ========================================================================== */

.container {
  width: 100%;
  max-width: var(--container-xl);
  margin-inline: auto;
  padding-inline: var(--container-gutter);
}

.container--sm { max-width: var(--container-sm); }
.container--md { max-width: var(--container-md); }
.container--lg { max-width: var(--container-lg); }

.grid {
  display: grid;
  gap: var(--space-6);
}

/* minmax(0, 1fr) rather than a bare 1fr — a grid item's default
   min-width:auto otherwise lets its own content (e.g. a long email
   address or an unbreakable phone number) force its track wider than
   its actual 1fr share, overflowing the grid instead of the content
   wrapping/shrinking to fit. Classic CSS-grid overflow footgun. */
.grid-cols-1 { grid-template-columns: repeat(1, minmax(0, 1fr)); }
.grid-cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid-cols-3 { grid-template-columns: repeat(3, minmax(0, 1fr)); }
.grid-cols-4 { grid-template-columns: repeat(4, minmax(0, 1fr)); }
.grid-cols-6 { grid-template-columns: repeat(6, minmax(0, 1fr)); }

/* Common product/category grid presets */
.grid-products {
  display: grid;
  gap: var(--space-6);
  grid-template-columns: repeat(2, minmax(0, 1fr));
}

.flex          { display: flex; }
.inline-flex   { display: inline-flex; }
.flex-col      { flex-direction: column; }
.flex-wrap     { flex-wrap: wrap; }
.flex-nowrap   { flex-wrap: nowrap; }
.flex-1        { flex: 1 1 0%; }
.flex-auto     { flex: 1 1 auto; }
.flex-none     { flex: none; }

.items-start    { align-items: flex-start; }
.items-center   { align-items: center; }
.items-end      { align-items: flex-end; }
.items-stretch  { align-items: stretch; }

.justify-start   { justify-content: flex-start; }
.justify-center  { justify-content: center; }
.justify-end     { justify-content: flex-end; }
.justify-between { justify-content: space-between; }
.justify-around  { justify-content: space-around; }

.gap-1  { gap: var(--space-1); }
.gap-2  { gap: var(--space-2); }
.gap-3  { gap: var(--space-3); }
.gap-4  { gap: var(--space-4); }
.gap-6  { gap: var(--space-6); }
.gap-8  { gap: var(--space-8); }

/* --------------------------------------------------------------------
   Spacing utilities (margin / padding, 4px grid)
   -------------------------------------------------------------------- */
.m-0  { margin: 0; }
.mt-1 { margin-top: var(--space-1); }  .mt-2 { margin-top: var(--space-2); }
.mt-3 { margin-top: var(--space-3); }  .mt-4 { margin-top: var(--space-4); }
.mt-6 { margin-top: var(--space-6); }  .mt-8 { margin-top: var(--space-8); }
.mt-12{ margin-top: var(--space-12); } .mt-16{ margin-top: var(--space-16); }

.mb-1 { margin-bottom: var(--space-1); } .mb-2 { margin-bottom: var(--space-2); }
.mb-3 { margin-bottom: var(--space-3); } .mb-4 { margin-bottom: var(--space-4); }
.mb-6 { margin-bottom: var(--space-6); } .mb-8 { margin-bottom: var(--space-8); }
.mb-12{ margin-bottom: var(--space-12); }

.mx-auto { margin-inline: auto; }

.p-0  { padding: 0; }
.p-2  { padding: var(--space-2); }  .p-3  { padding: var(--space-3); }
.p-4  { padding: var(--space-4); }  .p-6  { padding: var(--space-6); }
.p-8  { padding: var(--space-8); }

.py-2 { padding-block: var(--space-2); } .py-4 { padding-block: var(--space-4); }
.py-6 { padding-block: var(--space-6); } .py-8 { padding-block: var(--space-8); }
.py-section { padding-block: var(--section-padding); }

.px-2 { padding-inline: var(--space-2); } .px-4 { padding-inline: var(--space-4); }
.px-6 { padding-inline: var(--space-6); }

/* --------------------------------------------------------------------
   Display & responsive helpers
   -------------------------------------------------------------------- */
.bg-surface     { background-color: var(--color-surface); }
/* 2026-08-09 — flat, not a gradient: the guardrail is "text/interactive
   elements never sit directly on a gradient or peach fill, always on
   white or ink" — About/Shop both render an eyebrow+h2 straight onto
   this band, not inside a white card. */
.bg-surface-soft { background-color: var(--color-surface-soft); }
.bg-secondary   { background-color: var(--color-secondary); }

.hidden    { display: none !important; }
.block     { display: block; }
.invisible { visibility: hidden; }
.w-full    { width: 100%; }
.h-full    { height: 100%; }
.relative  { position: relative; }
.absolute  { position: absolute; }
.rounded-sm { border-radius: var(--radius-sm); }
.rounded-md { border-radius: var(--radius-md); }
.rounded-lg { border-radius: var(--radius-lg); }
.rounded-full { border-radius: var(--radius-full); }
.shadow-sm { box-shadow: var(--shadow-sm); }
.shadow-md { box-shadow: var(--shadow-md); }
.shadow-lg { box-shadow: var(--shadow-lg); }

@media (min-width: 480px) {
  .sm\:hidden { display: none !important; }
  .sm\:block  { display: block !important; }
  .sm\:flex   { display: flex !important; }
}

@media (min-width: 768px) {
  .md\:hidden { display: none !important; }
  .md\:block  { display: block !important; }
  .md\:flex   { display: flex !important; }
  .grid-products   { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .grid-cols-md-2  { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .grid-cols-md-3  { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

@media (min-width: 1024px) {
  .lg\:hidden { display: none !important; }
  .lg\:block  { display: block !important; }
  .lg\:flex   { display: flex !important; }
  .grid-products   { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .grid-cols-lg-3  { grid-template-columns: repeat(3, minmax(0, 1fr)); }
  .grid-cols-lg-4  { grid-template-columns: repeat(4, minmax(0, 1fr)); }
  .grid-cols-lg-6  { grid-template-columns: repeat(6, minmax(0, 1fr)); }
}

@media (min-width: 1280px) {
  .xl\:block { display: block !important; }
  .grid-cols-xl-5 { grid-template-columns: repeat(5, minmax(0, 1fr)); }
}

/* The wider (1760px) container gives product grids meaningfully more
   room on large desktops — use it to show more products per row
   instead of just inflating each card. */
@media (min-width: 1440px) {
  .grid-products { grid-template-columns: repeat(5, minmax(0, 1fr)); }
}

@media (min-width: 1800px) {
  .grid-products { grid-template-columns: repeat(6, minmax(0, 1fr)); }
}

/* ==========================================================================
   2. BUTTONS
   ========================================================================== */

.btn {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  height: 48px;
  padding-inline: var(--space-6);
  font-family: var(--font-heading);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wide);
  border-radius: var(--radius-full);
  border: 1px solid transparent;
  white-space: nowrap;
  cursor: pointer;
  overflow: hidden;
  transition: color var(--duration-fast) var(--ease-out),
              background-color var(--duration-base) var(--ease-out),
              border-color var(--duration-base) var(--ease-out),
              box-shadow var(--duration-base) var(--ease-out),
              transform var(--duration-base) var(--ease-spring);
  user-select: none;
}

.btn:focus-visible {
  outline: none;
  box-shadow: var(--shadow-focus);
}

.btn:active { transform: translateY(0) scale(0.985); }

.btn-sm { height: 38px; padding-inline: var(--space-4); font-size: var(--text-xs); }
.btn-lg { height: 56px; padding-inline: var(--space-8); font-size: var(--text-md); }

/* Primary — solid near-black pill, matching the live blissporium.com
   "Shop Now" CTA. Flat fill, three states (default/hover/active), no
   gradient or sheen. Pink is reserved for decorative accents, not the
   primary button. */
.btn-primary {
  background-color: var(--color-ink);
  color: var(--color-text-inverse);
  box-shadow: var(--shadow-sm);
}

.btn-primary:hover {
  background-color: var(--color-ink-hover);
  transform: translateY(-2px);
  box-shadow: var(--shadow-md);
}

.btn-primary:active {
  background-color: var(--color-ink-hover);
  transform: translateY(0) scale(0.985);
  box-shadow: var(--shadow-xs);
}

/* Secondary — spans pale to fully-saturated rose across its own
   gradient (and its hover state), so it needs dark text that stays
   legible at both ends rather than either "on-primary" token, which
   is tuned for a single flat background. */
.btn-secondary {
  background-image: linear-gradient(135deg, var(--color-primary-light) 0%, var(--color-primary) 100%);
  color: var(--color-text);
}
.btn-secondary:hover {
  background-image: linear-gradient(135deg, var(--color-primary) 0%, var(--color-primary-dark) 100%);
  transform: translateY(-2px);
  box-shadow: var(--shadow-sm);
}

/* Outline */
.btn-outline {
  background-color: transparent;
  border-color: var(--color-border-strong);
  color: var(--color-text);
}
.btn-outline:hover {
  border-color: var(--color-accent);
  color: var(--color-accent-dark);
  background-color: var(--color-surface-soft);
  transform: translateY(-2px);
  box-shadow: var(--shadow-sm);
}

/* Ghost */
.btn-ghost {
  background-color: transparent;
  color: var(--color-text);
}
.btn-ghost:hover {
  background-color: var(--color-border);
}

/* Danger */
.btn-danger {
  background-color: var(--color-danger);
  color: var(--color-text-inverse);
}
.btn-danger:hover { filter: brightness(0.92); }

/* Disabled */
.btn:disabled,
.btn.is-disabled {
  opacity: 0.5;
  cursor: not-allowed;
  transform: none !important;
  box-shadow: none !important;
}

/* Loading */
.btn.is-loading {
  color: transparent !important;
  pointer-events: none;
}

.btn.is-loading::after {
  content: '';
  position: absolute;
  width: 18px;
  height: 18px;
  border-radius: var(--radius-full);
  border: 2px solid rgba(255, 255, 255, 0.5);
  border-top-color: var(--color-text-inverse);
  animation: spin 0.6s linear infinite;
}

.btn-outline.is-loading::after,
.btn-ghost.is-loading::after {
  border-color: rgba(43, 43, 43, 0.25);
  border-top-color: var(--color-text);
}

/* Icon-only button */
.btn-icon {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  flex-shrink: 0;
  width: 44px;
  height: 44px;
  padding: 0;
  border-radius: var(--radius-full);
  background-color: var(--color-surface);
  border: 1px solid var(--color-border);
  color: var(--color-text);
  cursor: pointer;
  transition: var(--transition-base);
}

.btn-icon svg { display: block; flex-shrink: 0; }

.btn-icon:hover {
  border-color: var(--color-accent);
  color: var(--color-accent-dark);
  box-shadow: var(--shadow-sm);
  transform: translateY(-2px);
}

.btn-icon:focus-visible {
  outline: none;
  box-shadow: var(--shadow-focus);
}

.btn-block { width: 100%; }

/* ==========================================================================
   3. FORMS
   ========================================================================== */

.form-group {
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
  margin-bottom: var(--space-5);
}

.form-label {
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--color-text);
}

.form-label .required { color: var(--color-danger); margin-left: var(--space-1); }

.form-control {
  width: 100%;
  height: 48px;
  padding-inline: var(--space-4);
  background-color: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  font-size: var(--text-sm);
  color: var(--color-text);
  transition: var(--transition-color), box-shadow var(--duration-fast) var(--ease-out);
}

.form-control::placeholder { color: var(--color-text-muted); }

.form-control:hover { border-color: var(--color-border-strong); }

.form-control:focus {
  outline: none;
  border-color: var(--color-accent);
  box-shadow: var(--shadow-focus);
}

textarea.form-control {
  height: auto;
  min-height: 120px;
  padding-block: var(--space-3);
  resize: vertical;
}

/* Select / dropdown */
select.form-control {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='12' height='8' viewBox='0 0 12 8' fill='none'%3E%3Cpath d='M1 1L6 6L11 1' stroke='%23757575' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--space-4) center;
  padding-right: var(--space-8);
}

/* Checkbox */
.form-checkbox {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  cursor: pointer;
  font-size: var(--text-sm);
  color: var(--color-text);
}

.form-checkbox input[type='checkbox'] {
  appearance: none;
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  border: 1.5px solid var(--color-border-strong);
  border-radius: var(--radius-xs);
  background-color: var(--color-surface);
  display: grid;
  place-content: center;
  transition: var(--transition-fast);
}

.form-checkbox input[type='checkbox']::before {
  content: '';
  width: 10px;
  height: 10px;
  transform: scale(0);
  transition: transform var(--duration-fast) var(--ease-spring);
  box-shadow: inset 1em 1em var(--color-text-inverse);
  clip-path: polygon(14% 44%, 0 65%, 50% 100%, 100% 16%, 80% 0%, 43% 62%);
}

.form-checkbox input[type='checkbox']:checked {
  background-color: var(--color-primary-pressed);
  border-color: var(--color-primary-pressed);
}

.form-checkbox input[type='checkbox']:checked::before { transform: scale(1); }

/* Radio */
.form-radio {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  cursor: pointer;
  font-size: var(--text-sm);
  color: var(--color-text);
}

.form-radio input[type='radio'] {
  appearance: none;
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  border: 1.5px solid var(--color-border-strong);
  border-radius: var(--radius-full);
  background-color: var(--color-surface);
  display: grid;
  place-content: center;
  transition: var(--transition-fast);
}

.form-radio input[type='radio']::before {
  content: '';
  width: 10px;
  height: 10px;
  border-radius: var(--radius-full);
  transform: scale(0);
  transition: transform var(--duration-fast) var(--ease-spring);
  background-color: var(--color-text-inverse);
}

.form-radio input[type='radio']:checked {
  background-color: var(--color-primary-pressed);
  border-color: var(--color-primary-pressed);
}

.form-radio input[type='radio']:checked::before { transform: scale(1); }

/* Validation states */
.form-control.is-error {
  border-color: var(--color-danger);
}
.form-control.is-error:focus {
  box-shadow: 0 0 0 3px rgba(194, 101, 101, 0.2);
}

.form-control.is-success {
  border-color: var(--color-success);
}

.form-message {
  font-size: var(--text-xs);
  display: flex;
  align-items: center;
  gap: var(--space-1);
}

.form-message.is-error   { color: var(--color-danger); }
.form-message.is-success { color: var(--color-success); }
.form-message.is-hint    { color: var(--color-text-muted); }

/* Input group (e.g. search bar with icon) */
.input-group {
  position: relative;
  display: flex;
  align-items: center;
}

.input-group .form-control { padding-left: var(--space-10); }

.input-group__icon {
  position: absolute;
  left: var(--space-4);
  color: var(--color-text-muted);
  pointer-events: none;
}

/* ==========================================================================
   4. CARDS
   ========================================================================== */

.card {
  background-color: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  transition: var(--transition-base);
}

/* Product card */
/* ==========================================================================
   Product card — 2026-08-10 minimal redesign. Photo-first: no boxed
   background/border/shadow/rounded corners, no badges, no Quick View,
   no rating, no brand line, no on-card Add to Cart. Just photo, name,
   price — the card is a link through to the product page, where
   wishlist/add-to-cart/ratings/variants all live instead. Shared markup
   lives in templates/partials/_product_card.html and is used by every
   product grid sitewide (home, shop, category, cart's "You May Also
   Like"/"Best Sellers", PDP's "You May Also Like").

   .product-card__brand and .product-card__wishlist are kept below only
   for templates/customer/wishlist.html, which still renders its own
   brand line and a wishlist-remove heart — that page's card grid IS the
   wishlist, so removing an item without leaving the page is core to
   what the page does, not decorative chrome. Everything else in this
   file applies to it too (borderless container, minimal title/price
   type), it just keeps those two extra elements on top. */
.product-card {
  position: relative;
  display: flex;
  flex-direction: column;
}

.product-card__media {
  position: relative;
  display: block;
  aspect-ratio: 3 / 4;
  overflow: hidden;
  background-color: var(--color-surface-soft);
}

.product-card__media > a {
  display: block;
  width: 100%;
  height: 100%;
}

.product-card__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 800ms var(--ease-out);
  will-change: transform;
}

.product-card:hover .product-card__media img { transform: scale(1.04); }

.product-card__title a {
  color: inherit;
  transition: var(--transition-color);
}

.product-card__title a:hover { color: var(--color-text-secondary); }

/* Wishlist heart (wishlist.html only) rests hidden and floats in on
   hover (fine pointers only — always visible on touch). */
.product-card__wishlist {
  position: absolute;
  top: var(--space-3);
  right: var(--space-3);
  z-index: var(--z-raised);

  /* The form is a wrapper, not a control. Left as an inline box it collapsed
     around the button and sat on top of it, so on a touch device the tap
     landed on the form and nothing happened — the heart simply did not work.
     Sized to the button and made transparent to pointer events; the button
     takes them back below. */
  display: block;
  line-height: 0;
  pointer-events: none;
}

.product-card__wishlist button {
  pointer-events: auto;

  /* The heart artwork is 16px. That is a fine thing to look at and far too
     small to hit with a thumb, so the button carries a 44px target around it
     — the smallest most accessibility guidance accepts — without the icon
     itself growing. */
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 44px;
  min-height: 44px;
  padding: 0;
  border: 0;
  background: none;
  color: inherit;
  cursor: pointer;
}

@media (hover: hover) and (pointer: fine) {
  .product-card .product-card__wishlist {
    opacity: 0;
    transform: translateY(-6px) scale(0.9);
    transition: opacity var(--duration-base) var(--ease-out),
                transform var(--duration-base) var(--ease-spring);
  }

  .product-card:hover .product-card__wishlist,
  .product-card:focus-within .product-card__wishlist {
    opacity: 1;
    transform: translateY(0) scale(1);
  }
}

/* flex:1 1 auto lets .product-card__price's margin-top:auto below pin
   the price to the bottom of every card in a row at the same height,
   regardless of how many lines the name above it wrapped to — same
   mechanism as the button-alignment fix this card already had, still
   needed now that price is the last element instead of a button. */
.product-card__body {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  /* 2026-08-10 — was a flat 4px for both the image-to-text gap AND the
     name-to-price gap; those two gaps have opposite jobs (give the text
     block room to breathe vs. read name+price as one grouped unit), so
     one value couldn't serve both. Image-to-text now lives on padding-
     top below (bumped up); name-to-price is this gap (tightened down). */
  gap: 2px;
  padding-top: var(--space-4);
}

.product-card__brand {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
}

/* 2026-08-10 — hardcoded muted ink (#4A423F, the sitewide root
   --color-text-secondary value), not the variable: same page-scope
   collision pattern as .rating-stars/.bottom-nav__item.is-active above
   — .page-home redefines --color-text-secondary to a different hex for
   its own purposes, and this card renders on every page, so reading the
   variable would silently mean two different muted tones depending on
   which page it's on. Deliberately lighter/lower-weight than the price
   below it — the name's job is to identify the product, not compete
   with what it costs. */
/* 2026-08-10 — Quicksand (--font-heading), not Inter: gives the name
   its own typographic identity distinct from the price (which stays
   Inter/--font-nav) rather than just a lighter/smaller copy of the same
   face — same display font already used for the logo wordmark and
   section headings, at its lightest loaded weight (400; the Google
   Fonts import doesn't carry 300) so it reads as quietly branded, not
   heavy. Sized/weighted down from the previous pass, which read as
   competing with price instead of clearly secondary to it. */
.product-card__title {
  font-family: var(--font-heading);
  font-size: 15px;
  font-weight: var(--weight-regular);
  line-height: var(--leading-snug);
  color: #4A423F;
}

/* Star glyphs read as gray asterisks against secondary text unless
   explicitly colored — used by review cards and the product detail
   page's own rating display (no longer rendered on the grid card
   itself, but still genuinely shared markup elsewhere). */
/* 2026-08-10 — hardcoded, not var(--color-accent-dark): that variable is
   redefined inside .page-home (for an unrelated purpose — link/hover
   text) to #2A1912 (ink), while the sitewide root value is #AF555C
   (rose). Confirmed via getComputedStyle it silently rendered two
   different colors depending on page before this fix. Literal hex is
   what actually keeps it consistent regardless of page token scope. */
.rating-stars {
  color: #AF555C;
  letter-spacing: 1px;
}

/* 2026-08-10 — this is now the visual anchor of the card: noticeably
   larger than the name above it, semibold, and full ink (not the
   muted/secondary tone the name uses) — "how much is this" should read
   as the answer, not an afterthought. Color hardcoded to #2A1912 for
   the same reason .product-card__title's is above: a shared component
   can't read var(--color-ink), since .page-home redefines that name to
   a different hex than the sitewide root value. */
.product-card__price {
  display: flex;
  align-items: baseline;
  gap: var(--space-2);
  font-family: var(--font-nav);
  font-size: var(--text-md);
  font-weight: var(--weight-semibold);
  letter-spacing: 0.015em;
  color: #2A1912;
  margin-top: auto;
}

.product-card__price .price { font-size: var(--text-md); font-weight: var(--weight-semibold); color: #2A1912; }

/* The corner badge already communicates "on sale" — keep the price
   itself the same confident dark tone as a regular price instead of
   also colouring it red, so the numbers stay the calm, premium focus. */
.product-card__price .price--sale { color: var(--color-text); }

/* --------------------------------------------------------------------
   Mobile product card — 767px matches .grid-products' own 2-column
   breakpoint (768px is where it steps up to 3 columns), so this only
   touches the actual 2-up phone grid. Same taller-than-square crop as
   desktop's 3/4 would look cramped at 2-up phone widths, so it goes
   taller still; body padding/gap trimmed to match the now much smaller
   footer (name + price only). */
@media (max-width: 767px) {
  .product-card__media { aspect-ratio: 2 / 3; }

  .product-card__body {
    gap: 3px;
    padding-top: var(--space-2);
  }

  .product-card__brand { font-size: 10px; }

  .product-card__title {
    font-size: var(--text-sm);
    /* Hard cap at 2 lines regardless of product name length, so a long
       name can never push the price below the compact budget it's
       been given. */
    display: -webkit-box;
    -webkit-line-clamp: 2;
    -webkit-box-orient: vertical;
    overflow: hidden;
  }

  .product-card__price { font-size: var(--text-xs); }
  .product-card__price .price { font-size: var(--text-sm); }

  .product-card__wishlist {
    top: var(--space-2);
    right: var(--space-2);
  }
}

/* Narrowest phones (iPhone SE/older Android, ~320-360px). */
@media (max-width: 360px) {
  .product-card__body { gap: 2px; padding-top: var(--space-2); }
}

/* Category card */
.category-card {
  position: relative;
  display: block;
  border-radius: var(--radius-lg);
  overflow: hidden;
  aspect-ratio: 1 / 1;
  box-shadow: var(--shadow-sm);
  transition: box-shadow var(--duration-slow) var(--ease-out),
              transform var(--duration-slow) var(--ease-out);
}

.category-card:hover {
  box-shadow: var(--shadow-lg);
  transform: translateY(-4px);
}

.category-card img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 900ms var(--ease-out);
  will-change: transform;
}

.category-card:hover img { transform: scale(1.06); }

.category-card__overlay {
  position: absolute;
  inset: 0;
  display: flex;
  align-items: flex-end;
  padding: var(--space-6);
  background: linear-gradient(180deg, rgba(43,43,43,0) 35%, rgba(43,43,43,0.30) 68%, rgba(43,43,43,0.62) 100%);
  transition: background-color var(--duration-slow) var(--ease-out);
}

.category-card__label {
  font-family: var(--font-heading);
  font-size: var(--text-lg);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wide);
  color: var(--color-text-inverse);
  text-shadow: 0 1px 12px rgba(43, 43, 43, 0.35);
}

/* CTA slides up + fades in on hover; stays visible on touch */
.category-card__cta {
  display: inline-block;
  margin-top: var(--space-1);
  font-size: var(--text-sm);
  color: rgba(255, 255, 255, 0.92);
  transition: opacity var(--duration-slow) var(--ease-out),
              transform var(--duration-slow) var(--ease-out);
}

@media (hover: hover) and (pointer: fine) {
  .category-card__cta { opacity: 0; transform: translateY(8px); }
  .category-card:hover .category-card__cta { opacity: 1; transform: translateY(0); }
}

/* Feature card */
.feature-card {
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-4);
  padding: var(--space-8);
  background-color: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  transition: box-shadow var(--duration-slow) var(--ease-out),
              transform var(--duration-slow) var(--ease-out);
}

.feature-card:hover {
  box-shadow: var(--shadow-md);
  transform: translateY(-4px);
}

/* Light-coral-tint chip + ink icon (2026-08-09 rev 2 — was peach
   #F1D8C6). At icon-chip size/saturation peach read as cream/tan, not
   peach, to the eye — confirmed by screenshot. Scope decision: icon
   chip backgrounds now use #FDEEF0, not the peach token; peach's job
   is backgrounds/dividers only, no longer icon chips. */
.feature-card__icon {
  display: grid;
  place-items: center;
  width: 56px;
  height: 56px;
  border-radius: var(--radius-full);
  background-color: #FDEEF0;
  color: var(--color-ink);
  box-shadow: var(--shadow-xs);
  transition: transform var(--duration-base) var(--ease-spring);
}

.feature-card:hover .feature-card__icon { transform: scale(1.08) rotate(-4deg); }

/* Review card */
.review-card {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding: var(--space-6);
  background-color: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
}

.review-card__header {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

.review-card__avatar {
  width: 44px;
  height: 44px;
  border-radius: var(--radius-full);
  object-fit: cover;
  background-color: var(--color-primary-light);
}

.review-card__name {
  font-weight: var(--weight-semibold);
  font-size: var(--text-sm);
}

.review-card__stars { color: var(--color-accent-dark); }

.review-card__reply {
  margin-top: var(--space-2);
  padding: var(--space-3) var(--space-4);
  background-color: var(--color-surface-soft);
  border-left: 2px solid var(--color-accent);
  border-radius: var(--radius-sm);
}

/* Information card (trust/policy blocks) */
.info-card {
  display: flex;
  align-items: flex-start;
  gap: var(--space-4);
  padding: var(--space-6);
  background-color: var(--color-surface-soft);
  border-radius: var(--radius-lg);
}

/* Contact page form + order-support column (templates/customer/contact.html) */
.contact-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-10);
  max-width: 900px;
}

@media (min-width: 768px) {
  .contact-grid { grid-template-columns: 1.3fr 1fr; }
}

.contact-form {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

/* ==========================================================================
   4b. ANNOUNCEMENT BAR — site-wide, dismissible (templates/partials/
   announcement_bar.html). Ink background + white text, not a coral or
   peach fill — the guardrail is "text/interactive elements never sit
   directly on a gradient or peach fill, always on white or ink," and
   ink-with-white-text is the compliant dark option (white bg would work
   too, but wouldn't read as a distinct "announcement" band above the
   white navbar). Coral shows up only as the link's underline, same
   non-text-carrying role it has everywhere else in the system.
   ========================================================================== */

.announce-bar {
  background-color: var(--color-ink);
  overflow: hidden;
}

.announce-bar__inner {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  min-height: 40px;
  padding-block: var(--space-2);
  padding-right: 44px;
}

.announce-bar__text {
  margin: 0;
  font-family: var(--font-nav);
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  letter-spacing: 0.02em;
  color: var(--color-text-inverse);
  text-align: center;
}

.announce-bar__sep {
  opacity: 0.55;
  margin-inline: var(--space-2);
}

.announce-bar__link {
  position: relative;
  color: var(--color-text-inverse);
  font-weight: var(--weight-semibold);
  padding-bottom: 2px;
}

.announce-bar__link::after {
  content: '';
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: 2px;
  background-color: #F7AEB5;
  transform: scaleX(1);
  transform-origin: left center;
  transition: transform var(--duration-base) var(--ease-out);
}

.announce-bar__link:hover::after { transform: scaleX(0); transform-origin: right center; }

.announce-bar__close {
  position: absolute;
  right: var(--space-4);
  top: 50%;
  transform: translateY(-50%);
  display: grid;
  place-items: center;
  width: 24px;
  height: 24px;
  border-radius: var(--radius-full);
  background: none;
  border: none;
  color: var(--color-text-inverse);
  opacity: 0.7;
  font-size: 18px;
  line-height: 1;
  cursor: pointer;
  transition: opacity var(--duration-fast) var(--ease-out),
              background-color var(--duration-fast) var(--ease-out),
              transform var(--duration-fast) var(--ease-spring);
}

.announce-bar__close:hover {
  opacity: 1;
  background-color: rgba(255, 255, 255, 0.14);
}

.announce-bar__close:active { transform: translateY(-50%) scale(0.9); }

@media (max-width: 480px) {
  .announce-bar__text { font-size: 11px; }
  .announce-bar__sep { margin-inline: var(--space-1); }
}

/* ==========================================================================
   5. NAVIGATION — Navbar / Mega Menu / Breadcrumb
   ========================================================================== */

.navbar {
  position: sticky;
  top: 0;
  z-index: var(--z-header);
  background-color: rgba(255, 253, 251, 0.86);
  -webkit-backdrop-filter: blur(14px) saturate(1.4);
  backdrop-filter: blur(14px) saturate(1.4);
  border-bottom: 1px solid var(--color-border);
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.6) inset, var(--shadow-xs);
  transition: box-shadow var(--duration-base) var(--ease-out);
}

@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .navbar { background-color: var(--color-secondary); }
}

/* Subtle lift once the page has scrolled — main.js toggles this class
   at a small threshold so it reads as "the header just came forward",
   not a jarring pop-in. */
.navbar.is-scrolled {
  box-shadow: 0 1px 0 rgba(255, 255, 255, 0.6) inset, var(--shadow-md);
  /* Denser glass once the page is in motion, so content scrolling
     underneath never competes with the nav labels. */
  background-color: rgba(255, 253, 251, 0.94);
  -webkit-backdrop-filter: blur(20px) saturate(1.6);
  backdrop-filter: blur(20px) saturate(1.6);
}

.navbar__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  /* Height is a variable so the scrolled state can retune it in one
     place; the logo scales from the same signal. */
  height: var(--nav-height, 122px);
  transition: height var(--duration-slow) var(--ease-lux);
}

/* Condense on scroll — the full-height header is a landing flourish,
   but keeping 122px pinned to the top for the whole page is a lot of
   permanent furniture. This is one layout-affecting transition on a
   single element, crossed twice per page at most. */
.navbar.is-scrolled .navbar__inner { --nav-height: 88px; }

.navbar .brand__icon,
.navbar .brand__name {
  transition: height var(--duration-slow) var(--ease-lux),
              font-size var(--duration-slow) var(--ease-lux);
}

.navbar.is-scrolled .brand__icon { height: 56px; }
.navbar.is-scrolled .brand__name { font-size: var(--text-md); }

/* The tagline is the first thing to go — it is the least load-bearing
   part of the lockup and reads as clutter at the condensed height. */
.navbar .brand__tagline {
  overflow: hidden;
  transition: opacity var(--duration-base) var(--ease-out),
              max-height var(--duration-slow) var(--ease-lux);
  max-height: 2em;
  opacity: 1;
}

.navbar.is-scrolled .brand__tagline { opacity: 0; max-height: 0; }

/* Phones already run the condensed header at rest, so re-animating it
   on scroll would just be motion for its own sake. */
@media (max-width: 640px) {
  .navbar__inner,
  .navbar.is-scrolled .navbar__inner { transition: none; }
}

/* The extra height is a desktop luxury-brand flourish — on phones it
   just eats screen real estate above the fold, so it scales back down
   without losing the still-slightly-larger, more-prominent logo. */
@media (max-width: 640px) {
  .navbar__inner { height: 88px; }
  .navbar .brand__icon { height: 56px; }
  .navbar .brand__name { font-size: var(--text-md); }
}

/* Push the logo further toward the container edge (proper padding, not
   flush against it) and scale it up to match the taller header.
   2026-08-10 — back to living directly on .brand: the menu trigger no
   longer shares a wrapper with it (moved into .navbar__actions, last
   in the icon group, Sephora-style), so .brand is once again the
   element actually sitting at the row's edge. */
.navbar .brand {
  margin-left: -18px;
  padding-left: var(--space-2);
  /* 2026-08-10 — was space-4 (16px), which read as a large, unintended
     gap next to the icon's own built-in padding within the PNG asset.
     space-2 (8px) is normal icon-to-wordmark spacing. */
  gap: var(--space-2);
}

/* Icon mark: original size/artwork, untouched — only its color intensity
   changes (2026-08-09). It's a flattened PNG (static/images/logos/
   logo-mark.png), essentially monochrome coral with tonal shading, not
   multiple hues — a saturation filter is the correct CSS-only way to
   deepen it toward a bolder coral without touching the shape/artwork,
   which a hex-targeted recolor on raster pixels can't do anyway. */
.navbar .brand__icon { filter: saturate(1.75) brightness(0.97); }

/* Wordmark: Quicksand (2026-08-09) — this is --font-heading, already the
   sitewide heading font (product titles, section headings, buttons),
   not a new typeface. Semibold, not the boldest weight, matching the
   soft rounded-terminal reference; sentence case; stays dark ink, never
   coral — the coral lives on the icon above, not the letters. */
.navbar .brand__name {
  font-family: var(--font-heading);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-normal);
  color: var(--color-ink);
}

/* 2026-08-10 — logo size scale (78px icon / text-xl wordmark) now lives
   in ONE min-width-scoped rule instead of sitting unconditional between
   two max-width breakpoints below it. It used to win at every mobile
   width too (equal specificity, later in source beats an earlier
   max-width query), which silently broke the intended 640px/480px
   step-down for any viewport in the 481-640px gap — this rule always
   overrode the 640 query's smaller size there, and only the 480 query
   (further below, and further in source) ever actually took effect. A
   single min-width boundary here removes that ordering dependency
   entirely: each tier now only applies in its own range, so cascade
   order no longer has to be reasoned about across three separate blocks. */
@media (min-width: 641px) {
  .navbar .brand__icon { height: 78px; }
  .navbar .brand__name { font-size: var(--text-xl); }
}

.navbar .brand__tagline {
  font-family: var(--font-nav);
  font-weight: var(--weight-regular);
  font-size: 12px;
  letter-spacing: var(--tracking-wide);
  /* Explicit, not relied-on-by-default: flush with the wordmark's left
     edge, not centered under the icon+wordmark lockup as a whole. */
  text-align: left;
  align-self: flex-start;
}

/* --------------------------------------------------------------------
   Brand lockup (icon + wordmark + tagline) — used in the navbar, the
   mobile drawer header, and the footer. Text is real HTML, not baked
   into the logo image, so it stays crisp at any size/DPI.
   -------------------------------------------------------------------- */
.brand {
  display: inline-flex;
  align-items: center;
  gap: var(--space-3);
  color: var(--color-text);
  transition: opacity var(--duration-fast) var(--ease-out);
}

.brand:hover { opacity: 0.85; }

.brand__icon {
  height: 48px;
  width: auto;
  flex-shrink: 0;
}

.brand__text {
  display: flex;
  flex-direction: column;
  line-height: 1.05;
}

.brand__name {
  font-family: var(--font-brand);
  font-size: var(--text-lg);
  font-weight: var(--weight-bold);
  letter-spacing: var(--tracking-wide);
  color: var(--color-text);
  white-space: nowrap;
}

.brand__tagline {
  margin-top: 2px;
  font-family: var(--font-body);
  font-size: 11px;
  font-weight: var(--weight-regular);
  letter-spacing: var(--tracking-wider);
  text-transform: uppercase;
  color: var(--color-text-secondary);
  white-space: nowrap;
}

/* Larger variant for the footer, where there's more room to breathe */
.brand--lg .brand__icon { height: 64px; }
.brand--lg .brand__name { font-size: var(--text-2xl); }
.brand--lg .brand__tagline { font-size: var(--text-xs); margin-top: var(--space-1); }

/* Full lockup (icon + wordmark + tagline baked into one image) — used
   where the whole mark should render as a single image instead of an
   icon plus separately-styled HTML text (footer, maintenance page,
   invoice). Width-based sizing since the lockup is wider than it is
   tall, unlike the icon-only mark. */
.brand__lockup {
  width: 190px;
  height: auto;
}

@media (max-width: 480px) {
  .brand__lockup { width: 160px; }
}

/* Scoped to .navbar specifically (not just bare .brand__icon/__name) so
   this beats the .navbar-scoped 640px tier above on equal specificity —
   both selectors are "(0,0,2,0)", so without the .navbar-prefixed
   duplicate here, a bare .brand__icon rule couldn't out-rank it; the
   bare selectors alongside it are what let this tier also apply to
   .brand usages outside the navbar (footer, drawer header). */
@media (max-width: 480px) {
  .navbar .brand__icon,
  .brand__icon { height: 41px; }
  .navbar .brand__name,
  .brand__name { font-size: var(--text-sm); }
  /* 2026-08-10 — was display:none ("no room for three lines of text
     next to four action icons," per the old note below) — Wishlist and
     Cart have since moved out of the mobile top bar entirely (now
     covered by the persistent bottom nav instead), leaving only
     search + the menu trigger beside the logo. That's the room this
     needed; sized down rather than dropped. */
  .navbar .brand__tagline,
  .brand__tagline { font-size: 9px; }
}

/* 2026-08-10 — a logo without its name isn't a logo: this used to hide
   .brand__text entirely below 420px, leaving just the bare icon with no
   "Blissporium" wordmark anywhere on the mobile header. Now the name
   stays, just smaller and tighter against the icon. */
@media (max-width: 420px) {
  .navbar .brand { margin-left: -10px; gap: var(--space-2); }
}

/* Narrowest phones (iPhone SE/older Android, ~320-360px): four 44px
   action icons (search/wishlist/cart/menu) plus the icon+wordmark
   lockup no longer both fit at the 420px tier's sizing without
   overflowing — this shaves a bit more off both sides (wordmark size,
   icon-row gap) rather than going back to hiding the name. */
@media (max-width: 360px) {
  .navbar .brand__name,
  .brand__name { font-size: 13px; }
  .navbar__actions { gap: var(--space-2); }
}

@media (max-width: 330px) {
  .navbar .brand { gap: var(--space-1); margin-left: -12px; }
  .navbar .brand__name,
  .brand__name { font-size: 12px; }
}

.navbar__inner { gap: var(--space-10); }

.navbar__links {
  display: flex;
  align-items: center;
  gap: var(--space-10);
  flex-wrap: nowrap;
  min-width: 0;
}

/* 2026-08-09 — sized/cased down from the old --text-md (20px) sentence
   case to 15px uppercase (Sephora/Rhode/Apple pattern), then bumped back
   up slightly (16px) and to semibold across the board: with the wordmark
   now compact and quiet, the links are the confident workhorse of the
   bar and needed more presence, not less. The active page is no longer
   distinguished by weight (everything is 600 now) — the persistent
   coral underline (below) is the sole indicator, which was already
   "working well" per the brief. */
.navbar__link {
  position: relative;
  font-family: var(--font-nav);
  font-size: 16px;
  font-weight: var(--weight-semibold);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-nav-text);
  padding-block: var(--space-2);
  transition: color var(--duration-base) var(--ease-out);
  white-space: nowrap;
  display: inline-block;
}

/* The only thing that should ever make a nav link look "active" is
   actually being on that page — every link shares the same lighter
   weight and neutral colour by default, and only .is-active (driven by
   nav_is_home / nav_is_shop / nav_is_about / nav_is_contact in
   navbar.html, computed from request.path) gets the semibold weight +
   brand-colour treatment. Deliberately not bold (700) — a heavier
   semibold reads as "current page" without looking shouty. */
.navbar__link.is-active {
  font-weight: var(--weight-semibold);
  color: var(--color-nav-active);
}

/* "Categories" dropdown trigger — same look as a plain nav link, plus
   a chevron that flips while the menu is open. Must NOT set
   font-family: inherit here — on the same element as .navbar__link,
   `inherit` resolves against the DOM parent (<nav>, which cascades
   Quando serif from <body>), not against .navbar__link's own Quicksand
   declaration on this element, so it was silently overriding the nav
   font entirely and making Shop the only link rendered in a different
   (visually heavier) typeface. Simply not touching font-family here
   lets .navbar__link's declaration apply, same as every other link. */
.navbar__link--dropdown {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  background: none;
  border: none;
  cursor: pointer;
}

.navbar__link--dropdown svg {
  transition: transform var(--duration-fast) var(--ease-out);
}

.dropdown:has(.dropdown__menu.is-open) .navbar__link--dropdown svg { transform: rotate(180deg); }

/* Shop's chevron isn't a .dropdown trigger (it opens the full-width
   .mega-menu instead) — main.js sets aria-expanded on it directly. */
.navbar__link--dropdown[aria-expanded="true"] svg { transform: rotate(180deg); }

/* Tighten nav gaps a notch before the mobile breakpoint so five items
   plus the brand never force a wrap on narrow laptops/tablets */
@media (max-width: 1200px) {
  .navbar__links { gap: var(--space-4); }
  .navbar__inner { gap: var(--space-4); }
}

/* Underline wipes in on a transform, not a width.
   Animating `width` relayouts the link (and therefore the whole nav row)
   on every frame of the transition; scaleX is composited and costs
   nothing. The origin flip is what makes it feel considered: the rule
   grows from the left on the way in and retreats to the right on the
   way out, instead of collapsing back the way it came. */
.navbar__link::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: -2px;
  width: 100%;
  height: 2px;
  background-color: var(--color-nav-active);
  transform: scaleX(0);
  transform-origin: right center;
  transition: transform var(--duration-slow) var(--ease-lux);
}

.navbar__link:hover { color: var(--color-nav-active); }

.navbar__link:hover::after,
.navbar__link:focus-visible::after,
.navbar__link.is-active::after {
  transform: scaleX(1);
  transform-origin: left center;
}

/* The current page's underline is permanent, so it must not animate
   away and back when the pointer passes over it. */
.navbar__link.is-active::after { transition: none; }

.navbar__actions {
  display: flex;
  align-items: center;
  gap: var(--space-3);
}

/* Account trigger grows into a pill once it carries a name greeting
   instead of staying a fixed-size icon circle. */
.navbar__account-trigger:has(.navbar__greeting) {
  width: auto;
  padding-inline: var(--space-4) var(--space-3);
  border-radius: var(--radius-full);
  gap: var(--space-2);
}

.navbar__greeting {
  font-family: var(--font-nav);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  color: var(--color-text);
  white-space: nowrap;
}

/* Action icons — scoped to the navbar so the shared .btn-icon used in
   forms, modals and product cards keeps its own quieter behaviour. */
.navbar__actions .btn-icon {
  border-color: transparent;
  background-color: transparent;
  transition: background-color var(--duration-base) var(--ease-out),
              color var(--duration-base) var(--ease-out),
              transform var(--duration-base) var(--ease-spring);
}

/* Coral wash behind an ink glyph, not a coral glyph — #F7AEB5 only
   clears 1.8:1 against white, well under the 3:1 an icon needs, so the
   icon itself stays ink and coral shows up as the tinted background
   instead (same "tinted button" pattern used on the homepage pass). */
.navbar__actions .btn-icon:hover {
  background-color: rgba(247, 174, 181, 0.18);
  color: var(--color-ink);
  transform: translateY(-2px);
}

.navbar__actions .btn-icon:active { transform: translateY(0) scale(0.94); }

.navbar__actions .btn-icon svg {
  transition: transform var(--duration-base) var(--ease-spring);
}

.navbar__actions .btn-icon:hover svg { transform: scale(1.1); }

/* The count badge pops whenever its number changes. cart.js re-renders
   the badge element on add-to-cart, so a CSS animation on a freshly
   inserted node fires exactly once per update with no JS coordination. */
.navbar__actions .badge {
  animation: badge-pop var(--duration-slow) var(--ease-spring);
}

@keyframes badge-pop {
  0%   { transform: scale(0.4); opacity: 0; }
  60%  { transform: scale(1.18); opacity: 1; }
  100% { transform: scale(1); opacity: 1; }
}

@media (prefers-reduced-motion: reduce) {
  .navbar__inner,
  .navbar .brand__icon,
  .navbar .brand__name,
  .navbar .brand__tagline,
  .navbar__actions .btn-icon,
  .navbar__actions .btn-icon svg { transition: none; }
  .navbar__actions .badge { animation: none; }
  .navbar__link::after { transition: none; }
}

/* --------------------------------------------------------------------
   Shop mega menu — the primary browsing surface. Full-width panel that
   drops from the navbar with a soft spring, one column per category
   (icon + heading + subcategory links + "view all"), plus a promo card.
   -------------------------------------------------------------------- */
.mega-menu {
  position: absolute;
  top: 100%;
  left: 0;
  right: 0;
  background: linear-gradient(180deg, var(--color-surface) 0%, var(--color-surface-soft) 100%);
  border-top: 1px solid var(--color-border);
  border-radius: 0 0 var(--radius-xl) var(--radius-xl);
  box-shadow: var(--shadow-xl);
  opacity: 0;
  visibility: hidden;
  /* pointer-events is NOT transitioned — it must flip to "none" the
     instant the menu starts closing, not after the fade finishes. CSS
     only defers a transitioned property's value change to the END of
     the transition when moving toward the "hidden" state, so during the
     fade-out the menu was still fully interactive and visible as a
     translucent ghost over whatever page content it overlapped —
     including still firing mouseenter/mouseleave, which could make it
     look like the menu "reopened" from a stray hover nowhere near the
     actual trigger. Same class of bug as the lightbox overlay fix. */
  pointer-events: none;
  transform: translateY(-14px) scale(0.99);
  transform-origin: top center;
  transition: opacity var(--duration-fast) var(--ease-out),
              transform var(--duration-base) var(--ease-spring),
              visibility var(--duration-fast);
  z-index: var(--z-dropdown);
  overflow: hidden;
}

.mega-menu.is-open {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transform: translateY(0) scale(1);
}

.mega-menu__inner {
  display: flex;
  align-items: stretch;
  gap: var(--space-10);
  padding-block: var(--space-10);
}

.mega-menu__columns {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: var(--space-6);
  flex: 1;
  min-width: 0;
}

.mega-menu__column {
  display: flex;
  flex-direction: column;
  min-width: 0;
}

.mega-menu__col-header {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding-bottom: var(--space-3);
  margin-bottom: var(--space-3);
  border-bottom: 1px solid var(--color-border);
  color: inherit;
}

/* Light-coral-tint chip (2026-08-09 rev 2 — was peach #F1D8C6, which
   at this chip size/saturation read as cream/tan, not peach; see
   .feature-card__icon above for the full scope-decision note). */
.mega-menu__col-icon {
  display: grid;
  place-items: center;
  flex-shrink: 0;
  width: 38px;
  height: 38px;
  border-radius: var(--radius-md);
  background-color: #FDEEF0;
  color: var(--color-ink);
  box-shadow: var(--shadow-xs);
  transition: transform var(--duration-base) var(--ease-spring);
}

.mega-menu__col-header:hover .mega-menu__col-icon { transform: scale(1.1) rotate(-4deg); }

.mega-menu__col-title {
  font-family: var(--font-nav);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wide);
  color: var(--color-text);
  transition: var(--transition-color);
}

.mega-menu__col-header:hover .mega-menu__col-title { color: var(--color-accent-dark); }

.mega-menu__list {
  display: flex;
  flex-direction: column;
  gap: 1px;
  margin-bottom: var(--space-4);
}

.mega-menu__link {
  display: block;
  padding: var(--space-2) var(--space-2);
  margin-inline: calc(var(--space-2) * -1);
  border-radius: var(--radius-sm);
  font-family: var(--font-nav);
  font-size: var(--text-sm);
  color: var(--color-text-secondary);
  transition: color var(--duration-fast) var(--ease-out),
              background-color var(--duration-fast) var(--ease-out),
              transform var(--duration-base) var(--ease-out);
}

.mega-menu__link:hover {
  color: var(--color-accent-dark);
  background-color: var(--color-surface-soft);
  transform: translateX(3px);
}

.mega-menu__view-all {
  display: inline-flex;
  align-items: center;
  gap: var(--space-1);
  margin-top: auto;
  padding-top: var(--space-2);
  font-family: var(--font-nav);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wide);
  color: var(--color-accent-dark);
}

.mega-menu__view-all svg { transition: transform var(--duration-base) var(--ease-out); }
.mega-menu__view-all:hover svg { transform: translateX(3px); }

/* Promo card — soft luxury gradient, no external image dependency */
/* White-to-coral (2026-08-09, was a peach/pink radial gradient) —
   same #FFFFFF -> #FDEEF0 treatment as the newsletter section
   background. Text and the coral "Shop New Arrivals" button below are
   unchanged. */
.mega-menu__promo {
  position: relative;
  width: 250px;
  flex-shrink: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  padding: var(--space-6);
  border-radius: var(--radius-lg);
  background: linear-gradient(160deg, #FFFFFF 0%, #FDEEF0 100%);
  box-shadow: var(--shadow-md);
  overflow: hidden;
}

.mega-menu__promo::before {
  content: '';
  position: absolute;
  inset: 0;
  background: radial-gradient(circle at 85% -10%, rgba(255, 255, 255, 0.55), transparent 55%);
  pointer-events: none;
}

.mega-menu__promo-eyebrow,
.mega-menu__promo-title,
.mega-menu__promo-desc,
.mega-menu__promo-cta {
  position: relative;
}

.mega-menu__promo-eyebrow { color: var(--color-brown-deep); }

.mega-menu__promo-title {
  font-family: var(--font-brand);
  font-size: var(--text-xl);
  font-weight: var(--weight-semibold);
  line-height: var(--leading-tight);
  color: var(--color-text);
  margin-block: var(--space-2) var(--space-2);
}

.mega-menu__promo-desc {
  font-size: var(--text-sm);
  color: var(--color-text);
  opacity: 0.85;
  margin-bottom: var(--space-5);
}

.mega-menu__promo-cta { align-self: flex-start; }

/* ==========================================================================
   6. FOOTER
   ========================================================================== */

/* White/coral/ink footer (2026-08-09, replaces the warm-taupe
   --gradient-footer). Picked the white direction, not ink-on-dark —
   consistent with the pure-white system used everywhere else. Since
   the page background above it (newsletter/hero/etc.) is also heading
   toward white/peach, separation comes from a hairline + upward shadow
   rather than a tonal shift, same pattern as the homepage cards. */
.footer {
  background-color: var(--color-secondary);
  border-top: 1px solid var(--color-border);
  box-shadow: 0 -8px 24px rgba(42, 25, 18, 0.04);
  padding-block: var(--space-20) var(--space-8);
}

.footer__grid {
  display: grid;
  grid-template-columns: 2fr repeat(3, minmax(0, 1fr));
  gap: var(--space-8);
}

/* Deliberately small, uppercase and quiet — the #1 amateur-footer
   mistake is a heading that's the same size/weight as its links below
   it. Bold + uppercase + tracked is enough to read as a label without
   competing with the links it introduces. */
.footer__heading {
  font-family: var(--font-nav);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--color-text);
  margin-bottom: var(--space-4);
}

.footer__link {
  display: block;
  padding-block: var(--space-2);
  font-family: var(--font-nav);
  font-size: var(--text-sm);
  font-weight: var(--weight-regular);
  color: var(--color-text-secondary);
  transition: color var(--duration-base) var(--ease-out),
              transform var(--duration-base) var(--ease-out);
}

/* Coral shows up here as an underline, not the link text itself —
   #F7AEB5 text only measures 1.8:1 on white (fails AA); the ink color
   shift plus the coral underline together carry the hover state. */
.footer__link:hover {
  color: var(--color-text);
  text-decoration: underline;
  text-decoration-color: #F7AEB5;
  text-decoration-thickness: 2px;
  text-underline-offset: 4px;
  transform: translateX(4px);
}

.footer__bottom {
  display: grid;
  grid-template-columns: 1fr auto 1fr;
  align-items: center;
  gap: var(--space-4);
  margin-top: var(--space-12);
  padding-top: var(--space-6);
  border-top: 1px solid var(--color-divider);
  font-family: var(--font-nav);
  font-size: var(--text-xs);
  color: var(--color-text-secondary);
}

.footer__bottom > p:first-child { justify-self: start; }

.footer__credit {
  justify-self: center;
  text-align: center;
  white-space: nowrap;
}

.footer__payments { justify-self: end; }

.footer__credit-link {
  color: inherit;
  text-decoration: none;
  border-bottom: 1px solid transparent;
  transition: var(--transition-base);
}

.footer__credit-link:hover,
.footer__credit-link:focus-visible {
  color: var(--color-text);
  border-bottom-color: currentColor;
}

.footer__social {
  display: flex;
  gap: var(--space-3);
}

.footer__social .btn-icon {
  background-color: rgba(255, 255, 255, 0.5);
  border-color: var(--color-border-strong);
  color: var(--color-text-secondary);
  transition: background-color var(--duration-base) var(--ease-out),
              border-color var(--duration-base) var(--ease-out),
              color var(--duration-base) var(--ease-out),
              transform var(--duration-base) var(--ease-out);
}

.footer__social .btn-icon:hover {
  background-color: rgba(247, 174, 181, 0.18);
  border-color: #F7AEB5;
  color: var(--color-ink);
  transform: translateY(-2px);
}

/* ==========================================================================
   7. TOAST / ALERTS
   ========================================================================== */

.toast-container {
  position: fixed;
  bottom: var(--space-6);
  right: var(--space-6);
  z-index: var(--z-toast);
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  max-width: 360px;
}

.toast {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  background-color: var(--color-surface);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  border-left: 3px solid var(--color-accent);
  animation: toast-in var(--duration-base) var(--ease-out) both;
}

.toast.is-success { border-left-color: var(--color-success); }
.toast.is-error   { border-left-color: var(--color-danger); }
.toast.is-warning { border-left-color: var(--color-warning); }

.toast__title { font-weight: var(--weight-semibold); font-size: var(--text-sm); }
.toast__message { font-size: var(--text-sm); color: var(--color-text-secondary); }
.toast__close { margin-left: auto; color: var(--color-text-muted); }

/* Inline alert banners */
.alert {
  display: flex;
  align-items: flex-start;
  gap: var(--space-3);
  padding: var(--space-4) var(--space-5);
  border-radius: var(--radius-md);
  font-size: var(--text-sm);
}

.alert.is-info    { background-color: var(--color-info-bg); color: var(--color-text); }
.alert.is-success { background-color: var(--color-success-bg); color: var(--color-text); }
.alert.is-warning { background-color: var(--color-warning-bg); color: var(--color-text); }
.alert.is-error   { background-color: var(--color-danger-bg); color: var(--color-text); }

/* ==========================================================================
   8. LOADER / SPINNER
   ========================================================================== */

.loader {
  width: 32px;
  height: 32px;
  border-radius: var(--radius-full);
  border: 3px solid var(--color-border);
  border-top-color: var(--color-accent);
  animation: spin 0.7s linear infinite;
}

.loader-overlay {
  position: fixed;
  inset: 0;
  z-index: var(--z-overlay);
  display: grid;
  place-items: center;
  background-color: rgba(250, 248, 246, 0.8);
  backdrop-filter: blur(2px);
}

.page-loader {
  position: fixed;
  inset: 0;
  z-index: 9999;
  display: grid;
  place-items: center;
  background-color: var(--color-background);
  transition: opacity var(--duration-slow) var(--ease-out), visibility var(--duration-slow);
}

.page-loader.is-hidden {
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
}

/* ==========================================================================
   9. MODAL
   ========================================================================== */

.modal-backdrop {
  position: fixed;
  inset: 0;
  z-index: var(--z-overlay);
  background-color: var(--color-overlay);
  opacity: 0;
  visibility: hidden;
  /* Same fix as .mega-menu/.dropdown__menu — untransitioned pointer-events
     so the backdrop can't keep swallowing clicks while it fades out. */
  pointer-events: none;
  transition: opacity var(--duration-fast) var(--ease-out), visibility var(--duration-fast);
}

.modal-backdrop.is-open { opacity: 1; visibility: visible; pointer-events: auto; }

.modal {
  position: fixed;
  top: 50%;
  left: 50%;
  z-index: var(--z-modal);
  width: min(480px, calc(100vw - var(--space-8)));
  max-height: 85vh;
  overflow-y: auto;
  background-color: var(--color-surface);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-xl);
  padding: var(--space-8);
  transform: translate(-50%, -48%) scale(0.97);
  opacity: 0;
  visibility: hidden;
  pointer-events: none;
  transition: opacity var(--duration-fast) var(--ease-out),
              transform var(--duration-base) var(--ease-out),
              visibility var(--duration-fast);
}

.modal.is-open {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transform: translate(-50%, -50%) scale(1);
}

.modal__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-6);
}

.modal__title {
  font-family: var(--font-heading);
  font-size: var(--text-lg);
  font-weight: var(--weight-semibold);
}

.modal__close { color: var(--color-text-muted); }

/* ==========================================================================
   10. TABS
   ========================================================================== */

.tabs__list {
  display: flex;
  gap: var(--space-6);
  border-bottom: 1px solid var(--color-border);
}

.tabs__tab {
  position: relative;
  padding-block: var(--space-3);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--color-text-secondary);
  transition: var(--transition-color);
}

.tabs__tab::after {
  content: '';
  position: absolute;
  left: 0;
  bottom: -1px;
  width: 100%;
  height: 2px;
  background-color: var(--color-accent);
  transform: scaleX(0);
  transition: transform var(--duration-base) var(--ease-out);
}

.tabs__tab:hover { color: var(--color-text); }

.tabs__tab.is-active {
  color: var(--color-text);
}

.tabs__tab.is-active::after { transform: scaleX(1); }

.tabs__panel {
  padding-top: var(--space-6);
  display: none;
}

.tabs__panel.is-active {
  display: block;
  animation: fade-in var(--duration-slow) var(--ease-out) both;
}

/* ==========================================================================
   11. ACCORDION
   ========================================================================== */

.accordion-item {
  border-bottom: 1px solid var(--color-border);
}

.accordion-item__trigger {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding-block: var(--space-5);
  font-family: var(--font-heading);
  font-size: var(--text-md);
  font-weight: var(--weight-medium);
  text-align: left;
}

.accordion-item__icon {
  transition: transform var(--duration-base) var(--ease-out);
  color: var(--color-text-muted);
  flex-shrink: 0;
}

.accordion-item.is-open .accordion-item__icon { transform: rotate(45deg); }

.accordion-item__content {
  overflow: hidden;
  max-height: 0;
  transition: max-height var(--duration-slow) var(--ease-out);
}

.accordion-item__body {
  padding-bottom: var(--space-5);
  color: var(--color-text-secondary);
  font-size: var(--text-sm);
  line-height: var(--leading-relaxed);
}

/* ==========================================================================
   12. PAGINATION
   ========================================================================== */

.pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
}

/* Same [hidden]-vs-class specificity tie as .product-grid[hidden] in
   shop.css — author `display: flex` ties the UA `[hidden] { display:
   none }` rule and wins on source order, so toggling the `hidden`
   attribute alone does nothing without this override. */
.pagination[hidden] { display: none; }

.pagination__item {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: var(--radius-full);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--color-text);
  transition: var(--transition-base);
}

.pagination__item:hover {
  background-color: var(--color-primary-light);
  color: var(--color-text-on-light);
  transform: translateY(-2px);
}

/* accent-dark, not accent: white on the soft accent is ~2:1 and the
   current page number was unreadable — on a control whose entire job is
   to show you where you are. */
.pagination__item.is-active {
  background-color: var(--color-accent-dark);
  color: var(--color-text-inverse);
  box-shadow: var(--shadow-sm);
}

.pagination__item.is-disabled {
  opacity: 0.4;
  pointer-events: none;
}

/* ==========================================================================
   13. NOTIFICATION BADGE
   ========================================================================== */

/* Coral fill + ink text (2026-08-09), not the old rose-gradient +
   white text — white on #F7AEB5 measures 1.8:1 (fails AA); ink on the
   same fill measures 9.37:1 (passes comfortably). */
.badge {
  position: absolute;
  top: -6px;
  right: -6px;
  min-width: 19px;
  height: 19px;
  padding-inline: 4px;
  display: grid;
  place-items: center;
  font-size: 10px;
  font-weight: var(--weight-bold);
  color: var(--color-ink);
  background-color: #F7AEB5;
  border-radius: var(--radius-full);
  border: 2px solid var(--color-secondary);
  box-shadow: var(--shadow-xs);
  animation: scale-in var(--duration-base) var(--ease-spring) both;
}

.badge-dot {
  width: 8px;
  height: 8px;
  padding: 0;
  min-width: 0;
  border: none;
}

/* ==========================================================================
   14. DROPDOWN
   ========================================================================== */

.dropdown {
  position: relative;
  display: inline-block;
}

.dropdown__menu {
  position: absolute;
  top: calc(100% + 10px);
  right: 0;
  min-width: 210px;
  background-color: rgba(255, 255, 255, 0.92);
  -webkit-backdrop-filter: blur(16px) saturate(1.3);
  backdrop-filter: blur(16px) saturate(1.3);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-md);
  box-shadow: var(--shadow-lg);
  padding: var(--space-2);
  opacity: 0;
  visibility: hidden;
  /* Same fix as .mega-menu above: untransitioned pointer-events so the
     closing menu can't intercept clicks/hover as a translucent ghost
     during its fade-out (account menu, search panel, and the PDP share
     dropdown all render through this one class). */
  pointer-events: none;
  transform: translateY(-8px) scale(0.97);
  transform-origin: top right;
  transition: opacity var(--duration-fast) var(--ease-out),
              transform var(--duration-base) var(--ease-spring),
              visibility var(--duration-fast);
  z-index: var(--z-dropdown);
}

.dropdown__menu.is-open {
  opacity: 1;
  visibility: visible;
  pointer-events: auto;
  transform: translateY(0) scale(1);
}

.dropdown__item {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  width: 100%;
  padding: var(--space-3) var(--space-3);
  font-family: var(--font-nav);
  font-size: var(--text-sm);
  color: var(--color-text);
  border-radius: var(--radius-sm);
  transition: var(--transition-base);
  text-align: left;
}

.dropdown__item:hover {
  background-color: var(--color-primary-light);
  color: var(--color-text-on-light);
  padding-left: var(--space-4);
}
.dropdown__divider { height: 1px; background-color: var(--color-border); margin-block: var(--space-2); }

/* ==========================================================================
   15. RESPONSIVE — Breakpoint adjustments for components above
   ========================================================================== */

@media (max-width: 1023px) {
  .footer__grid      { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

@media (max-width: 767px) {
  .footer__grid      { grid-template-columns: 1fr; gap: var(--space-8); }
  .footer__bottom    { grid-template-columns: 1fr; justify-items: start; gap: var(--space-3); }
  .footer__credit    { justify-self: start; text-align: left; }
  .footer__payments  { justify-self: start; }
  .grid-cols-2,
  .grid-cols-3,
  .grid-cols-4       { grid-template-columns: 1fr; }
  .modal             { padding: var(--space-6); }
}

/* ==========================================================================
   17. MOBILE NAV BURGER + DRAWER   (16. ANNOUNCEMENT BAR removed 2026-08)
   ========================================================================== */

.navbar__burger {
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: center;
  flex-shrink: 0;
  gap: 5px;
  width: 44px;
  height: 44px;
}


.navbar__burger span {
  display: block;
  width: 20px;
  height: 1.5px;
  background-color: var(--color-text);
  transition: var(--transition-base);
}

.nav-drawer-backdrop {
  position: fixed;
  inset: 0;
  z-index: var(--z-overlay);
  background-color: var(--color-overlay);
  opacity: 0;
  visibility: hidden;
  /* Same fix as .mega-menu/.dropdown__menu — untransitioned pointer-events
     so the backdrop can't keep swallowing clicks while it fades out. */
  pointer-events: none;
  transition: opacity var(--duration-fast) var(--ease-out), visibility var(--duration-fast);
}

.nav-drawer-backdrop.is-open { opacity: 1; visibility: visible; pointer-events: auto; }

.nav-drawer {
  /* Starts below the sticky site header instead of covering it (was
     top:0, overlapping the navbar entirely) — this is what lets tapping
     the hamburger again close the drawer: the header (with the burger)
     stays visible and un-obscured above the drawer panel itself, rather
     than needing the drawer to out-rank the header's own stacking
     context (.navbar is position:sticky + z-index, its own stacking
     context — no z-index on a nested child could ever rank it above a
     later sibling like this drawer anyway). Also removes the need for a
     second, redundant logo inside the drawer (removed below) — the
     already-visible site header provides that branding while the
     drawer is open. Value matches the navbar's rendered mobile height;
     see also the "hidden lg:flex" burger — this element only matters
     below the lg breakpoint. */
  position: fixed;
  top: var(--navbar-height, 88px);
  left: 0;
  bottom: 0;
  z-index: var(--z-drawer);
  width: min(320px, 85vw);
  background-color: var(--color-surface);
  box-shadow: var(--shadow-xl);
  padding: var(--space-6);
  transform: translateX(-100%);
  transition: transform var(--duration-slow) var(--ease-out);
  overflow-y: auto;
}

.nav-drawer.is-open { transform: translateX(0); }

.nav-drawer__links {
  display: flex;
  flex-direction: column;
}

.nav-drawer__links a {
  padding-block: var(--space-4);
  font-family: var(--font-nav);
  font-size: var(--text-md);
  font-weight: var(--weight-medium);
  color: var(--color-text);
  border-bottom: 1px solid var(--color-border);
}

.nav-drawer__links a.is-active {
  font-weight: var(--weight-semibold);
  color: var(--color-accent-dark);
}

.nav-drawer__divider {
  height: var(--space-4);
}

/* --------------------------------------------------------------------
   Mobile drawer category accordion — tap a category to reveal its
   subcategories, using the sitewide accordion driver (main.js).
   -------------------------------------------------------------------- */
.nav-drawer__accordion { display: flex; flex-direction: column; }

.nav-drawer__accordion-item { border-bottom: 1px solid var(--color-border); }

.nav-drawer__accordion-trigger {
  display: flex;
  align-items: center;
  justify-content: space-between;
  width: 100%;
  padding-block: var(--space-3);
  font-family: var(--font-nav);
  font-size: var(--text-md);
  font-weight: var(--weight-medium);
  color: var(--color-text);
  text-align: left;
}

.nav-drawer__accordion-trigger svg {
  color: var(--color-text-muted);
  transition: transform var(--duration-base) var(--ease-out);
  flex-shrink: 0;
}

.nav-drawer__accordion-item.is-open .nav-drawer__accordion-trigger svg { transform: rotate(180deg); }

.nav-drawer__accordion-content {
  overflow: hidden;
  max-height: 0;
  transition: max-height var(--duration-slow) var(--ease-out);
}

.nav-drawer__accordion-body {
  display: flex;
  flex-direction: column;
  padding-bottom: var(--space-3);
}

.nav-drawer__accordion-body a {
  padding: var(--space-2) 0 var(--space-2) var(--space-4);
  font-family: var(--font-nav);
  font-size: var(--text-sm);
  color: var(--color-text-secondary);
  border-bottom: none;
}

.nav-drawer__accordion-body a:hover { color: var(--color-accent-dark); }

.nav-drawer__accordion-viewall {
  font-weight: var(--weight-semibold) !important;
  color: var(--color-accent-dark) !important;
}

/* ==========================================================================
   18. SEARCH PANEL (navbar dropdown)
   ========================================================================== */

.search-panel {
  width: min(440px, 92vw);
  padding: var(--space-6);
  max-height: 80vh;
  overflow-y: auto;
}

/* 2026-08-10 — on phones this panel was still using the desktop anchor
   (.dropdown__menu's `position:absolute; right:0`, sized off `92vw`),
   which measures against the small search ICON it's attached to, not
   the viewport. With the icon sitting well inside the header (left of
   wishlist/cart/menu), a 92vw-wide panel anchored to the icon's right
   edge ran ~150px off the LEFT edge of the screen — the reported
   clipped category text and "double panel" look (the off-canvas portion
   never rendered; what was visible was the translucent panel's edge
   blurring over the hero photo behind it). Fixed positioning anchored
   to the viewport (not the icon) with symmetric side margins is what
   actually keeps it on-screen at every width, not a wider/narrower
   vw value — vw was never the bug, the anchor point was. */
@media (max-width: 1023px) {
  .navbar__search .dropdown__menu {
    position: fixed;
    top: var(--navbar-height, 88px);
    left: var(--space-3);
    right: var(--space-3);
    min-width: 0;
    transform-origin: top center;
    z-index: var(--z-drawer);
  }

  /* .dropdown__menu and .search-panel are the SAME element (see
     navbar.html) — the unconditional `.search-panel{width:min(440px,92vw)}`
     above still applies at this width unless explicitly cancelled, and
     `width:100%` would be just as wrong (100% of a fixed-position box's
     containing block is the full viewport, ignoring the 12px side
     insets). `auto` is what actually lets the left/right pair above
     resolve the width themselves. */
  .search-panel {
    width: auto;
    max-height: calc(100vh - var(--navbar-height, 88px) - var(--space-6));
  }
}

.search-panel__form { position: relative; }

/* Header row: title + the ONE permanent close button — kept visually and
   structurally separate from the input's own clear-text control below,
   so the two never look like duplicate ×'s sitting next to each other. */
.search-panel__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-4);
}

.search-panel__title {
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  color: var(--color-text);
  margin: 0;
}

.search-panel__close {
  width: 32px;
  height: 32px;
  flex-shrink: 0;
}

.search-panel__input-group .form-control {
  height: 52px;
  border-radius: var(--radius-full);
  padding-left: 2.75rem;
  padding-right: 2.75rem;
}

.search-panel__input-group .input-group__icon { left: var(--space-4); }

/* Native browser clear-× on type="search" inputs is suppressed — it's
   unstyleable and would sit in the exact same spot as our own clear
   button, recreating the "two identical ×'s" problem. */
.search-panel__input-group input[type='search']::-webkit-search-cancel-button,
.search-panel__input-group input[type='search']::-webkit-search-decoration {
  -webkit-appearance: none;
  appearance: none;
}
.search-panel__input-group input[type='search']::-ms-clear { display: none; }

/* Small, subtle, inline "clear text" control — deliberately understated
   (no border, no filled circle at rest) so it reads as part of the input
   rather than a second modal-level action next to the close button. */
.search-panel__clear {
  position: absolute;
  right: var(--space-3);
  top: 50%;
  transform: translateY(-50%);
  display: flex;
  align-items: center;
  justify-content: center;
  width: 24px;
  height: 24px;
  padding: 0;
  border: none;
  border-radius: var(--radius-full);
  background: transparent;
  color: var(--color-text-muted);
  cursor: pointer;
  transition: var(--transition-fast);
}

.search-panel__clear:hover,
.search-panel__clear:focus-visible {
  background-color: var(--color-surface-soft);
  color: var(--color-text);
}

.search-panel__clear[hidden] { display: none; }

/* .input-group__icon is normally decorative (pointer-events: none) — this
   is the one place it's a real <button type="submit">, so those need
   re-enabling plus a plain button reset (no border/background of its
   own), while keeping the exact same position/color as the decorative
   version everywhere else. */
.search-panel__submit {
  pointer-events: auto;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
  border-radius: var(--radius-full);
}

.search-panel__submit:hover,
.search-panel__submit:focus-visible {
  color: var(--color-accent-dark);
}

.search-panel__suggest {
  margin-top: var(--space-5);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

/* THE FIX: [hidden] and this class-based `display: flex` are the same
   specificity (0,0,1,0 each) — without this override, the author rule
   above wins the cascade tie (it comes later in the stylesheet than the
   browser's UA default `[hidden] { display: none }`), so setting
   suggest.hidden = true in JS had ZERO visual effect. This is why
   Popular Searches / Shop by Category kept showing while typing. */
.search-panel__suggest[hidden] { display: none; }

.search-panel__label {
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.search-panel__tags {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-2);
}

.search-panel__cat-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-2);
}

.search-panel__cat {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-3);
  border-radius: var(--radius-md);
  background-color: var(--color-surface-soft);
  border: 1px solid var(--color-border);
  color: var(--color-text);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  transition: var(--transition-base);
}

.search-panel__cat svg { color: var(--color-accent-dark); flex-shrink: 0; }

.search-panel__cat:hover {
  border-color: var(--color-accent);
  background-color: var(--color-primary-light);
  transform: translateY(-2px);
  box-shadow: var(--shadow-xs);
}

.search-results {
  display: none;
  flex-direction: column;
  gap: var(--space-1);
  margin-top: var(--space-3);
  max-height: 320px;
  overflow-y: auto;
}

.search-results.is-open { display: flex; }

.search-results__item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  padding: var(--space-2);
  border-radius: var(--radius-sm);
  transition: var(--transition-base);
}

.search-results__item:hover,
.search-results__item.is-active {
  background-color: var(--color-surface-soft);
}

.search-results__thumb {
  width: 40px;
  height: 40px;
  border-radius: var(--radius-sm);
  object-fit: cover;
  background-color: var(--color-surface-soft);
  flex-shrink: 0;
}

.search-results__info {
  display: flex;
  flex-direction: column;
  gap: 1px;
  min-width: 0;
  flex: 1;
}

.search-results__name {
  font-size: var(--text-sm);
  color: var(--color-text);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.search-results__highlight {
  background: none;
  color: var(--color-accent-dark);
  font-weight: var(--weight-semibold);
}

.search-results__brand {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.search-results__meta {
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 2px;
  flex-shrink: 0;
}

.search-results__price {
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  color: var(--color-text-secondary);
  white-space: nowrap;
}

.search-results__stock {
  font-size: 11px;
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
}

.search-results__stock.is-out { color: var(--color-danger); }

.search-results__empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--space-2);
  padding: var(--space-8) var(--space-4);
  text-align: center;
}

.search-results__empty-icon {
  display: grid;
  place-items: center;
  width: 48px;
  height: 48px;
  border-radius: var(--radius-full);
  background-color: var(--color-surface-soft);
  color: var(--color-text-muted);
}

.search-results__empty-title {
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  color: var(--color-text);
  margin: 0;
}

.search-results__empty-message {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  margin: 0;
}

/* ==========================================================================
   19. FLASH ALERT STACK
   ========================================================================== */

.flash-stack {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  padding-top: var(--space-4);
}

/* ==========================================================================
   20. FOOTER — extra elements
   ========================================================================== */

.footer__brand { padding-right: var(--space-6); }

.footer__tagline {
  margin-top: var(--space-4);
  margin-bottom: var(--space-5);
  font-family: var(--font-nav);
  font-weight: var(--weight-regular);
  font-size: var(--text-sm);
  color: var(--color-text-secondary);
  max-width: 32ch;
  line-height: var(--leading-relaxed);
}

.footer__payments { color: var(--color-text-secondary); }

/* ==========================================================================
   21. WISHLIST BUTTON (on product media)
   ========================================================================== */

.wishlist-btn {
  display: grid;
  place-items: center;
  width: 38px;
  height: 38px;
  border-radius: var(--radius-full);
  background-color: rgba(255, 255, 255, 0.88);
  color: var(--color-text);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
  box-shadow: var(--shadow-xs);
  transition: color var(--duration-fast) var(--ease-out),
              transform var(--duration-base) var(--ease-spring),
              box-shadow var(--duration-base) var(--ease-out);
}

.wishlist-btn:hover {
  color: var(--color-danger);
  transform: scale(1.12);
  box-shadow: var(--shadow-sm);
}

.wishlist-btn:active { transform: scale(0.94); }

.wishlist-btn.is-active {
  color: var(--color-danger);
}

.wishlist-btn.is-active svg {
  fill: currentColor;
  animation: heart-pop var(--duration-slow) var(--ease-spring);
}

@keyframes heart-pop {
  0%   { transform: scale(0.6); }
  55%  { transform: scale(1.25); }
  100% { transform: scale(1); }
}

/* ==========================================================================
   22. IMAGE FALLBACK (graceful degradation for missing product imagery)
   ========================================================================== */

.media-fallback.is-broken {
  visibility: hidden;
}

/* ==========================================================================
   23. FILTER PANEL (shop sidebar / mobile drawer)
   ========================================================================== */

.filter-panel {
  display: flex;
  flex-direction: column;
  gap: var(--space-8);
}

.filter-group {
  padding-bottom: var(--space-6);
  border-bottom: 1px solid var(--color-border);
}

.filter-group:last-child { border-bottom: none; padding-bottom: 0; }

.filter-group__title {
  display: flex;
  align-items: center;
  justify-content: space-between;
  font-family: var(--font-heading);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wide);
  text-transform: uppercase;
  color: var(--color-text);
  margin-bottom: var(--space-4);
}

.filter-group__list {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.filter-count {
  font-size: var(--text-xs);
  color: var(--color-text-muted);
  font-weight: var(--weight-regular);
}

.filter-panel__clear {
  width: 100%;
}

/* --------------------------------------------------------------------
   Range slider (price filter)
   -------------------------------------------------------------------- */
.range-slider {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.range-slider__values {
  display: flex;
  justify-content: space-between;
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--color-text);
}

.range-slider input[type='range'] {
  appearance: none;
  width: 100%;
  height: 4px;
  border-radius: var(--radius-full);
  background: linear-gradient(to right, var(--color-accent) 0%, var(--color-accent) var(--range-fill, 50%), var(--color-border) var(--range-fill, 50%), var(--color-border) 100%);
  cursor: pointer;
}

.range-slider input[type='range']::-webkit-slider-thumb {
  appearance: none;
  width: 18px;
  height: 18px;
  border-radius: var(--radius-full);
  background: var(--color-surface);
  border: 2px solid var(--color-accent);
  box-shadow: var(--shadow-sm);
  transition: var(--transition-fast);
}

.range-slider input[type='range']::-webkit-slider-thumb:hover {
  transform: scale(1.1);
}

.range-slider input[type='range']::-moz-range-thumb {
  width: 18px;
  height: 18px;
  border-radius: var(--radius-full);
  background: var(--color-surface);
  border: 2px solid var(--color-accent);
  box-shadow: var(--shadow-sm);
}

/* --------------------------------------------------------------------
   Star rating filter (checkbox rows with visual stars)
   -------------------------------------------------------------------- */
.rating-filter {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  cursor: pointer;
  font-size: var(--text-sm);
  color: var(--color-text-secondary);
}

.rating-filter .rating-stars { font-size: var(--text-sm); }

/* --------------------------------------------------------------------
   Mobile filter drawer (mirrors .nav-drawer for consistent motion)
   -------------------------------------------------------------------- */
.filter-drawer-backdrop {
  position: fixed;
  inset: 0;
  z-index: var(--z-overlay);
  background-color: var(--color-overlay);
  opacity: 0;
  visibility: hidden;
  /* Same fix as .mega-menu/.dropdown__menu — untransitioned pointer-events
     so the backdrop can't keep swallowing clicks while it fades out. */
  pointer-events: none;
  transition: opacity var(--duration-fast) var(--ease-out), visibility var(--duration-fast);
}

.filter-drawer-backdrop.is-open { opacity: 1; visibility: visible; pointer-events: auto; }

.filter-drawer {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: var(--z-drawer);
  width: min(360px, 88vw);
  background-color: var(--color-surface);
  box-shadow: var(--shadow-xl);
  padding: var(--space-6);
  overflow-y: auto;
  transform: translateX(100%);
  transition: transform var(--duration-slow) var(--ease-out);
}

.filter-drawer.is-open { transform: translateX(0); }

.filter-drawer__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  margin-bottom: var(--space-6);
}

/* ==========================================================================
   24. SHOP TOOLBAR
   ========================================================================== */

.shop-toolbar {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-4) var(--space-5);
  background-color: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-xs);
}

.shop-toolbar__search { flex: 1 1 240px; }

.shop-toolbar__sort { flex: 0 0 auto; }

.shop-toolbar__meta {
  font-size: var(--text-sm);
  color: var(--color-text-secondary);
  white-space: nowrap;
}

.view-toggle {
  display: inline-flex;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-full);
  padding: 3px;
  gap: 2px;
}

.view-toggle__btn {
  display: grid;
  place-items: center;
  width: 34px;
  height: 34px;
  border-radius: var(--radius-full);
  color: var(--color-text-muted);
  transition: var(--transition-base);
}

.view-toggle__btn.is-active {
  background-color: var(--color-primary);
  color: var(--color-text);
}

/* ==========================================================================
   25. PRODUCT GRID — list view variant + brand line + badges row
   ========================================================================== */

/* Muted ink (2026-08-09, was --color-accent-dark rose) — brand labels
   are a quiet meta line, same "ink family" treatment as .eyebrow. */
.product-card__brand {
  font-size: var(--text-xs);
  font-weight: var(--weight-medium);
  color: var(--color-text-secondary);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
}

.product-card__badges {
  position: absolute;
  top: var(--space-3);
  left: var(--space-3);
  z-index: var(--z-raised);
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--space-1);
}

/* Inside the badges row the container owns positioning — badges flow
   in the column instead of stacking on one absolute spot */
.product-card__badges .product-card__badge {
  position: static;
}

.product-grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--space-6);
}

@media (min-width: 768px) {
  .product-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

@media (min-width: 1280px) {
  .product-grid { grid-template-columns: repeat(4, minmax(0, 1fr)); }
}

@media (min-width: 1440px) {
  .product-grid { grid-template-columns: repeat(5, minmax(0, 1fr)); }
}

@media (min-width: 1800px) {
  .product-grid { grid-template-columns: repeat(6, minmax(0, 1fr)); }
}

/* List view: stack as horizontal rows instead of a grid */
.product-grid.is-list-view {
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.product-grid.is-list-view .product-card {
  flex-direction: row;
  align-items: stretch;
}

.product-grid.is-list-view .product-card__media {
  width: 200px;
  flex-shrink: 0;
  aspect-ratio: 1 / 1;
}

.product-grid.is-list-view .product-card__body {
  flex: 1;
  justify-content: center;
  padding-inline: var(--space-6);
}

.product-grid.is-list-view .quick-view-btn { display: none; }

@media (max-width: 640px) {
  .product-grid.is-list-view .product-card { flex-direction: column; }
  .product-grid.is-list-view .product-card__media { width: 100%; }
}

/* ==========================================================================
   22. TOUCH TARGET SIZING (touch/coarse pointers only)
   Compact icon controls (wishlist heart, quick-add, view-toggle,
   pagination, quick-view) are sized for a dense premium grid on desktop.
   On touch input there's no hover affordance to help aim, so expand the
   tappable area to the 44px minimum via an invisible pseudo-element
   rather than growing the icon itself — keeps the compact visual size.
   ========================================================================== */
@media (hover: none), (pointer: coarse) {
  .wishlist-btn,
  .product-card__quick-add,
  .view-toggle__btn,
  .pagination__item {
    position: relative;
  }

  .wishlist-btn::after,
  .product-card__quick-add::after,
  .view-toggle__btn::after,
  .pagination__item::after {
    content: '';
    position: absolute;
    top: 50%;
    left: 50%;
    width: max(44px, 100%);
    height: max(44px, 100%);
    transform: translate(-50%, -50%);
  }

  .quick-view-btn { padding-block: var(--space-3); }
}

/* Skeleton placeholder card (shown briefly while "loading" filtered results) */
.product-card-skeleton {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
  border-radius: var(--radius-lg);
  overflow: hidden;
}

/* ==========================================================================
   26. EMPTY STATE
   ========================================================================== */

.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding-block: var(--space-20);
  gap: var(--space-5);
}

.empty-state[hidden] { display: none; }

/* 2026-08-09 — was color: var(--color-accent), 2.01:1 on white (a real
   AA failure, same bug class as the star-picker fix earlier). Flat
   light-coral-tint fill + ink icon, matching every other icon chip. */
.empty-state__icon {
  display: grid;
  place-items: center;
  width: 96px;
  height: 96px;
  border-radius: var(--radius-full);
  background-color: #FDEEF0;
  color: #2A1912;
  box-shadow: var(--shadow-sm);
  animation: empty-float 4s var(--ease-in-out) infinite alternate;
}

@keyframes empty-float {
  from { transform: translateY(0); }
  to   { transform: translateY(-8px); }
}

@media (prefers-reduced-motion: reduce) {
  .empty-state__icon { animation: none; }
}

.empty-state__title {
  font-family: var(--font-heading);
  font-size: var(--text-xl);
  font-weight: var(--weight-semibold);
}

.empty-state__message {
  max-width: 40ch;
  color: var(--color-text-secondary);
}

/* ==========================================================================
   27. SHOP HEADER (heading + meta)
   ========================================================================== */

.shop-header {
  padding-block: var(--space-6) var(--space-5);
  text-align: center;
}

.shop-header__title {
  font-size: var(--text-2xl);
  margin-bottom: var(--space-2);
}

.shop-header__desc {
  max-width: 56ch;
  margin-inline: auto;
  font-size: var(--text-sm);
  color: var(--color-text-secondary);
}

.shop-header__count {
  margin-top: var(--space-2);
  font-size: var(--text-xs);
  color: var(--color-text-muted);
}

/* ==========================================================================
   28. QUANTITY STEPPER (Quick View modal + Product Details page)
   ========================================================================== */

.qty-stepper {
  display: inline-flex;
  align-items: center;
  border: 1px solid var(--color-border);
  border-radius: var(--radius-full);
  overflow: hidden;
}

.qty-stepper__btn {
  display: grid;
  place-items: center;
  width: 40px;
  height: 44px;
  color: var(--color-text);
  font-size: var(--text-md);
  transition: var(--transition-base);
}

.qty-stepper__btn:hover { background-color: var(--color-surface-soft); color: var(--color-accent-dark); }
.qty-stepper__btn:disabled { opacity: 0.4; cursor: not-allowed; }

.qty-stepper__input {
  width: 44px;
  height: 44px;
  border: none;
  border-left: 1px solid var(--color-border);
  border-right: 1px solid var(--color-border);
  text-align: center;
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  -moz-appearance: textfield;
}

.qty-stepper__input::-webkit-outer-spin-button,
.qty-stepper__input::-webkit-inner-spin-button {
  -webkit-appearance: none;
  margin: 0;
}

.qty-stepper-wrap { display: inline-flex; flex-direction: column; gap: var(--space-1); }

.qty-stepper__hint {
  font-size: var(--text-xs);
  color: var(--color-text-secondary);
}

.qty-stepper__subtotal {
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  color: var(--color-text);
}

.quick-view__actions {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--space-4);
}

.quick-view__name {
  font-family: var(--font-heading);
  font-size: var(--text-lg);
  font-weight: var(--weight-semibold);
  margin-top: var(--space-1);
  margin-bottom: var(--space-2);
}

/* ==========================================================================
   29. SAVINGS BADGE & STOCK STATUS
   ========================================================================== */

.savings-badge {
  display: inline-flex;
  align-items: center;
  padding: var(--space-1) var(--space-3);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  color: var(--color-danger);
  background-color: var(--color-danger-bg);
  border-radius: var(--radius-full);
}

.stock-status {
  display: inline-flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
}

.stock-status::before {
  content: '';
  width: 8px;
  height: 8px;
  border-radius: var(--radius-full);
  background-color: currentColor;
}

.stock-status.in-stock { color: var(--color-success); }
.stock-status.low-stock { color: var(--color-warning); }
.stock-status.out-of-stock { color: var(--color-danger); }

/* ==========================================================================
   30. RATING BREAKDOWN (review summary bars)
   ========================================================================== */

.rating-summary {
  display: flex;
  flex-wrap: wrap;
  gap: var(--space-10);
  padding: var(--space-6);
  background-color: var(--color-surface-soft);
  border-radius: var(--radius-lg);
}

.rating-summary__score {
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  min-width: 140px;
}

.rating-summary__number {
  font-family: var(--font-heading);
  font-size: var(--text-3xl);
  font-weight: var(--weight-bold);
  color: var(--color-text);
  line-height: 1;
}

.rating-summary__bars {
  flex: 1;
  min-width: 220px;
  display: flex;
  flex-direction: column;
  gap: var(--space-2);
}

.rating-bar {
  display: grid;
  grid-template-columns: 50px 1fr 40px;
  align-items: center;
  gap: var(--space-3);
  font-size: var(--text-xs);
  color: var(--color-text-secondary);
}

.rating-bar__track {
  height: 6px;
  border-radius: var(--radius-full);
  background-color: var(--color-border);
  overflow: hidden;
}

.rating-bar__fill {
  height: 100%;
  background-color: var(--color-accent);
  border-radius: var(--radius-full);
}

/* Interactive star picker (Write a Review form) */
.star-picker {
  display: inline-flex;
  gap: var(--space-1);
  font-size: var(--text-xl);
  color: var(--color-border-strong);
}

.star-picker__star {
  cursor: pointer;
  transition: var(--transition-fast);
}

/* 2026-08-09 — was --color-accent (2.01:1 on white, a real AA
   failure, not just a perception issue). --color-accent-dark clears
   4.51:1. */
.star-picker__star.is-active,
.star-picker__star:hover,
.star-picker__star:hover ~ .star-picker__star {
  color: var(--color-accent-dark);
}

/* ==========================================================================
   31. STICKY MOBILE ADD-TO-CART BAR
   ========================================================================== */

.sticky-cart-bar {
  position: fixed;
  left: 0;
  right: 0;
  /* Rests just above the persistent bottom nav rather than at the very
     edge — the bottom nav occupies that edge on every mobile page now. */
  bottom: var(--bottom-nav-height, 0px);
  z-index: var(--z-sticky);
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-3) var(--space-4);
  background-color: var(--color-surface);
  border-top: 1px solid var(--color-border);
  box-shadow: var(--shadow-lg);
  transform: translateY(100%);
  transition: transform var(--duration-base) var(--ease-out);
}

.sticky-cart-bar.is-visible { transform: translateY(0); }

.sticky-cart-bar__price {
  display: flex;
  flex-direction: column;
  line-height: 1.2;
}

.sticky-cart-bar__price .price { font-size: var(--text-md); }

.sticky-cart-bar .btn { flex: 1; }

@media (min-width: 1024px) {
  .sticky-cart-bar { display: none; }
}

/* --------------------------------------------------------------------
   Floating WhatsApp button — site-wide (templates/layouts/base.html),
   hidden entirely when no number is configured (see
   templates/partials/whatsapp_button.html).
   -------------------------------------------------------------------- */
.whatsapp-float {
  position: fixed;
  right: var(--space-5);
  /* Above the persistent bottom nav on mobile (--bottom-nav-height is 0
     on desktop, where the bottom nav doesn't render). */
  bottom: calc(var(--bottom-nav-height, 0px) + var(--space-5));
  z-index: var(--z-sticky);
  width: 56px;
  height: 56px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-full);
  background-color: #25d366;
  color: #fff;
  box-shadow: var(--shadow-lg);
  transition: transform var(--duration-fast) var(--ease-spring), box-shadow var(--duration-fast) var(--ease-out);
}

.whatsapp-float:hover {
  transform: scale(1.08);
  box-shadow: var(--shadow-xl);
  color: #fff;
}

/* The mobile sticky add-to-cart bar (product pages, <1024px) is its own
   fixed-position element sharing the same bottom edge — shift the
   WhatsApp button up above it rather than letting them overlap. Reacts
   live to .is-visible (the bar only appears once scrolled past the CTA
   block) via :has(), no JS coordination needed between the two. */
@media (max-width: 1023px) {
  body:has(.sticky-cart-bar.is-visible) .whatsapp-float {
    bottom: calc(var(--bottom-nav-height, 0px) + var(--space-5) + 76px);
  }
}

/* --------------------------------------------------------------------
   Persistent bottom navigation (mobile only, <1024px) — templates/
   partials/bottom_nav.html, included site-wide from base.html. This is
   mobile's equivalent of the desktop top navbar's always-visible links:
   Home / Shop / Wishlist / Cart / Account, one thumb-reach away instead
   of buried behind the hamburger drawer.

   --bottom-nav-height is defined here (not in variables.css) because it
   only exists on the widths where the bar actually renders — every
   other fixed-bottom element (.sticky-cart-bar, .whatsapp-float) reads
   it via var(--bottom-nav-height, 0px), so on desktop, where this
   variable is never set, they fall back to sitting flush with the
   viewport edge exactly as before.
   -------------------------------------------------------------------- */
@media (max-width: 1023px) {
  :root { --bottom-nav-height: calc(60px + env(safe-area-inset-bottom, 0px)); }

  /* So the footer (and anything else that scrolls to the bottom of the
     page) never ends up hidden underneath the bar. */
  body { padding-bottom: var(--bottom-nav-height); }
}

.bottom-nav {
  position: fixed;
  left: 0;
  right: 0;
  bottom: 0;
  z-index: var(--z-sticky);
  display: flex;
  align-items: stretch;
  height: 60px;
  padding-bottom: env(safe-area-inset-bottom, 0px);
  background-color: var(--color-surface);
  border-top: 1px solid var(--color-champagne);
  box-shadow: 0 -2px 12px rgba(42, 25, 18, 0.06);
}

.bottom-nav__item {
  flex: 1 1 0;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  gap: 3px;
  min-width: 44px;
  color: var(--color-ink-soft, var(--color-text-secondary));
  font-family: var(--font-nav);
  font-size: 11px;
  font-weight: var(--weight-medium);
  letter-spacing: var(--tracking-wide);
  transition: color var(--duration-fast) var(--ease-out);
}

.bottom-nav__item svg { transition: transform var(--duration-fast) var(--ease-spring); }

/* 2026-08-10 — hardcoded, not var(--color-primary-pressed): root defines
   that variable as #AF5F66 (deeper rose, chosen for 4.5:1 text
   contrast), but .page-home redefines the SAME name to #F7AEB5 (bright
   decorative coral, explicitly documented there as fill-only — coral
   text fails AA at 1.8:1). The bottom nav is shared, persistent, sitewide
   markup (base.html, every page), so it rendered a genuinely different
   — and less accessible — color on the homepage than everywhere else.
   Confirmed via getComputedStyle: rgb(247,174,181) on home vs
   rgb(175,95,102) on shop/cart/wishlist before this fix. Literal hex
   matching the root/correct value is what keeps this identical and
   AA-safe on every page regardless of that page's token scope. */
.bottom-nav__item.is-active {
  color: #AF5F66;
}

.bottom-nav__item.is-active svg { transform: translateY(-1px); }

.bottom-nav__item:active svg { transform: scale(0.9); }

.bottom-nav__icon-wrap { position: relative; display: inline-flex; }

.bottom-nav__badge {
  position: absolute;
  top: -4px;
  right: -8px;
  min-width: 16px;
  height: 16px;
  padding: 0 3px;
  display: grid;
  place-items: center;
  border-radius: var(--radius-full);
  /* Hardcoded to match the top navbar's .badge exactly (same literal
     #F7AEB5, not a variable) — same var(--color-primary-pressed)
     page-scope collision described above would otherwise apply here too. */
  background-color: #F7AEB5;
  color: var(--color-ink);
  font-size: 10px;
  font-weight: var(--weight-semibold);
  line-height: 1;
  font-variant-numeric: tabular-nums;
}

/* ==========================================================================
   33. MINI CART (navbar slide-out drawer)
   ========================================================================== */

.mini-cart-backdrop {
  position: fixed;
  inset: 0;
  z-index: var(--z-overlay);
  background-color: var(--color-overlay);
  opacity: 0;
  visibility: hidden;
  /* Same fix as .mega-menu/.dropdown__menu — untransitioned pointer-events
     so the backdrop can't keep swallowing clicks meant for the page
     underneath while it fades out after the cart drawer closes. */
  pointer-events: none;
  transition: opacity var(--duration-fast) var(--ease-out), visibility var(--duration-fast);
}

.mini-cart-backdrop.is-open { opacity: 1; visibility: visible; pointer-events: auto; }

.mini-cart {
  position: fixed;
  top: 0;
  right: 0;
  bottom: 0;
  z-index: var(--z-drawer);
  width: min(400px, 90vw);
  display: flex;
  flex-direction: column;
  background-color: var(--color-surface);
  box-shadow: var(--shadow-xl);
  transform: translateX(100%);
  transition: transform var(--duration-slow) var(--ease-out);
}

.mini-cart.is-open { transform: translateX(0); }

.mini-cart__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--space-5) var(--space-6);
  border-bottom: 1px solid var(--color-border);
}

.mini-cart__body {
  flex: 1;
  overflow-y: auto;
  padding: var(--space-4) var(--space-6);
  display: flex;
  flex-direction: column;
  gap: var(--space-4);
}

.mini-cart-item {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  animation: fade-in var(--duration-base) var(--ease-out) both;
}

.mini-cart-item img {
  width: 64px;
  height: 64px;
  object-fit: cover;
  border-radius: var(--radius-md);
  background-color: var(--color-surface-soft);
  flex-shrink: 0;
}

.mini-cart-item__info {
  flex: 1;
  min-width: 0;
}

.mini-cart-item__name {
  font-size: var(--text-sm);
  font-weight: var(--weight-medium);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}

.mini-cart__empty {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding-block: var(--space-16);
}

.mini-cart__footer {
  padding: var(--space-5) var(--space-6);
  border-top: 1px solid var(--color-border);
}

.mini-cart__subtotal {
  display: flex;
  justify-content: space-between;
  align-items: center;
  font-weight: var(--weight-medium);
  margin-bottom: var(--space-4);
}

/* ==========================================================================
   34. CHECKOUT PROGRESS STEPS
   ========================================================================== */

.checkout-steps {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--space-2);
  flex-wrap: wrap;
  padding-block: var(--space-6);
}

.checkout-step {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-size: var(--text-sm);
  color: var(--color-text-muted);
}

.checkout-step__dot {
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border-radius: var(--radius-full);
  border: 1.5px solid var(--color-border-strong);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  color: var(--color-text-muted);
  background-color: var(--color-surface);
  transition: var(--transition-base);
}

.checkout-step.is-complete .checkout-step__dot {
  background-color: var(--color-success);
  border-color: var(--color-success);
  color: var(--color-text-inverse);
}

/* accent-dark, not accent — the step number inside the active dot was
   white on a light rose fill (~2:1). */
.checkout-step.is-active .checkout-step__dot {
  background-color: var(--color-accent-dark);
  border-color: var(--color-accent-dark);
  color: var(--color-text-inverse);
}

.checkout-step.is-active .checkout-step__label {
  color: var(--color-text);
  font-weight: var(--weight-semibold);
}

.checkout-step__connector {
  width: clamp(24px, 4vw, 56px);
  height: 1.5px;
  background-color: var(--color-border-strong);
}

.checkout-step.is-complete ~ .checkout-step__connector,
.checkout-step__connector.is-complete {
  background-color: var(--color-success);
}

@media (max-width: 640px) {
  .checkout-step__label { display: none; }
}

/* ==========================================================================
   35. CHECKOUT CARD (shared section wrapper)
   ========================================================================== */

.checkout-card {
  padding: var(--space-6);
  background-color: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  margin-bottom: var(--space-6);
}

.checkout-card__title {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  font-size: var(--text-md);
  margin-bottom: var(--space-5);
}

/* Light-coral-tint (2026-08-09 rev 2 — was peach #F1D8C6). Visually
   confirmed live on checkout (added an item, walked the flow) before
   changing — matched the other icon chips' cream/tan appearance
   exactly, so it gets the same fix. */
.checkout-card__title .checkout-card__number {
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border-radius: var(--radius-full);
  background-color: #FDEEF0;
  color: var(--color-ink);
  font-size: var(--text-sm);
  font-weight: var(--weight-semibold);
  flex-shrink: 0;
}

.form-grid {
  display: grid;
  grid-template-columns: 1fr;
  gap: var(--space-4) var(--space-5);
}

@media (min-width: 560px) {
  .form-grid--2 { grid-template-columns: 1fr 1fr; }
  .form-grid--3 { grid-template-columns: 1fr 1fr 1fr; }
}

.form-grid .form-group { margin-bottom: 0; }
.form-span-full { grid-column: 1 / -1; }

/* ==========================================================================
   36. PAYMENT OPTION CARDS
   ========================================================================== */

.option-card-list {
  display: flex;
  flex-direction: column;
  gap: var(--space-3);
}

.option-card {
  position: relative;
  display: flex;
  align-items: center;
  gap: var(--space-4);
  padding: var(--space-4) var(--space-5);
  border: 1.5px solid var(--color-border);
  border-radius: var(--radius-md);
  cursor: pointer;
  transition: var(--transition-base);
}

.option-card:hover { border-color: var(--color-border-strong); }

.option-card:has(input:checked),
.option-card.is-selected {
  border-color: var(--color-accent);
  background-color: var(--color-surface-soft);
  box-shadow: 0 0 0 1px var(--color-accent);
}

.option-card:has(input:disabled) {
  opacity: 0.55;
  cursor: not-allowed;
}

.option-card input[type='radio'] {
  appearance: none;
  width: 20px;
  height: 20px;
  flex-shrink: 0;
  border: 1.5px solid var(--color-border-strong);
  border-radius: var(--radius-full);
  display: grid;
  place-content: center;
  transition: var(--transition-fast);
}

.option-card input[type='radio']::before {
  content: '';
  width: 10px;
  height: 10px;
  border-radius: var(--radius-full);
  transform: scale(0);
  transition: transform var(--duration-fast) var(--ease-spring);
  background-color: var(--color-text-inverse);
}

/* A checked radio is a state indicator, so the fill has to be clearly
   distinguishable from the unchecked one — the soft accent sat at
   2.01:1 against the surrounding white, which is under the 3:1 WCAG
   1.4.11 asks of non-text UI. */
.option-card input[type='radio']:checked {
  background-color: var(--color-accent-dark);
  border-color: var(--color-accent-dark);
}

.option-card input[type='radio']:checked::before { transform: scale(1); }

.option-card__icon {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border-radius: var(--radius-md);
  background-color: var(--color-surface-soft);
  color: var(--color-text-secondary);
  flex-shrink: 0;
}

.option-card__body { flex: 1; min-width: 0; }

.option-card__title {
  display: flex;
  align-items: center;
  gap: var(--space-2);
  font-weight: var(--weight-medium);
  font-size: var(--text-sm);
  color: var(--color-text);
}

.option-card__meta {
  font-size: var(--text-xs);
  color: var(--color-text-secondary);
  margin-top: 2px;
}

/* ==========================================================================
   37. ORDER CONFIRMATION
   ========================================================================== */

.order-confirmation {
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  padding-block: var(--space-16);
  max-width: 560px;
  margin-inline: auto;
}

.order-confirmation__icon {
  display: grid;
  place-items: center;
  width: 88px;
  height: 88px;
  border-radius: var(--radius-full);
  background-color: var(--color-success-bg);
  color: var(--color-success);
  margin-bottom: var(--space-6);
  animation: scale-in var(--duration-slow) var(--ease-spring) both;
}

/* Status modifiers — reused by Payment Failed / Payment Cancelled */
.order-confirmation__icon.is-failed {
  background-color: var(--color-danger-bg);
  color: var(--color-danger);
}

.order-confirmation__icon.is-cancelled {
  background-color: var(--color-warning-bg);
  color: var(--color-warning);
}

.order-confirmation__number.is-failed,
.order-confirmation__number.is-cancelled {
  color: var(--color-text);
}

.order-confirmation__number {
  font-family: var(--font-heading);
  font-size: var(--text-xl);
  font-weight: var(--weight-bold);
  color: var(--color-accent-dark);
  margin-block: var(--space-2) var(--space-5);
}

.order-confirmation__details {
  width: 100%;
  text-align: left;
  background-color: var(--color-surface-soft);
  border-radius: var(--radius-lg);
  padding: var(--space-6);
  margin-block: var(--space-8);
}

.order-confirmation__row {
  display: flex;
  justify-content: space-between;
  font-size: var(--text-sm);
  padding-block: var(--space-2);
  border-bottom: 1px solid var(--color-border);
}

.order-confirmation__row:last-child { border-bottom: none; }

/* ==========================================================================
   38. AUTHENTICATION (login / register / forgot / reset password)
   ========================================================================== */

.auth-section {
  display: flex;
  align-items: center;
  justify-content: center;
  padding-block: var(--space-12) var(--space-20);
}

.auth-card {
  width: 100%;
  max-width: 440px;
  padding: var(--space-8);
  background-color: var(--color-surface);
  border: 1px solid var(--color-border);
  border-radius: var(--radius-lg);
  box-shadow: var(--shadow-sm);
}

.auth-card__header {
  text-align: center;
  margin-bottom: var(--space-8);
}

.auth-card__title {
  font-size: var(--text-2xl);
  margin-bottom: var(--space-2);
}

.auth-card__subtitle {
  color: var(--color-text-secondary);
  font-size: var(--text-sm);
}

.auth-card__footer {
  text-align: center;
  margin-top: var(--space-6);
  font-size: var(--text-sm);
  color: var(--color-text-secondary);
}

.auth-card__divider {
  display: flex;
  align-items: center;
  gap: var(--space-3);
  margin-block: var(--space-6);
  color: var(--color-text-muted);
  font-size: var(--text-xs);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
}

.auth-card__divider::before,
.auth-card__divider::after {
  content: '';
  flex: 1;
  height: 1px;
  background-color: var(--color-border);
}

.auth-row {
  display: flex;
  align-items: center;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--space-3);
  margin-bottom: var(--space-5);
}

/* Password field with a show/hide toggle button inside the input */
.password-field {
  position: relative;
}

.password-field .form-control {
  padding-right: var(--space-10);
}

.password-field__toggle {
  position: absolute;
  top: 50%;
  right: var(--space-3);
  transform: translateY(-50%);
  color: var(--color-text-muted);
  padding: var(--space-1);
  border-radius: var(--radius-sm);
  transition: var(--transition-color);
}

.password-field__toggle:hover { color: var(--color-text); }

/* Password strength meter (register form) */
.strength-meter {
  margin-top: var(--space-2);
}

.strength-meter__track {
  display: flex;
  gap: var(--space-1);
  height: 4px;
}

.strength-meter__segment {
  flex: 1;
  border-radius: var(--radius-full);
  background-color: var(--color-border);
  transition: background-color var(--duration-fast) var(--ease-out);
}

.strength-meter__segment.is-active {
  background-color: var(--strength-color, var(--color-accent));
}

.strength-meter__label {
  display: block;
  margin-top: var(--space-1);
  font-size: var(--text-xs);
  color: var(--color-text-secondary);
}

/* Reuses .form-checkbox for Remember Me / Terms acceptance rows */
.auth-form .form-checkbox { margin-bottom: var(--space-5); }


/* Variant label on a wishlist card — a shopper who saved three shades
   needs to see which one each card is, not three identical products. */
/* Light coral tint (2026-08-09, was pale champagne/cream) — text stays
   ink so it's still comfortably readable on the lighter tint. */
.wishlist-variant {
  display: inline-block;
  padding: 2px var(--space-3);
  font-size: var(--text-xs);
  font-weight: var(--weight-semibold);
  letter-spacing: var(--tracking-wide);
  color: var(--color-ink);
  background-color: #FDEEF0;
  border-radius: var(--radius-full);
}
