/* Storefront design system — the primitives every component composes from.
 *
 * Loads first (the theme inlines its stylesheets in sorted order and `_d` sorts
 * before `_m`, `b`, `c`…), after the Ruby-generated colour/spacing/font vars. So
 * everything here may read --color-*, and everything after here may read --ds-*.
 *
 * Why a token layer at all: before this, components hardcoded their own values —
 * `0.75rem` radius here, `0.5rem` there, `200ms ease-out` in one file and
 * `150ms` in the next, and `border: 1px solid var(--color-secondary)` in several
 * places, which is a *near-black* hairline (the default secondary is #0f172a),
 * not the near-invisible one the design wanted. Independent guesses read as
 * amateur work no matter how good each guess is. One scale, used everywhere,
 * reads as designed.
 *
 * Nothing here emits CSS on its own except the small primitives at the bottom
 * (.hi, .ds-focusable, .ds-container). It is a vocabulary, not a stylesheet.
 */

:root {
  /* ── Hairlines & surfaces ───────────────────────────────────────────────
     Derived from the palette rather than fixed, so a client's dark or warm
     background keeps borders that sit *just* off it instead of a grey that
     fights it. 8% is the Stripe/Linear hairline: present, never a line you
     notice. */
  --ds-hairline: color-mix(in srgb, var(--color-text) 8%, transparent);
  --ds-hairline-strong: color-mix(in srgb, var(--color-text) 14%, transparent);
  --ds-hairline-inset: color-mix(in srgb, var(--color-text) 5%, transparent);

  --ds-surface: var(--color-background);
  --ds-surface-raised: color-mix(in srgb, var(--color-background) 97%, var(--color-text));
  --ds-surface-sunken: color-mix(in srgb, var(--color-background) 94%, var(--color-text));
  --ds-surface-inverted: var(--color-secondary);

  /* Tints for state, not decoration: a hover that changes the surface by 4% is
     felt more than seen, which is the point. */
  --ds-tint-hover: color-mix(in srgb, var(--color-text) 4%, transparent);
  --ds-tint-active: color-mix(in srgb, var(--color-text) 7%, transparent);
  --ds-tint-primary: color-mix(in srgb, var(--color-primary) 8%, transparent);
  --ds-tint-primary-strong: color-mix(in srgb, var(--color-primary) 14%, transparent);

  /* ── Elevation ──────────────────────────────────────────────────────────
     Real shadows are two shadows: a tight contact shadow where the object meets
     the surface, and a wide diffuse one from the ambient light. A single blurred
     box-shadow is the thing that makes a card look like 2013. Alpha stays low
     and the colour is pulled from --color-text, so on a warm or dark palette the
     shadow tints with it instead of going muddy grey. */
  --ds-shadow-color: color-mix(in srgb, var(--color-text) 90%, black);
  --ds-shadow-xs:
    0 1px 2px -1px color-mix(in srgb, var(--ds-shadow-color) 8%, transparent);
  --ds-shadow-sm:
    0 1px 2px -1px color-mix(in srgb, var(--ds-shadow-color) 8%, transparent),
    0 2px 6px -2px color-mix(in srgb, var(--ds-shadow-color) 6%, transparent);
  --ds-shadow-md:
    0 1px 2px -1px color-mix(in srgb, var(--ds-shadow-color) 7%, transparent),
    0 4px 12px -3px color-mix(in srgb, var(--ds-shadow-color) 9%, transparent),
    0 12px 28px -8px color-mix(in srgb, var(--ds-shadow-color) 7%, transparent);
  --ds-shadow-lg:
    0 1px 2px -1px color-mix(in srgb, var(--ds-shadow-color) 6%, transparent),
    0 8px 20px -4px color-mix(in srgb, var(--ds-shadow-color) 10%, transparent),
    0 24px 48px -12px color-mix(in srgb, var(--ds-shadow-color) 12%, transparent);
  --ds-shadow-xl:
    0 2px 4px -2px color-mix(in srgb, var(--ds-shadow-color) 6%, transparent),
    0 16px 32px -8px color-mix(in srgb, var(--ds-shadow-color) 12%, transparent),
    0 40px 72px -16px color-mix(in srgb, var(--ds-shadow-color) 14%, transparent);
  /* A 1px inner top highlight — the thing that makes a surface read as lit from
     above rather than printed on. Composed *with* a shadow, never alone. */
  --ds-sheen: inset 0 1px 0 0 color-mix(in srgb, white 55%, transparent);

  /* ── Radii ──────────────────────────────────────────────────────────────
     --button-radius is admin-configurable and must keep winning on buttons; the
     rest is a fixed scale so a card, its image and its badge stay concentric. */
  --ds-radius-xs: 0.375rem;
  --ds-radius-sm: 0.5rem;
  --ds-radius-md: 0.75rem;
  --ds-radius-lg: 1rem;
  --ds-radius-xl: 1.25rem;
  --ds-radius-2xl: 1.75rem;
  --ds-radius-full: 9999px;

  /* ── Motion ─────────────────────────────────────────────────────────────
     One easing curve does most of the work: a fast start that settles long, so
     an element arrives before you finish looking at it. `ease-out` (the CSS
     keyword) is too lazy at the tail to feel expensive. */
  --ds-ease: cubic-bezier(0.16, 1, 0.3, 1);
  --ds-ease-in-out: cubic-bezier(0.65, 0, 0.35, 1);
  --ds-ease-spring: cubic-bezier(0.34, 1.56, 0.64, 1);

  --ds-duration-instant: 90ms;
  --ds-duration-fast: 160ms;
  --ds-duration-base: 240ms;
  --ds-duration-slow: 400ms;
  --ds-duration-slower: 640ms;

  /* ── Type ───────────────────────────────────────────────────────────────
     Fluid across the whole range, so there is no breakpoint where a heading
     visibly jumps. Tracking tightens as size grows — the optical correction that
     separates a set headline from a big one. */
  --ds-text-2xs: 0.6875rem;
  --ds-text-xs: 0.75rem;
  --ds-text-sm: 0.8125rem;
  --ds-text-base: 0.9375rem;
  --ds-text-md: 1rem;
  --ds-text-lg: clamp(1.0625rem, 1rem + 0.3vw, 1.1875rem);
  --ds-text-xl: clamp(1.25rem, 1.15rem + 0.5vw, 1.5rem);
  --ds-text-2xl: clamp(1.5rem, 1.3rem + 1vw, 2rem);
  --ds-text-3xl: clamp(1.875rem, 1.5rem + 1.6vw, 2.75rem);
  --ds-text-4xl: clamp(2.25rem, 1.6rem + 2.8vw, 3.75rem);

  --ds-tracking-wide: 0.08em;
  --ds-tracking-normal: 0em;
  --ds-tracking-tight: -0.011em;
  --ds-tracking-tighter: -0.022em;
  --ds-tracking-tightest: -0.032em;

  --ds-leading-none: 1;
  --ds-leading-tight: 1.15;
  --ds-leading-snug: 1.35;
  --ds-leading-normal: 1.55;
  --ds-leading-relaxed: 1.7;

  --ds-weight-normal: 400;
  --ds-weight-medium: 500;
  --ds-weight-semibold: 600;
  --ds-weight-bold: 700;

  /* ── Space ──────────────────────────────────────────────────────────────
     A 4px base, but the large end is fluid: generous white space is the single
     biggest difference between a premium layout and a cramped one, and it should
     grow with the viewport instead of staying at its mobile value on a 27" screen. */
  --ds-space-1: 0.25rem;
  --ds-space-2: 0.5rem;
  --ds-space-3: 0.75rem;
  --ds-space-4: 1rem;
  --ds-space-5: 1.25rem;
  --ds-space-6: 1.5rem;
  --ds-space-8: 2rem;
  --ds-space-10: 2.5rem;
  --ds-space-12: clamp(2.5rem, 2rem + 2vw, 3.5rem);
  --ds-space-16: clamp(3rem, 2.2rem + 3.4vw, 5rem);
  --ds-space-24: clamp(4rem, 2.8rem + 5vw, 7.5rem);

  /* ── Layout ─────────────────────────────────────────────────────────────── */
  --ds-container: 80rem;
  --ds-container-narrow: 46rem;
  --ds-gutter: clamp(1rem, 0.5rem + 2.2vw, 2.5rem);

  /* ── Gradients ──────────────────────────────────────────────────────────
     Barely there on purpose. A gradient you can name is a gradient that dates;
     these read as light falling on a surface, which does not. */
  --ds-gradient-surface: linear-gradient(
    180deg,
    color-mix(in srgb, var(--color-text) 1.5%, var(--color-background)) 0%,
    var(--color-background) 60%
  );
  --ds-gradient-primary: linear-gradient(
    135deg,
    var(--color-primary) 0%,
    color-mix(in srgb, var(--color-primary) 82%, var(--color-text)) 100%
  );
  --ds-gradient-halo: radial-gradient(
    60% 60% at 50% 0%,
    color-mix(in srgb, var(--color-primary) 10%, transparent) 0%,
    transparent 100%
  );
  --ds-gradient-hairline: linear-gradient(
    90deg,
    transparent 0%,
    var(--ds-hairline-strong) 18%,
    var(--ds-hairline-strong) 82%,
    transparent 100%
  );

  /* ── Focus ──────────────────────────────────────────────────────────────
     One ring, everywhere, drawn with box-shadow so it follows border-radius
     instead of boxing a rounded control in a rectangle. The white gap between
     control and ring keeps it legible on any surface. */
  --ds-focus-ring:
    0 0 0 2px var(--color-background),
    0 0 0 4px color-mix(in srgb, var(--color-primary) 65%, transparent);
  --ds-z-sticky: 20;
  --ds-z-overlay: 40;
  --ds-z-modal: 60;
}

/* ── Primitives ──────────────────────────────────────────────────────────── */

/* Inlined Heroicons (Frontend::IconsHelper). They carry width/height attributes
   for the no-CSS case; these rules make them behave like text: sized in `em`,
   coloured by `currentColor`, aligned to the baseline of the label beside them. */
.hi {
  display: inline-block;
  width: 1em;
  height: 1em;
  flex: none;
  vertical-align: -0.135em;
  color: currentColor;
  /* Heroicons' 1.5 stroke is tuned for 24px. Below ~20px it reads heavy, so
     thin it as the icon shrinks. */
  stroke-width: 1.5;
}

.hi--outline {
  fill: none;
}

.hi--solid,
.hi--micro {
  fill: currentColor;
  stroke: none;
}

.ds-focusable:focus-visible,
.ds-focus-ring:focus-visible {
  outline: none;
  box-shadow: var(--ds-focus-ring);
}

/* Focus that only shows for keyboards, but never *silently* — if :focus-visible
   is unsupported the plain :focus ring below is what the user gets. */
.ds-focusable:focus:not(:focus-visible) {
  box-shadow: none;
}

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

.ds-container--narrow {
  max-width: var(--ds-container-narrow);
}

/* A hairline that fades at both ends instead of butting into the gutter. */
.ds-rule {
  height: 1px;
  border: 0;
  background: var(--ds-gradient-hairline);
}

/* Tabular numerals for anything a customer compares down a column: prices,
   quantities, order totals. Proportional digits make a price column wobble. */
.ds-numeric {
  font-variant-numeric: tabular-nums;
  font-feature-settings: "tnum" 1;
}

/* Truncate to N lines without a JS measure pass. */
.ds-clamp-1,
.ds-clamp-2,
.ds-clamp-3 {
  display: -webkit-box;
  -webkit-box-orient: vertical;
  overflow: hidden;
}

.ds-clamp-1 { -webkit-line-clamp: 1; }
.ds-clamp-2 { -webkit-line-clamp: 2; }
.ds-clamp-3 { -webkit-line-clamp: 3; }

/* Visible to screen readers, not to the eye. */
.ds-sr-only {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

/* A skeleton that shimmers rather than blinks — the blink reads as a bug. */
.ds-skeleton {
  background:
    linear-gradient(
      90deg,
      var(--ds-surface-sunken) 25%,
      color-mix(in srgb, var(--color-text) 3%, var(--color-background)) 37%,
      var(--ds-surface-sunken) 63%
    )
    0 0 / 400% 100%;
  animation: ds-shimmer 1.4s var(--ds-ease-in-out) infinite;
  border-radius: var(--ds-radius-sm);
}

@keyframes ds-shimmer {
  to { background-position: -135% 0; }
}

/* ── Reduced motion ──────────────────────────────────────────────────────
   A blanket rule, so a component author cannot forget it. Durations collapse to
   ~0 rather than `animation: none`, which keeps animation *end* events firing —
   scroll-reveal and the mini-cart rely on them, and `none` would leave elements
   stuck at their initial opacity. */
@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    animation-duration: 0.01ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.01ms !important;
    scroll-behavior: auto !important;
  }
}

.frontend-main-menu {
  --frontend-menu-dark-background: var(--color-secondary);
  --frontend-menu-logo-height: 1.8rem;
  color: var(--frontend-menu-text);
  position: relative;
  transition: color 0.2s ease;
  z-index: 1000;
}

.frontend-main-menu__shell {
  background: var(--frontend-menu-shell-background);
  box-shadow: var(--frontend-menu-shell-shadow);
  border-bottom: 1px solid var(--frontend-menu-border);
  transition: background-color 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease, transform 0.2s ease;
}

.frontend-main-menu__topbar {
  background: var(--frontend-menu-shell-background);
  border-bottom: 1px solid var(--frontend-menu-border);
  transition: background-color 0.2s ease, border-color 0.2s ease;
}

.frontend-main-menu__announcement {
  background: var(--frontend-menu-shell-background);
  border-top: 1px solid var(--frontend-menu-border);
  border-bottom: 1px solid var(--frontend-menu-border);
  transition: background-color 0.2s ease, border-color 0.2s ease;
}

.frontend-main-menu__announcement-body {
  font-size: 0.875rem;
  line-height: 1.5;
}

.frontend-main-menu__announcement-body a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

.frontend-main-menu__topbar-inner {
  flex-wrap: wrap;
}

.frontend-main-menu__burger {
  color: var(--frontend-menu-text);
}

.frontend-main-menu__burger-icon {
  display: inline-flex;
  flex-direction: column;
  gap: 0.3rem;
  width: 1.45rem;
}

.frontend-main-menu__burger-line {
  background: currentColor;
  border-radius: 999px;
  display: block;
  height: 2px;
  transform-origin: center;
  transition: opacity 0.18s ease, transform 0.22s ease;
  width: 100%;
}

.frontend-main-menu__mobile-nav--open .frontend-main-menu__burger-line:first-child {
  transform: translateY(0.42rem) rotate(45deg);
}

.frontend-main-menu__mobile-nav--open .frontend-main-menu__burger-line:nth-child(2) {
  opacity: 0;
  transform: scaleX(0);
}

.frontend-main-menu__mobile-nav--open .frontend-main-menu__burger-line:last-child {
  transform: translateY(-0.42rem) rotate(-45deg);
}

.frontend-main-menu__mobile-backdrop {
  background: color-mix(in srgb, black 42%, transparent);
  bottom: 0;
  left: 0;
  opacity: 0;
  pointer-events: none;
  position: fixed;
  right: 0;
  top: 6.5rem;
  transition: opacity 0.22s ease, visibility 0.22s ease;
  visibility: hidden;
  z-index: 1000;
}

.frontend-main-menu__mobile-panel {
  background: var(--frontend-menu-mobile-background);
  box-shadow: 24px 0 48px color-mix(in srgb, black 16%, transparent);
  display: block;
  height: calc(100vh - 6.5rem);
  height: calc(100dvh - 6.5rem);
  left: 0;
  max-width: none;
  opacity: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  pointer-events: none;
  position: fixed;
  top: 6.5rem;
  transform: translateX(-100%);
  transition: opacity 0.22s ease, transform 0.22s ease, visibility 0.22s ease;
  visibility: hidden;
  width: 100vw;
  z-index: 1001;
  -webkit-overflow-scrolling: touch;
}

details.frontend-main-menu__mobile-nav:not([open]) .frontend-main-menu__mobile-backdrop,
details.frontend-main-menu__mobile-nav:not([open]) .frontend-main-menu__mobile-panel {
  display: block;
}

.frontend-main-menu__mobile-nav--open .frontend-main-menu__mobile-backdrop {
  opacity: 1;
  pointer-events: auto;
  visibility: visible;
}

.frontend-main-menu__mobile-nav--open .frontend-main-menu__mobile-panel {
  opacity: 1;
  pointer-events: auto;
  transform: translateX(0);
  visibility: visible;
}

.frontend-main-menu__mobile-parent {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
}

.frontend-main-menu__mobile-item {
  display: contents;
  interpolate-size: allow-keywords;
}

.frontend-main-menu__mobile-toggle {
  color: var(--frontend-menu-text);
}

.frontend-main-menu__mobile-submenu {
  grid-column: 1 / -1;
  max-height: 0;
  opacity: 0;
  overflow: hidden;
  transform: translateY(-0.35rem);
  transition: max-height 0.22s ease, opacity 0.18s ease, transform 0.18s ease;
}

.frontend-main-menu__mobile-item--open .frontend-main-menu__mobile-submenu {
  max-height: 32rem;
  opacity: 1;
  transform: translateY(0);
}

@media (min-width: 640px) {
  .frontend-main-menu__mobile-panel {
    max-width: min(24rem, 86vw);
    width: 100%;
  }
}

.frontend-main-menu__desktop-row {
  display: flex;
  align-items: center;
  gap: 1rem;
}

.frontend-main-menu__desktop-row--left {
  flex: 1 1 auto;
  justify-content: space-between;
}

.frontend-main-menu__desktop-row--center {
  position: relative;
  justify-content: center;
}

.frontend-main-menu__desktop-row--centered-nav {
  justify-content: space-between;
  position: relative;
}

.frontend-main-menu__desktop-row--centered-nav .frontend-main-menu__nav {
  /* Transform-free centering (left/right 0 + margin auto + fit-content): a
     translateX(-50%) here made the nav the CONTAINING BLOCK for every
     position:fixed descendant, so the mini-cart drawer opened as a 42px box
     trapped inside the header instead of a viewport overlay. */
  left: 0;
  right: 0;
  margin-inline: auto;
  position: absolute;
  width: fit-content;
}

.frontend-main-menu__desktop-row--centered-nav .frontend-main-menu__actions,
.frontend-main-menu__desktop-row--centered-nav > .frontend-main-menu__languages {
  margin-left: auto;
}

.frontend-main-menu__desktop-row--center .frontend-main-menu__actions,
.frontend-main-menu__desktop-row--center > .frontend-main-menu__languages {
  position: absolute;
  right: 0;
}

.frontend-main-menu__desktop-row--right {
  flex: 1 1 auto;
  justify-content: flex-end;
}

.frontend-main-menu__desktop-row--right-nav {
  flex: 1 1 auto;
  justify-content: flex-start;
}

.frontend-main-menu__desktop-row--right-nav .frontend-main-menu__nav {
  margin-left: auto;
}

.frontend-main-menu__list {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

.frontend-main-menu__list--center {
  justify-content: center;
}

.frontend-main-menu__list--right {
  justify-content: flex-end;
}

.frontend-main-menu__top-list {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
  justify-content: flex-end;
  list-style: none;
  margin: 0;
  padding: 0;
}

.frontend-main-menu__summary::-webkit-details-marker,
.frontend-main-menu__mobile-summary::-webkit-details-marker,
.frontend-main-menu__language-summary::-webkit-details-marker {
  display: none;
}

.frontend-main-menu__summary,
.frontend-main-menu__mobile-summary,
.frontend-main-menu__language-summary {
  list-style: none;
}

.frontend-main-menu__link,
.frontend-main-menu__top-link,
.frontend-main-menu__summary,
.frontend-main-menu__mobile-link,
.frontend-main-menu__language-summary {
  color: var(--frontend-menu-text);
}

.frontend-main-menu__link:visited,
.frontend-main-menu__top-link:visited,
.frontend-main-menu__top-dropdown-link:visited,
.frontend-main-menu__mobile-link:visited,
.frontend-main-menu__dropdown-link:visited,
.frontend-main-menu__language-option:visited {
  color: var(--frontend-menu-text);
}

.frontend-main-menu__dropdown-copy:visited {
  color: var(--frontend-menu-muted);
}

.frontend-main-menu__item-active {
  background: var(--frontend-menu-surface-hover);
}

.frontend-main-menu__item[open] .frontend-main-menu__summary,
.frontend-main-menu__mobile-item[open] .frontend-main-menu__mobile-summary,
.frontend-main-menu__language-picker[open] .frontend-main-menu__language-summary {
  background: var(--frontend-menu-surface-hover);
}

.frontend-main-menu__summary-icon,
.frontend-main-menu__top-icon,
.frontend-main-menu__mobile-icon,
.frontend-main-menu__language-icon {
  color: var(--frontend-menu-muted);
  transition: transform 0.2s ease;
}

.frontend-main-menu__item[open] .frontend-main-menu__summary .frontend-main-menu__summary-icon,
.frontend-main-menu__top-item:hover .frontend-main-menu__top-icon,
.frontend-main-menu__top-item:focus-within .frontend-main-menu__top-icon,
.frontend-main-menu__mobile-item--open .frontend-main-menu__mobile-toggle .frontend-main-menu__mobile-icon,
.frontend-main-menu__language-picker[open] .frontend-main-menu__language-summary .frontend-main-menu__language-icon,
.frontend-main-menu__mobile-nav--open .frontend-main-menu__mobile-summary .frontend-main-menu__mobile-icon {
  transform: rotate(180deg);
}

.frontend-main-menu__dropdown {
  background: var(--frontend-menu-surface);
  border: 1px solid var(--frontend-menu-border);
  box-shadow: 0 20px 48px color-mix(in srgb, black 14%, transparent);
  position: relative;
  z-index: 1001;
}

.frontend-main-menu__top-dropdown {
  background: var(--frontend-menu-surface);
  border: 1px solid var(--frontend-menu-border);
  border-radius: 1rem;
  box-shadow: 0 20px 48px color-mix(in srgb, black 14%, transparent);
  padding: 0.5rem;
  position: relative;
  z-index: 1001;
}

.frontend-main-menu__language-picker,
.frontend-main-menu__languages,
.frontend-main-menu__actions {
  position: relative;
  cursor: pointer;
}

.frontend-main-menu__language-dropdown {
  background: var(--frontend-menu-surface);
  border: 1px solid var(--frontend-menu-border);
  border-radius: 1rem;
  box-shadow: 0 20px 48px color-mix(in srgb, black 14%, transparent);
  min-width: 10rem;
  opacity: 0;
  padding: 0.5rem;
  pointer-events: none;
  position: absolute;
  right: 0;
  top: calc(100% + 0.75rem);
  transform: translateY(-0.35rem);
  transition: opacity 0.18s ease, transform 0.18s ease, visibility 0.18s ease;
  visibility: hidden;
  z-index: 1002;
}

.frontend-main-menu__language-picker--open .frontend-main-menu__language-dropdown {
  opacity: 1;
  pointer-events: auto;
  transform: translateY(0);
  visibility: visible;
}

.frontend-main-menu__dropdown-wrap {
  opacity: 0;
  pointer-events: none;
  transform: translateY(-0.25rem);
  transition: opacity 0.16s ease, transform 0.16s ease, visibility 0.16s ease;
  visibility: hidden;
  z-index: 1001;
}

.frontend-main-menu__top-dropdown-wrap {
  opacity: 0;
  pointer-events: none;
  transform: translateY(-0.25rem);
  transition: opacity 0.16s ease, transform 0.16s ease, visibility 0.16s ease;
  visibility: hidden;
  z-index: 1001;
}

.frontend-main-menu__item:hover .frontend-main-menu__dropdown-wrap,
.frontend-main-menu__item:focus-within .frontend-main-menu__dropdown-wrap {
  opacity: 1;
  pointer-events: auto;
  transform: translateY(0);
  visibility: visible;
}

.frontend-main-menu__top-item:hover .frontend-main-menu__top-dropdown-wrap,
.frontend-main-menu__top-item:focus-within .frontend-main-menu__top-dropdown-wrap {
  opacity: 1;
  pointer-events: auto;
  transform: translateY(0);
  visibility: visible;
}

.frontend-main-menu__item:hover .frontend-main-menu__summary-icon,
.frontend-main-menu__item:focus-within .frontend-main-menu__summary-icon {
  transform: rotate(180deg);
}

.frontend-main-menu__dropdown-link,
.frontend-main-menu__dropdown-title,
.frontend-main-menu__top-dropdown-link,
.frontend-main-menu__mobile-link,
.frontend-main-menu__language-option {
  color: var(--frontend-menu-text);
}

.frontend-main-menu__dropdown-copy {
  color: var(--frontend-menu-muted);
}

.frontend-main-menu__dropdown-link:hover,
.frontend-main-menu__dropdown-copy:hover,
.frontend-main-menu__top-dropdown-link:hover,
.frontend-main-menu__mobile-link:hover,
.frontend-main-menu__language-option:hover {
  background: var(--frontend-menu-surface-hover);
}

.frontend-main-menu__link,
.frontend-main-menu__top-link,
.frontend-main-menu__mobile-link,
.frontend-main-menu__language-summary,
.frontend-main-menu__mobile-toggle {
  transition: background-color 0.18s ease, color 0.18s ease;
}

.frontend-main-menu__link:hover,
.frontend-main-menu__link:focus-visible,
.frontend-main-menu__top-link:hover,
.frontend-main-menu__top-link:focus-visible,
.frontend-main-menu__mobile-link:hover,
.frontend-main-menu__mobile-link:focus-visible,
.frontend-main-menu__language-summary:hover,
.frontend-main-menu__language-summary:focus-visible,
.frontend-main-menu__mobile-toggle:hover,
.frontend-main-menu__mobile-toggle:focus-visible {
  background: var(--frontend-menu-surface-hover);
}

.frontend-main-menu__item:hover > .frontend-main-menu__link,
.frontend-main-menu__item:focus-within > .frontend-main-menu__link {
  background: var(--frontend-menu-surface-hover);
}

.frontend-main-menu__logo-image {
  max-height: var(--frontend-menu-logo-height);
  width: auto;
}

.frontend-main-menu__logo-fallback {
  color: var(--frontend-menu-text);
}

.frontend-main-menu__language-flag {
  border: 1px solid var(--frontend-menu-border);
}

.frontend-main-menu__flag-fallback {
  background: var(--frontend-menu-surface-hover);
  border: 1px solid var(--frontend-menu-border);
  color: var(--frontend-menu-text);
}

.frontend-main-menu--fixed,
.frontend-main-menu--transparent-fixed {
  position: fixed;
  inset-inline: 0;
  top: 0;
  z-index: 1000;
}

.frontend-main-menu--transparent {
  position: absolute;
  inset-inline: 0;
  top: 0;
  z-index: 1000;
}

.frontend-main-menu--transparent .frontend-main-menu__shell,
.frontend-main-menu--transparent .frontend-main-menu__announcement,
.frontend-main-menu--transparent .frontend-main-menu__topbar,
.frontend-main-menu--transparent-fixed .frontend-main-menu__shell,
.frontend-main-menu--transparent-fixed .frontend-main-menu__announcement,
.frontend-main-menu--transparent-fixed .frontend-main-menu__topbar {
  background: transparent;
  box-shadow: none;
  border-color: transparent;
}

.frontend-main-menu--transparent-fixed.frontend-main-menu--scrolled .frontend-main-menu__shell,
.frontend-main-menu--transparent-fixed.frontend-main-menu--scrolled .frontend-main-menu__announcement,
.frontend-main-menu--transparent-fixed.frontend-main-menu--scrolled .frontend-main-menu__topbar {
  background: var(--frontend-menu-shell-background);
  box-shadow: var(--frontend-menu-shell-shadow);
  border-color: var(--frontend-menu-border);
}

.frontend-main-menu--squish {
  position: fixed;
  inset-inline: 0;
  top: 0;
  z-index: 1000;
  padding-top: 1rem;
}

.frontend-main-menu--squish .frontend-main-menu__shell,
.frontend-main-menu--squish .frontend-main-menu__announcement,
.frontend-main-menu--squish .frontend-main-menu__topbar {
  margin-inline: auto;
  max-width: min(94rem, calc(100% - 2rem));
  border-radius: 1.5rem;
  backdrop-filter: blur(18px);
}

/* Account area — the signed-in customer panel (dashboard, orders, addresses,
 * profile, password).
 *
 * These pages moved their H1 out of the page_shell header and into the content
 * column, so the air the header used to provide has to come from the layout:
 * .account-layout puts it back, and page-shell__header--flush drops the header's
 * own bottom margin because the column stack (space-y-10) owns that rhythm now.
 *
 * Every value is a --ds-* token, colour only from --color-*, so a client palette
 * flows through untouched.
 */

.account-layout {
  padding-top: var(--ds-space-8);
}

@media (min-width: 768px) {
  .account-layout {
    padding-top: var(--ds-space-10);
  }
}

/* Title -> supporting line. A hair more air than the shell default, because in
   the column the line runs the full measure and needs the separation. */
.page-shell__header--flush .page-shell__heading {
  gap: var(--ds-space-3);
}

/* ── Navigation card ─────────────────────────────────────────────────────── */

/* A surface, not a bare list: hairline + soft shadow, matching the storefront
   cards. Sticky so the panel stays reachable while a long order list or a tall
   profile form scrolls; it only engages from lg up, where the sidebar sits
   beside the content instead of stacking above it.
   Not md: a tablet gives the panel the full width above the content (18rem of
   navigation beside a 44rem column left the order lines unreadable), and a
   full-width sticky bar would cover the page as it scrolls. */
.account-nav {
  border-radius: var(--ds-radius-lg);
  background-color: var(--color-surface, var(--color-background));
  border: 1px solid var(--ds-hairline);
  box-shadow: var(--ds-shadow-sm);
  padding: var(--ds-space-3);
}

@media (min-width: 1024px) {
  .account-nav {
    position: sticky;
    /* Clears the sticky site header if the theme has one. */
    top: var(--ds-space-6);
  }
}

/* ── Wishlist grid entry ─────────────────────────────────────────────────── */

/* A saved product is one product_card, which carries the entry's controls as its
   own action row. The wrapper exists so the card can be told to fill the grid
   cell: a row of cards with titles of different heights would otherwise end with
   their action rows at four different heights. */
.wishlist-entry {
  display: flex;
  flex-direction: column;
  height: 100%;
}

/* The card is the part that stretches. Without this the card is only as tall as
   its own text, so a two-line title pushed the action row of that column down
   and the grid read as a row of cards of four different heights. */
.wishlist-entry > .product-card {
  flex: 1;
}

/* No whole-card click target here. In the catalog the stretched title link is
   the point — the card IS the product. On this page the card carries three
   controls of its own, and an invisible link over the price and the stock line
   competes with them: a click that misses the trash button by a few pixels left
   the page instead of removing the row. The title is plain text here
   (title_link: false), so the two ways to the product page are the image and the
   "view details" button; this rule is the belt to that braces, in case a caller
   ever renders the card here with its link back on. */
.wishlist-entry .product-card__title a::after {
  content: none;
}

/* Remove, move to cart, view details. Rendered INSIDE the card, as its action
   row (product_card's `actions` slot). The two icon buttons keep their square
   footprint and the details button takes what is left, so the row is the same
   height in every column. */
.wishlist-entry__actions {
  display: flex;
  align-items: stretch;
  gap: var(--ds-space-2);
}

/* button_to renders a <form> around its button; it must not take part in the
   flex sizing itself. */
.wishlist-entry__actions form {
  display: contents;
}

.wishlist-entry__icon-button {
  display: inline-flex;
  flex: none;
  width: 2.5rem;
  min-height: 2.75rem;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--ds-hairline-strong);
  border-radius: var(--button-radius, var(--ds-radius-sm));
  background: var(--color-background);
  color: var(--color-text-muted);
  cursor: pointer;
  transition:
    background-color var(--ds-duration-fast) var(--ds-ease),
    border-color var(--ds-duration-fast) var(--ds-ease),
    color var(--ds-duration-fast) var(--ds-ease);
}

.wishlist-entry__icon-button:hover {
  border-color: var(--color-danger);
  background: color-mix(in srgb, var(--color-danger) 8%, transparent);
  color: var(--color-danger);
}

/* Moving to the cart is the positive action of the pair, so it hovers in the
   brand colour while removal hovers in the danger colour above. */
.wishlist-entry__icon-button--primary:hover {
  border-color: var(--color-primary);
  background: var(--ds-tint-primary);
  color: var(--color-primary);
}

.wishlist-entry__icon-button--disabled {
  cursor: not-allowed;
  opacity: 0.45;
}

.wishlist-entry__icon-button--disabled:hover {
  border-color: var(--ds-hairline-strong);
  background: var(--color-background);
  color: var(--color-text-muted);
}

/* Reuses the card's own call-to-action skin (product-card__cta). `min-width: 0`
   because the row lives in a grid column that can be narrower than the label:
   without it the flex item refuses to shrink and pushes the icons out of the
   card. */
.wishlist-entry__details {
  flex: 1;
  width: auto;
  min-width: 0;
  padding-inline: var(--ds-space-2);
  /* One line, clipped rather than spilled: the label is longer in some locales
     than the column is wide, and a wrapped or overflowing label would break the
     row's shared height. */
  overflow: hidden;
  white-space: nowrap;
  text-overflow: ellipsis;
}

/* Sort + filters. A row of labelled controls that wraps: in the account column
   three of them fit side by side on a desktop and stack on a phone, with no
   breakpoint of their own. */
.wishlist-toolbar {
  position: relative;
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  gap: var(--ds-space-3) var(--ds-space-4);
}

.wishlist-toolbar__field {
  display: flex;
  min-width: 12rem;
  flex: 1 1 12rem;
  flex-direction: column;
  gap: var(--ds-space-1);
}

/* The search field is the row's elastic cell: the three selects keep the width
   they need to read, and every pixel left over on the line goes here, so the
   input ends where the product grid below it ends instead of stopping short. */
.wishlist-toolbar__field--search {
  flex: 100 1 16rem;
}

/* The search box and, when there is something to clear, the reset icon. They are
   one cell rather than two row items: the toolbar wraps, and as separate items
   the icon dropped onto a line of its own as soon as the row ran short. */
.wishlist-toolbar__search-row {
  display: flex;
  align-items: center;
  gap: var(--ds-space-2);
}

.wishlist-toolbar__search {
  position: relative;
  min-width: 0;
  flex: 1 1 auto;
}

.wishlist-toolbar__label {
  color: var(--color-text-muted);
  font-size: var(--ds-text-sm);
}

/* One skin for both the native <select> (no JavaScript) and the trigger button
   searchable-select puts in its place, which is handed this same class through
   data-searchable-select-button-class-value. */
.wishlist-toolbar__control {
  width: 100%;
  min-height: 2.5rem;
  padding: var(--ds-space-2) var(--ds-space-3);
  border: 1px solid var(--ds-hairline);
  border-radius: var(--button-radius, var(--ds-radius-sm));
  background-color: var(--color-surface, var(--color-background));
  color: var(--color-text);
  font-size: var(--ds-text-sm);
}

.wishlist-toolbar__control--search {
  padding-inline-end: 2.75rem;
}

.wishlist-toolbar__control--search::-webkit-search-cancel-button,
.wishlist-toolbar__control--search::-webkit-search-decoration {
  appearance: none;
}

.wishlist-toolbar__control:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

.wishlist-toolbar__search-button {
  position: absolute;
  inset-block: 0;
  inset-inline-end: 0;
  display: inline-flex;
  width: 2.75rem;
  align-items: center;
  justify-content: center;
  border: 0;
  background: transparent;
  color: var(--color-text-muted);
  cursor: pointer;
  transition: color var(--ds-duration-fast) var(--ds-ease);
}

.wishlist-toolbar__search-button:hover,
.wishlist-toolbar__search-button:focus-visible {
  color: var(--color-primary);
}

.wishlist-toolbar__search-button:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: -4px;
}

/* "Clear all". A square the size of the controls, sitting beside the search box
   on the control line — not on a line of its own and not floated up onto the
   label line, where it read as something dropped on the toolbar rather than part
   of it. It is rendered only when there is something to clear, so on an
   unfiltered page the search field is the row's last thing and ends exactly
   where the product grid does. */
.wishlist-toolbar__reset {
  position: relative;
  display: inline-flex;
  width: 2.5rem;
  height: 2.5rem;
  flex: 0 0 auto;
  align-items: center;
  justify-content: center;
  border: 1px solid var(--ds-hairline);
  border-radius: var(--button-radius, var(--ds-radius-sm));
  background-color: var(--color-surface, var(--color-background));
  color: var(--color-text-muted);
  transition:
    border-color var(--ds-duration-fast) var(--ds-ease),
    color var(--ds-duration-fast) var(--ds-ease);
}

.wishlist-toolbar__reset:hover,
.wishlist-toolbar__reset:focus-visible {
  border-color: var(--color-primary);
  color: var(--color-primary);
}

.wishlist-toolbar__reset:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

/* The words the icon replaced, kept for hover and keyboard focus. It hangs above
   the button, over the label line, and is aligned to its right edge: the button
   is the last thing on the row, so a label growing to the left is the only one
   that cannot push the page sideways. `aria-hidden` on the span and an aria-label
   on the link keep it from being announced twice. */
.wishlist-toolbar__reset-label {
  position: absolute;
  bottom: calc(100% + var(--ds-space-1));
  inset-inline-end: 0;
  padding: var(--ds-space-1) var(--ds-space-2);
  border-radius: var(--ds-radius-sm);
  background-color: var(--color-text);
  color: var(--color-background);
  font-size: var(--ds-text-xs);
  line-height: 1.4;
  white-space: nowrap;
  opacity: 0;
  pointer-events: none;
  transition: opacity var(--ds-duration-fast) var(--ds-ease);
}

.wishlist-toolbar__reset:hover .wishlist-toolbar__reset-label,
.wishlist-toolbar__reset:focus-visible .wishlist-toolbar__reset-label {
  opacity: 1;
}

@media (prefers-reduced-motion: reduce) {
  .wishlist-toolbar__reset,
  .wishlist-toolbar__reset-label {
    transition: none;
  }
}

/* ──────────────────────────────────────────────────────────────────────────
   article_list — editorial cards for the Articoli section.

   Three layouts off one card: a grid (default), a stacked list where the image
   sits beside the text, and a highlight where the first card runs full width.
   Every rule is scoped with [data-module="article_list"] so nothing leaks onto
   other modules. Colours come from the design-system tokens only.
   ────────────────────────────────────────────────────────────────────────── */

[data-module="article_list"] {
  --al-radius: var(--ds-radius-lg, 1rem);
  --al-hairline: var(--ds-hairline, color-mix(in srgb, var(--color-text) 12%, transparent));
}

/* ── The card ─────────────────────────────────────────────────────────────── */

[data-module="article_list"] .article-card {
  height: 100%;
}

[data-module="article_list"] .article-card__link {
  display: flex;
  flex-direction: column;
  height: 100%;
  gap: 1rem;
  color: inherit;
  text-decoration: none;
}

[data-module="article_list"] .article-card__media {
  position: relative;
  overflow: hidden;
  border-radius: var(--al-radius);
  aspect-ratio: var(--article-card-aspect, 3 / 2);
  background: var(--color-surface);
}

[data-module="article_list"] .article-card__image {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* The image is the only thing that moves on hover: a card that lifts, tilts
     and darkens all at once reads as a button, not as an article. */
  transition: transform var(--ds-duration-base, 0.35s) var(--ds-ease, ease);
}

[data-module="article_list"] .article-card__link:hover .article-card__image {
  transform: scale(1.04);
}

@media (prefers-reduced-motion: reduce) {
  [data-module="article_list"] .article-card__image {
    transition: none;
  }
  [data-module="article_list"] .article-card__link:hover .article-card__image {
    transform: none;
  }
}

[data-module="article_list"] .article-card__body {
  display: flex;
  flex-direction: column;
  gap: 0.5rem;
}

[data-module="article_list"] .article-card__date {
  font-size: 0.8125rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}

[data-module="article_list"] .article-card__title {
  font-size: 1.25rem;
  font-weight: 600;
  line-height: 1.3;
  color: var(--color-text);
}

[data-module="article_list"] .article-card__excerpt {
  color: var(--color-text-muted);
  line-height: 1.6;
}

[data-module="article_list"] .article-card__cta {
  margin-top: auto;
  padding-top: 0.25rem;
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--color-primary);
}

[data-module="article_list"] .article-card__link:hover .article-card__cta {
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

/* ── Stacked list: image beside the text, one article per row ─────────────── */

[data-module="article_list"] .article-list__grid--stacked {
  display: flex;
  flex-direction: column;
  gap: 2rem;
}

[data-module="article_list"] .article-list__grid--stacked .article-card__link {
  flex-direction: row;
  align-items: flex-start;
  gap: 1.5rem;
}

[data-module="article_list"] .article-list__grid--stacked .article-card__media {
  flex: 0 0 clamp(8rem, 28%, 20rem);
}

@media (max-width: 640px) {
  [data-module="article_list"] .article-list__grid--stacked .article-card__link {
    flex-direction: column;
  }
  [data-module="article_list"] .article-list__grid--stacked .article-card__media {
    flex-basis: auto;
    width: 100%;
  }
}

/* ── The big treatment ────────────────────────────────────────────────────────
 * Two ways in: the `highlight` layout promotes its FIRST card, and any grid
 * promotes a card that turns out to be the ONLY one. The second is the reason
 * this is not just a highlight rule — a band that resolves to a single article
 * would otherwise render one narrow column in a three-column grid, which reads
 * as a mistake rather than as a feature. Pick three and you get three abreast;
 * pick one and it fills the band. */

@media (min-width: 640px) {
  [data-module="article_list"].article-list--highlight .article-card:first-child,
  [data-module="article_list"] .article-list__grid .article-card:only-child {
    grid-column: 1 / -1;
  }

  [data-module="article_list"].article-list--highlight .article-card:first-child .article-card__link,
  [data-module="article_list"] .article-list__grid .article-card:only-child .article-card__link {
    flex-direction: row;
    align-items: center;
    gap: 2rem;
  }

  [data-module="article_list"].article-list--highlight .article-card:first-child .article-card__media,
  [data-module="article_list"] .article-list__grid .article-card:only-child .article-card__media {
    flex: 0 0 55%;
  }

  [data-module="article_list"].article-list--highlight .article-card:first-child .article-card__title,
  [data-module="article_list"] .article-list__grid .article-card:only-child .article-card__title {
    font-size: 1.75rem;
  }
}

/* ── Pagination ───────────────────────────────────────────────────────────── */

[data-module="article_list"] .article-list__pagination {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
  margin-top: 3rem;
}

[data-module="article_list"] .article-list__page {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 2.5rem;
  height: 2.5rem;
  padding: 0 0.75rem;
  border: 1px solid var(--al-hairline);
  border-radius: var(--ds-radius-sm, 0.5rem);
  color: var(--color-text);
  text-decoration: none;
  transition: background-color var(--ds-duration-fast, 0.15s) var(--ds-ease, ease);
}

[data-module="article_list"] .article-list__page:hover {
  background: var(--color-surface);
}

[data-module="article_list"] .article-list__page--current {
  background: var(--color-primary);
  border-color: var(--color-primary);
  color: var(--color-on-primary);
  font-weight: 600;
}

/* ──────────────────────────────────────────────────────────────────────────
   The article page.

   A measured reading column rather than the full-width page shell: an article
   is prose, and prose set edge to edge is not read. The column width is the one
   thing this file really decides; everything else is spacing and tokens.
   ────────────────────────────────────────────────────────────────────────── */

.article-page {
  --article-measure: 44rem;
}

.article-page__container {
  max-width: var(--article-measure);
  margin-inline: auto;
  padding-inline: 1.5rem;
}

.article-page__header {
  padding-block: clamp(2rem, 5vw, 4rem) clamp(1.5rem, 3vw, 2.5rem);
}

.article-page__back {
  margin-bottom: 1.5rem;
}

.article-page__back-link {
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
  font-size: 0.875rem;
  font-weight: 600;
  color: var(--color-text-muted);
  text-decoration: none;
}

.article-page__back-link:hover {
  color: var(--color-primary);
}

.article-page__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  margin-bottom: 0.75rem;
  font-size: 0.8125rem;
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.article-page__badge {
  padding: 0.15rem 0.55rem;
  border-radius: 9999px;
  background: color-mix(in srgb, var(--color-primary) 12%, transparent);
  color: var(--color-primary);
  font-weight: 600;
  letter-spacing: 0.06em;
}

.article-page__title {
  font-size: clamp(2rem, 4.5vw, 3rem);
  line-height: 1.15;
  font-weight: 700;
  color: var(--color-text);
  text-wrap: balance;
}

.article-page__standfirst {
  margin-top: 1rem;
  font-size: 1.125rem;
  line-height: 1.6;
  color: var(--color-text-muted);
  text-wrap: pretty;
}

/* The cover runs wider than the text column — a photograph wants room that a
   paragraph does not — but stays inside the page container. */
.article-page__cover {
  max-width: 68rem;
  margin-inline: auto;
  padding-inline: 1.5rem;
  margin-bottom: clamp(2rem, 4vw, 3rem);
}

.article-page__cover-image {
  width: 100%;
  height: auto;
  border-radius: var(--ds-radius-lg, 1rem);
  object-fit: cover;
}

/* Modules keep their own full-bleed behaviour; only the text ones are pulled
   back to the reading measure, which is where the body copy lives. */
.article-page__body [data-module="text-images-base"] .container,
.article-page__body [data-module="text_images_base"] .container {
  max-width: var(--article-measure);
}

.article-page__body {
  padding-bottom: clamp(3rem, 6vw, 5rem);
}

.btn {
  align-items: center;
  border: 1px solid transparent;
  border-radius: var(--button-radius);
  cursor: pointer;
  display: inline-flex;
  font-weight: 500;
  gap: .5rem;
  justify-content: center;
  line-height: 1.2;
  padding: .75rem 1.25rem;
  text-decoration: none;
  transition: background-color .18s ease, border-color .18s ease, color .18s ease, transform .18s ease;
}

[data-module] .text-left {
  --module-buttons-justify: flex-start;
}

[data-module] .text-center {
  --module-buttons-justify: center;
}

[data-module] .text-right {
  --module-buttons-justify: flex-end;
}

[data-module-buttons="true"] {
  justify-content: var(--module-buttons-justify, flex-start);
}

.btn:hover {
  transform: translateY(-1px);
}

.btn:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 3px;
}

.btn-primary {
  background-color: var(--color-primary);
  color: var(--color-primary-contrast);
}

.btn-primary:hover {
  background-color: var(--color-primary-hover);
}

/* Ghost: transparent, inherits the surrounding text colour (adapts to dark
   heroes automatically). Used as the secondary button style. */
.btn-ghost {
  background-color: transparent;
  border-color: currentColor;
  color: inherit;
}

.btn-ghost:hover {
  background-color: color-mix(in srgb, currentColor 12%, transparent);
}

.btn-secondary {
  background-color: var(--color-secondary);
  color: var(--color-secondary-contrast);
}

.btn-secondary:hover {
  background-color: var(--color-secondary-hover);
}

.btn-success {
  background-color: var(--color-success);
  color: var(--color-success-contrast);
}

.btn-success:hover {
  background-color: var(--color-success-hover);
}

.btn-danger {
  background-color: var(--color-danger);
  color: var(--color-danger-contrast);
}

.btn-danger:hover {
  background-color: var(--color-danger-hover);
}

.btn-warning {
  background-color: var(--color-warning);
  color: var(--color-warning-contrast);
}

.btn-warning:hover {
  background-color: var(--color-warning-hover);
}

.btn-outline-primary {
  background-color: transparent;
  border-color: var(--color-primary);
  color: var(--color-primary);
}

.btn-outline-primary:hover {
  background-color: var(--color-primary);
  color: var(--color-primary-contrast);
}

.btn-outline-secondary {
  background-color: transparent;
  border-color: var(--color-secondary);
  color: var(--color-secondary);
}

.btn-outline-secondary:hover {
  background-color: var(--color-secondary);
  color: var(--color-secondary-contrast);
}

.btn-outline-success {
  background-color: transparent;
  border-color: var(--color-success);
  color: var(--color-success);
}

.btn-outline-success:hover {
  background-color: var(--color-success);
  color: var(--color-success-contrast);
}

.btn-outline-danger {
  background-color: transparent;
  border-color: var(--color-danger);
  color: var(--color-danger);
}

.btn-outline-danger:hover {
  background-color: var(--color-danger);
  color: var(--color-danger-contrast);
}

.btn-outline-warning {
  background-color: transparent;
  border-color: var(--color-warning);
  color: var(--color-warning);
}

.btn-outline-warning:hover {
  background-color: var(--color-warning);
  color: var(--color-warning-contrast);
}

.btn-light {
  background-color: #ffffff;
  border-color: #ffffff;
  color: #111827;
}

.btn-light:hover {
  background-color: rgba(255, 255, 255, 0.85);
  border-color: rgba(255, 255, 255, 0.85);
}

.btn-dark {
  background-color: #111827;
  border-color: #111827;
  color: #ffffff;
}

.btn-dark:hover {
  background-color: #1f2937;
  border-color: #1f2937;
}

.btn-outline-light {
  background-color: transparent;
  border-color: #ffffff;
  color: #ffffff;
}

.btn-outline-light:hover {
  background-color: #ffffff;
  color: #111827;
}

.btn-outline-dark {
  background-color: transparent;
  border-color: #111827;
  color: #111827;
}

.btn-outline-dark:hover {
  background-color: #111827;
  color: #ffffff;
}

.btn-link {
  background-color: transparent;
  border-color: transparent;
  color: var(--color-primary);
  padding-left: 0;
  padding-right: 0;
}

.btn-sm {
  font-size: .875rem;
  padding: .5rem .875rem;
}

.btn-lg {
  font-size: 1.125rem;
  padding: 1rem 1.5rem;
}

.text-primary-contrast {
  color: var(--color-primary-contrast);
}

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

@media (prefers-reduced-motion: reduce) {
  .btn {
    transition: none;
  }
  .btn:hover {
    transform: none;
  }
}

.blaze-slider {
  --slides-to-show: 1;
  --slide-gap: 20px;
  direction: ltr;
}

.blaze-container {
  position: relative;
}

.blaze-track-container {
  overflow: hidden;
}

.blaze-track {
  --slide-width: calc((100% - (var(--slides-to-show) - 1) * var(--slide-gap)) / var(--slides-to-show));
  box-sizing: border-box;
  display: flex;
  gap: var(--slide-gap);
  touch-action: pan-y;
  will-change: transform;
}

.blaze-track > * {
  box-sizing: border-box;
  flex-shrink: 0;
  width: var(--slide-width);
}

.carousel__arrow {
  position: absolute;
  top: 50%;
  z-index: 20;
  align-items: center;
  justify-content: center;
  width: 3rem;
  height: 3rem;
  color: white;
  opacity: 0;
  background-color: color-mix(in srgb, var(--color-text) 38%, transparent);
  border: 1px solid color-mix(in srgb, var(--color-background) 34%, transparent);
  box-shadow: 0 18px 42px rgba(15, 23, 42, 0.28);
  backdrop-filter: blur(14px);
  transform: translateY(-50%) scale(0.94);
  transition:
    opacity 0.22s ease,
    transform 0.22s ease,
    background-color 0.22s ease,
    border-color 0.22s ease;
}

.carousel__arrow.arrow-cards-slider {
  top: 40%;
}

.carousel__arrow:hover {
  color: white;
  background-color: color-mix(in srgb, var(--color-primary) 84%, transparent);
  border-color: color-mix(in srgb, var(--color-background) 46%, transparent);
  transform: translateY(-50%) scale(1);
}

.blaze-slider:hover .carousel__arrow,
.blaze-slider:focus-within .carousel__arrow {
  opacity: 1;
}

.carousel__arrow:focus-visible,
.carousel__dot:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 3px;
}

.carousel__arrow--prev {
  left: 1rem;
  transform: translate(-0.35rem, -50%) scale(0.94);
}

.carousel__arrow--next {
  right: 1rem;
  transform: translate(0.35rem, -50%) scale(0.94);
}

.blaze-slider:hover .carousel__arrow--prev,
.blaze-slider:focus-within .carousel__arrow--prev,
.carousel__arrow--prev:hover {
  transform: translate(0, -50%) scale(1);
}

.blaze-slider:hover .carousel__arrow--next,
.blaze-slider:focus-within .carousel__arrow--next,
.carousel__arrow--next:hover {
  transform: translate(0, -50%) scale(1);
}

.carousel__pagination,
.blaze-pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: 0.5rem;
}

.carousel__pagination {
  position: absolute;
  right: 1rem;
  bottom: 1rem;
  left: 1rem;
  z-index: 20;
}

.carousel__dot,
.blaze-pagination button {
  width: 0.7rem;
  height: 0.7rem;
  padding: 0;
  overflow: hidden;
  font-size: 0;
  color: transparent;
  cursor: pointer;
  background-color: color-mix(in srgb, var(--color-background) 58%, transparent);
  border: 1px solid color-mix(in srgb, var(--color-background) 76%, transparent);
  border-radius: 999px;
}

.carousel__dot.active,
.blaze-pagination button.active {
  width: 1.75rem;
  background-color: var(--color-primary);
  border-color: var(--color-primary);
}

.homepage-hero-carousel,
.homepage-hero-carousel .blaze-container,
.homepage-hero-carousel .blaze-track-container,
.homepage-hero-carousel .blaze-track,
.homepage-hero-carousel .blaze-track > *,
.standard-page-hero-carousel,
.standard-page-hero-carousel .blaze-container,
.standard-page-hero-carousel .blaze-track-container,
.standard-page-hero-carousel .blaze-track,
.standard-page-hero-carousel .blaze-track > * {
  height: 100%;
}

.homepage-hero-carousel__slide,
.standard-page-hero-carousel__slide {
  position: relative;
  flex: 0 0 var(--slide-width);
  width: var(--slide-width);
  min-width: 0;
  height: 100%;
}

/* ──────────────────────────────────────────────────────────────────────────
   category_grid — modern collection cards.

   Full-bleed image with a bottom scrim, the category name overlaid, an optional
   product-count pill, and a CTA that reveals on hover (the collection-card
   pattern used by Shopify / large storefronts). Every rule is scoped with
   [data-module="category_grid"] so it never leaks onto other modules. Colours
   come from the design-system tokens; the overlay ink/scrim are local vars so
   the caption stays legible over any photo in both light and dark themes.
   ────────────────────────────────────────────────────────────────────────── */

[data-module="category_grid"] {
  --cg-ink: #fff;
  /* Bottom scrim behind the caption: derived from the brand primary, darkened
     with black so the overlaid white title keeps enough contrast. Change
     --color-primary and this warms/cools with it automatically. */
  --cg-scrim: color-mix(in srgb, var(--color-primary) 60%, #000 40%);
  --cg-radius: var(--ds-radius-lg, 1rem);

  /* Card drop-shadow: tinted with the brand primary and given a negative spread
     so it stays a soft glow under the card. The negative spread pulls the blur
     in at the sides (thin edges) so it does not read as a hard band against the
     white page; only the bottom shows a gentle primary halo. */
  --cg-shadow: 0 8px 16px -10px color-mix(in srgb, var(--color-primary) 32%, transparent);
  --cg-shadow-hover: 0 16px 30px -12px color-mix(in srgb, var(--color-primary) 42%, transparent);
}

[data-module="category_grid"] .category-grid__grid {
  gap: clamp(0.75rem, calc(0.5rem + 1vw), 1.5rem);
}

/* ── Card ────────────────────────────────────────────────────────────────── */
[data-module="category_grid"] .category-card {
  position: relative;
  border-radius: var(--cg-radius);
  overflow: hidden;
  isolation: isolate;
  background: var(--ds-surface-sunken, #f2f2f2);
  box-shadow: var(--cg-shadow);
  transition:
    transform var(--ds-duration-base, 240ms) var(--ds-ease, ease),
    box-shadow var(--ds-duration-base, 240ms) var(--ds-ease, ease);
}

[data-module="category_grid"] .category-card:hover,
[data-module="category_grid"] .category-card:focus-within {
  transform: translateY(-6px);
  box-shadow: var(--cg-shadow-hover);
}

[data-module="category_grid"] .category-card__link {
  display: block;
  position: relative;
  text-decoration: none;
  color: var(--cg-ink);
}

/* Keyboard focus ring on the whole card (link fills it). */
[data-module="category_grid"] .category-card__link:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 3px;
  border-radius: var(--cg-radius);
}

/* ── Media ───────────────────────────────────────────────────────────────── */
[data-module="category_grid"] .category-card__media {
  position: relative;
  aspect-ratio: var(--category-card-aspect, 1 / 1);
  overflow: hidden;
}

[data-module="category_grid"] .category-card__image {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform var(--ds-duration-slow, 400ms) var(--ds-ease, ease);
}

[data-module="category_grid"] .category-card:hover .category-card__image {
  transform: scale(1.06);
}

[data-module="category_grid"] .category-card__image--placeholder {
  object-fit: contain;
  background: var(--ds-surface-sunken, #f2f2f2);
}

/* Bottom scrim: keeps the overlaid caption legible over any image, and darkens
   further on hover so the revealed CTA reads cleanly. */
[data-module="category_grid"] .category-card__scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(to top, var(--cg-scrim) 0%, transparent 55%);
  opacity: 0.9;
  transition: opacity var(--ds-duration-base, 240ms) var(--ds-ease, ease);
  pointer-events: none;
}

[data-module="category_grid"] .category-card:hover .category-card__scrim {
  opacity: 1;
}

/* ── Count pill (glassy, top-right) ──────────────────────────────────────── */
[data-module="category_grid"] .category-card__count {
  position: absolute;
  top: 0.75rem;
  right: 0.75rem;
  z-index: 2;
  padding: 0.25rem 0.625rem;
  font-size: var(--ds-text-xs, 0.75rem);
  font-weight: 600;
  line-height: 1;
  color: var(--cg-ink);
  background: color-mix(in srgb, #000 38%, transparent);
  border: 1px solid color-mix(in srgb, #fff 35%, transparent);
  border-radius: var(--ds-radius-full, 9999px);
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
}

/* ── Caption (name + CTA, bottom-left) ───────────────────────────────────── */
[data-module="category_grid"] .category-card__caption {
  position: absolute;
  inset-inline: 0;
  bottom: 0;
  z-index: 2;
  display: flex;
  flex-direction: column;
  gap: 0.35rem;
  padding: clamp(0.875rem, 0.5rem + 1.2vw, 1.25rem);
}

[data-module="category_grid"] .category-card__title {
  margin: 0;
  color: var(--cg-ink);
  font-size: var(--ds-text-lg, 1.125rem);
  font-weight: 700;
  line-height: 1.2;
  letter-spacing: -0.01em;
  text-shadow: 0 1px 3px color-mix(in srgb, #000 45%, transparent);
}

[data-module="category_grid"] .category-card__cta {
  display: inline-flex;
  align-items: center;
  gap: 0.35rem;
  font-size: var(--ds-text-sm, 0.8125rem);
  font-weight: 600;
  color: var(--cg-ink);
  opacity: 0;
  transform: translateY(6px);
  transition:
    opacity var(--ds-duration-base, 240ms) var(--ds-ease, ease),
    transform var(--ds-duration-base, 240ms) var(--ds-ease, ease);
}

[data-module="category_grid"] .category-card:hover .category-card__cta,
[data-module="category_grid"] .category-card:focus-within .category-card__cta {
  opacity: 1;
  transform: translateY(0);
}

[data-module="category_grid"] .category-card__cta-icon {
  width: 1rem;
  height: 1rem;
  transition: transform var(--ds-duration-fast, 160ms) var(--ds-ease, ease);
}

[data-module="category_grid"] .category-card:hover .category-card__cta-icon {
  transform: translateX(3px);
}

/* ── Motion / touch fallbacks ────────────────────────────────────────────── */
@media (hover: none) {
  /* No hover on touch: keep the CTA visible so the affordance is not lost. */
  [data-module="category_grid"] .category-card__cta {
    opacity: 1;
    transform: none;
  }
}

@media (prefers-reduced-motion: reduce) {
  [data-module="category_grid"] .category-card,
  [data-module="category_grid"] .category-card__image,
  [data-module="category_grid"] .category-card__cta,
  [data-module="category_grid"] .category-card__cta-icon,
  [data-module="category_grid"] .category-card__scrim {
    transition: none;
  }
  [data-module="category_grid"] .category-card:hover {
    transform: none;
  }
  [data-module="category_grid"] .category-card:hover .category-card__image {
    transform: none;
  }
}

/* ──────────────────────────────────────────────────────────────────────────
   Inline content embeds — `#product:ID#` in a module body.

   The card is a block interruption of the copy, so it needs its own vertical
   rhythm (fluid clamp(), calibrated at 690px / 1440px like the modules) and a
   width cap: a full-bleed card inside a text column reads as a broken layout.
   ────────────────────────────────────────────────────────────────────────── */

.content-embed {
  margin: clamp(1.5rem, calc(1.1rem + 1.6vw), 3rem) 0;
}

.content-embed--product {
  max-width: 22rem;
}

/* Inside a prose column the card must not inherit the typographic resets the
   surrounding copy uses. */
.content-embed--product .product-card__title {
  margin: 0;
}

/* standard_page - gap system*/

.default-page-stack--flush-first-gap > .main-standard-page {
  margin-top: calc(var(--mobile-gap) * -1);
}

@media (min-width: 768px) {
  .default-page-stack--flush-first-gap > .main-standard-page {
    margin-top: calc(var(--desktop-gap) * -1);
  }
}

@media (min-width: 768px) {
  .standard-page-breadcrumb--without-banner {
    margin-top: 3.3rem;
  }
}

@media (min-width: 1024px) {
  .main-standard-page--with-side-menu .main-standard-page__content > .text-images-base:first-child > div > .grid {
    align-items: flex-start;
  }
}

.standard-page-side-menu__link--active-root {
  box-shadow: inset 3px 0 0 var(--color-primary);
  font-weight: 700;
}

.main-standard-page__content--hidden-page-title > .text-images-base:first-child {
  margin-bottom: calc(var(--mobile-gap) * -1);
}

.main-standard-page__content--hidden-page-title > .text-images-base:first-child .body.prose > :first-child {
  margin-top: 0;
}

@media (min-width: 768px) {
  .main-standard-page__content--hidden-page-title > .text-images-base:first-child {
    margin-bottom: calc(var(--desktop-gap) * -1);
  }
}

.main-divider {
  width: 100%;
  height: clamp(3rem, 6vw, 7rem);
  overflow: hidden;
  line-height: 0;
  pointer-events: none;
}

.main-divider svg {
  display: block;
  width: 100%;
  height: 100%;
}

.main-divider path {
  fill: currentColor;
}

.main-divider--straight_line {
  height: 1px;
}

turbo-frame.gallery-page-frame[src]:not([complete]) {
  display: block;
  grid-column: 1 / -1;
  min-height: 4rem;
}

turbo-frame.gallery-page-frame[complete],
turbo-frame.gallery-page-frame:not([src]) {
  display: contents;
}

turbo-frame.masonry-loader-frame:not([src]),
turbo-frame.masonry-loader-frame[src]:not([complete]) {
  display: block;
  min-height: 4rem;
}

.masonry-grid:not(.masonry-grid-ready) {
  min-height: 16rem;
}

/* ─────────────────────────────────────────────────────────────────────────
   Hero module typography — fully fluid type + spacing system.

   Every gap uses clamp() on the same vw curve as the font sizes, so the
   proportions stay constant whether the hero is inside a 690px iframe
   (Lookbook preview) or a 1440px full-viewport page.

   Calibration reference points:
     narrow  (690px iframe):  title ~47px  → spacings look correct here
     desktop (1440px full):   title  68px  → spacings must scale to match
   ───────────────────────────────────────────────────────────────────────── */

/*
  Font sizes — delivered as CSS custom properties set inline on the <section>
  so they beat Tailwind @layer base without specificity hacks.

  Scale  Mobile → Desktop
  Title  36px  → 68px   (clamp 2.25rem → 4.25rem, slope 3.8vw)
  Sub    20px  → 30px
  Lead   17px  → 19px
*/

[data-module="hero"] .hero-eyebrow {
  margin: 0 0 clamp(0.75rem, calc(0.5rem + 0.7vw), 1.125rem) !important;
  color: var(--section-muted);
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}

[data-module="hero"] .hero-title {
  margin: 0;
  color: var(--section-text);
  font-weight: 800;
  letter-spacing: -0.03em;
  /* Mobile: slight breathing room for 2-line wraps. Desktop: tight display. */
  line-height: 1.1;
  text-wrap: balance;
  font-size: var(--hero-title-size) !important;
}

@media (min-width: 1024px) {
  [data-module="hero"] .hero-title { line-height: 1.0; }
}

[data-module="hero"] .hero-subtitle {
  /* ~24px at 690px · ~34px at 1440px */
  margin: clamp(1.25rem, calc(0.9rem + 1.35vw), 2.25rem) 0 0 !important;
  color: var(--section-text);
  font-weight: 400;
  line-height: 1.4;
  text-wrap: balance;
  font-size: var(--hero-subtitle-size) !important;
  opacity: 0.85;
}

[data-module="hero"] .hero-lead {
  /* ~18px at 690px · ~26px at 1440px */
  margin: clamp(1rem, calc(0.65rem + 1.1vw), 1.75rem) 0 0 !important;
  max-width: 42rem;
  color: var(--section-muted);
  font-size: var(--hero-lead-size) !important;
  line-height: 1.7;
}

[data-module="hero"] .hero-lead p { margin: 0; }
[data-module="hero"] .hero-lead p + p { margin-top: 0.75em; }

[data-module="hero"] .text-center .hero-lead { margin-left: auto; margin-right: auto; }

[data-module="hero"] .hero-actions {
  /* ~52px at 690px · ~76px at 1440px */
  margin-top: clamp(2.25rem, calc(1.875rem + 3.2vw), 5rem) !important;
  display: flex;
  flex-wrap: wrap;
  gap: 0.875rem;
}
[data-module="hero"] .text-center .hero-actions { justify-content: center; }
[data-module="hero"] .text-right  .hero-actions { justify-content: flex-end; }

/* Background Image Zoom (One time) */
@keyframes hero-zoom {
  from { transform: scale(1); }
  to { transform: scale(1.15); }
}

/* Text Zoom Effect */
@keyframes hero-text-zoom {
  from { transform: scale(0.9); opacity: 0; }
  to { transform: scale(1); opacity: 1; }
}

/* Text Fall Effect (Top to Bottom) */
@keyframes hero-text-fall {
  from { transform: translateY(-20px); opacity: 0; }
  to { transform: translateY(0); opacity: 1; }
}

/* Text Fade Effect (Step by Step) */
@keyframes hero-text-fade {
  from { opacity: 0; transform: translateY(5px); }
  to { opacity: 1; transform: translateY(0); }
}

.hero-anim-item {
  opacity: 0;
  animation-fill-mode: forwards;
  animation-duration: 1s;
  animation-timing-function: cubic-bezier(0.4, 0, 0.2, 1); /* Smoother standard ease for step-by-step */
}

.hero-anim--fade { animation-name: hero-text-fade; }
.hero-anim--zoom { animation-name: hero-text-zoom; }
.hero-anim--fall { animation-name: hero-text-fall; }

.hero-font-display { font-family: var(--font-display); }
.hero-font-body    { font-family: var(--font-body); }

.hero-slide--zoom { animation: hero-zoom 15s ease-in-out forwards; }

/* ──────────────────────────────────────────────────────────────────────────
   link_columns — closing band of link columns.

   Every rule is scoped with [data-module="link-columns"] so nothing leaks onto
   the site footer partial, which sits below this band and is not ours.
   Spacing is fluid clamp() calibrated at 690px / 1440px, the same method the
   hero and product_showcase modules use (docs/guides/MODULE_CSS_GUIDE.md §3),
   and every colour reads a token so a client palette recolours the band.
   ────────────────────────────────────────────────────────────────────────── */

[data-module="link-columns"] {
  padding-block: clamp(2.5rem, calc(1.6rem + 3.4vw), 5rem);
  border-top: 1px solid var(--ds-hairline, rgba(0, 0, 0, 0.08));
}

[data-module="link-columns"] .link-columns__header {
  margin-bottom: clamp(2rem, calc(1.4rem + 2.4vw), 3.5rem);
  text-align: center;
}

[data-module="link-columns"] .link-columns__title {
  font-size: var(--ds-text-2xl, 1.5rem);
  font-weight: var(--ds-weight-semibold, 600);
  letter-spacing: var(--ds-tracking-tight, -0.011em);
  color: var(--color-text);
}

[data-module="link-columns"] .link-columns__subtitle {
  margin-top: var(--ds-space-3, 0.75rem);
  font-size: var(--ds-text-lg, 1.0625rem);
  color: var(--color-text-muted);
}

[data-module="link-columns"] .link-columns__grid {
  gap: clamp(2rem, calc(1.4rem + 2vw), 3rem);
}

/* ── Brand cell ──────────────────────────────────────────────────────────── */

[data-module="link-columns"] .link-columns__brand-title {
  font-size: var(--ds-text-xl, 1.25rem);
  font-weight: var(--ds-weight-semibold, 600);
  letter-spacing: var(--ds-tracking-tight, -0.011em);
  color: var(--color-text);
}

[data-module="link-columns"] .link-columns__brand-subtitle {
  margin-top: var(--ds-space-3, 0.75rem);
  max-width: 32ch;
  font-size: var(--ds-text-md, 1rem);
  line-height: var(--ds-leading-relaxed, 1.7);
  color: var(--color-text-muted);
}

[data-module="link-columns"] .link-columns__actions {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ds-space-3, 0.75rem);
  margin-top: var(--ds-space-6, 1.5rem);
}

/* ── Link columns ────────────────────────────────────────────────────────── */

[data-module="link-columns"] .link-columns__column-title {
  font-size: var(--ds-text-xs, 0.75rem);
  font-weight: var(--ds-weight-semibold, 600);
  letter-spacing: var(--ds-tracking-wide, 0.08em);
  text-transform: uppercase;
  color: var(--color-text);
}

[data-module="link-columns"] .link-columns__links {
  margin-top: var(--ds-space-4, 1rem);
  font-size: var(--ds-text-base, 0.9375rem);
  color: var(--color-text-muted);
}

/* Tailwind preflight already strips the bullets; this only sets the rhythm. */
[data-module="link-columns"] .link-columns__links li + li,
[data-module="link-columns"] .link-columns__links p + p {
  margin-top: var(--ds-space-3, 0.75rem);
}

[data-module="link-columns"] .link-columns__links a {
  color: var(--color-text-muted);
  text-decoration: none;
  transition: color var(--ds-duration-fast, 160ms) var(--ds-ease, ease);
}

[data-module="link-columns"] .link-columns__links a:hover {
  color: var(--color-primary);
}

[data-module="link-columns"] .link-columns__links a:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 3px;
  border-radius: var(--ds-radius-xs, 0.375rem);
}

/* ── Fine print ──────────────────────────────────────────────────────────── */

[data-module="link-columns"] .link-columns__fine-print {
  margin-top: clamp(2rem, calc(1.4rem + 2.4vw), 3.5rem);
  padding-top: var(--ds-space-6, 1.5rem);
  border-top: 1px solid var(--ds-hairline, rgba(0, 0, 0, 0.08));
  font-size: var(--ds-text-sm, 0.8125rem);
  color: var(--color-text-muted);
}

[data-module="link-columns"] .link-columns__fine-print a {
  color: var(--color-text-muted);
  text-decoration: underline;
  text-underline-offset: 3px;
}

[data-module="link-columns"] .link-columns__fine-print a:hover {
  color: var(--color-primary);
}

/* ── Media banner ──────────────────────────────────────────────────────────
 * Full-bleed media band at the top of a product detail or a category listing
 * page (image or video, one item or a BlazeSlider of them). Above the crumbs and
 * outside page_shell, so it spans the viewport while everything below it keeps
 * the shell's container measure.
 *
 * The frame owns the height through an aspect-ratio rather than a fixed pixel
 * value: every slide then resolves to the same box, so the track never jumps
 * between a portrait and a landscape upload, and the band stays a band on a
 * phone instead of eating the first screen. */

.media-banner {
  position: relative;
  width: 100%;
}

.media-banner__frame {
  position: relative;
  width: 100%;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  background: var(--ds-surface-sunken);
}

@media (min-width: 768px) {
  .media-banner__frame {
    aspect-ratio: 24 / 7;
  }
}

/* optimize_image_tag puts our class on the <img>, not on its <picture> wrapper,
 * so the wrapper needs sizing of its own -- otherwise it collapses and the image
 * inside is measured against nothing. Sizing only: <picture> is not a replaced
 * element, so object-fit would do nothing here. */
.media-banner__frame .optimize-image {
  display: block;
  width: 100%;
  height: 100%;
}

/* The replaced elements, which is where object-fit actually applies. */
.media-banner__image,
.media-banner__video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center;
}

.media-banner__slide {
  position: relative;
  min-width: 0;
}

/* Arrows are 3rem wide, pinned 1rem from each edge, and they fade in ON HOVER --
 * straight over the copy panel, which starts at the overlay's own padding. Give
 * the sliding variant enough inline padding to clear them, from the breakpoint
 * where the arrows exist at all (they are hidden below md). */
@media (min-width: 768px) {
  .media-banner__slider .media-banner__overlay {
    padding-inline: var(--ds-space-16);
  }
}

/* ── Text overlay ───────────────────────────────────────────────────────────
 * The panel carries its own themed surface instead of a dark scrim over the
 * photo: a scrim built from --color-text inverts with the theme and turns pale
 * in dark mode, which is exactly when the words need the most help. A translucent
 * background surface keeps token colours on both sides of the contrast pair. */

.media-banner__overlay {
  position: absolute;
  inset: 0;
  /* Above the media, explicitly. optimize_image_tag's <img> carries
   * `position: relative; z-index: 1` (it has to sit over the LQIP backdrop its
   * <picture> paints), and the <picture> itself does not open a stacking
   * context -- so that 1 competes with this overlay at the frame's level and
   * wins on `auto`, hiding the copy behind the photo. */
  z-index: 2;
  /* Centred on both axes by default: the band is full-bleed page chrome and the
   * panel is the only thing in it, so anything else reads as misaligned rather
   * than as a choice. */
  display: flex;
  align-items: center;
  justify-content: center;
  padding: clamp(var(--ds-space-3), 3vw, var(--ds-space-8));
  pointer-events: none;
}

.media-banner__panel {
  /* Its own content centred too, so a two-line title and a shorter subtitle sit
   * on one axis instead of stacking left-aligned inside a centred box. */
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  max-width: min(32rem, 100%);
  padding: clamp(var(--ds-space-3), 2vw, var(--ds-space-6));
  color: var(--color-text);
  text-align: center;
  pointer-events: auto;
  background: color-mix(in srgb, var(--color-background) 82%, transparent);
  border: 1px solid var(--ds-hairline);
  border-radius: var(--ds-radius-lg);
  backdrop-filter: blur(10px);
}

.media-banner__title {
  font-size: var(--ds-text-2xl);
  font-weight: var(--ds-weight-bold);
  line-height: var(--ds-leading-tight);
  letter-spacing: var(--ds-tracking-tight);
}

.media-banner__subtitle {
  margin-top: var(--ds-space-2);
  font-size: var(--ds-text-md);
  font-weight: var(--ds-weight-medium);
  color: var(--color-text-muted);
}

.media-banner__body {
  margin-top: var(--ds-space-2);
  font-size: var(--ds-text-base);
  line-height: var(--ds-leading-normal);
  color: var(--color-text-muted);
}

.media-banner__cta {
  margin-top: var(--ds-space-4);
}

/* ── Slider chrome ──────────────────────────────────────────────────────────
 * Dots ride inside the band, bottom centre. The shared .carousel__pagination
 * class is deliberately not reused: it pins the row to the bottom of whatever
 * box it sits in with a full-width inset, which here would stretch the dots
 * across the banner instead of centring them under it. */

.media-banner__pagination {
  position: absolute;
  bottom: clamp(var(--ds-space-2), 2vw, var(--ds-space-4));
  left: 50%;
  z-index: 20;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--ds-space-2);
  transform: translateX(-50%);
}

.page-404__title {
  font-family: var(--font-display);
}

picture.optimize-image {
  --optimize-lqip-blur: 12px;
  --optimize-lqip-bleed: calc(var(--optimize-lqip-blur) * 2);

  display: block;
  overflow: hidden;
  position: relative;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  height: 100%;
  width: 100%;
}

picture.optimize-image::before {
  content: "";
  position: absolute;
  /* Bleed outward so blurred transparent edges are clipped outside the image. */
  inset: calc(var(--optimize-lqip-bleed) * -1);
  z-index: 0;
  background-image: var(--optimize-lqip);
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  filter: blur(var(--optimize-lqip-blur));
  opacity: 1;
  transition: opacity 0.35s ease-out;
}

picture.optimize-image.unblur::before {
  opacity: 0;
}

picture.optimize-image img {
  display: block;
  width: 100%;
  height: 100%;
  position: relative;
  z-index: 1;
}

picture.optimize-image.blurred img {
  opacity: 0;
  transition: opacity 0.35s ease-out;
}

/* The LCP image must not wait for the lazy Stimulus controller before it can
   paint. Its LQIP remains underneath until the real image pixels are ready. */
picture.optimize-image.optimize-image--priority img {
  opacity: 1;
  transition: none;
}

picture.optimize-image.unblur img {
  opacity: 1;
}

/* Page shell — the single page chrome shared by CMS pages and the storefront.
 *
 * Rendered by themes/default/views/shared/_page_shell.html.erb. It exists so a
 * controller-driven storefront page (catalog, cart, checkout, account) and a
 * CMS page node measure the same, break at the same gutters and put the same
 * amount of air between the breadcrumb bar, the H1 and the first block.
 *
 * Vertical rhythm is NOT declared here. The shell wrapper carries the theme's
 * page-stack class, so breadcrumb -> body is exactly --mobile-gap /
 * --desktop-gap, the same gap a CMS page leaves between its breadcrumb bar and
 * its first module. Change the theme's vertical spacing and both paths move
 * together, which is the whole point.
 *
 * Container: max-width and gutters mirror the CMS body (Tailwind `container
 * mx-auto px-6 lg:px-8`, which is also what _breadcrumb_bar uses), so the H1
 * and the trail above it share a left edge on every page.
 *
 * Type: the H1 mirrors the CMS page title (`text-3xl md:text-4xl font-bold
 * leading-tight`) to the pixel. It is written in plain CSS rather than utility
 * classes because the storefront views are not the only caller and the value
 * must not drift.
 */

.page-shell {
  width: 100%;
}

.page-shell__body {
  width: 100%;
  margin-inline: auto;
  /* Matches the CMS container gutters: px-6, lg:px-8. */
  padding-inline: 1.5rem;
  /* The page has to clear the footer; a CMS page gets that from its own last
     module, a controller page has nothing below it. Fluid, so it grows with the
     viewport instead of stranding a short page just above the fold. */
  padding-bottom: var(--ds-space-24);
  min-width: 0;
}

@media (min-width: 1024px) {
  .page-shell__body {
    padding-inline: 2rem;
  }
}

/* Measure. The chrome is identical in all three; only how wide the text and the
   controls are allowed to run changes, because a login form and a product grid
   do not want the same line length.

   --wide declares no max-width on purpose. The partial puts the Tailwind
   `container` utility on the same element, which is exactly what a CMS page
   body and the breadcrumb bar use, and which STEPS with the viewport
   (80rem at 1440px, 96rem at 1600px). Pinning it to a single value here is what
   made the storefront 1280px wide next to a 1536px CMS page on a wide monitor. */

.page-shell__body--narrow {
  max-width: calc(var(--ds-container-narrow) + 4rem);
}

.page-shell__body--form {
  max-width: 32rem;
}

/* ── Page header ─────────────────────────────────────────────────────────── */

.page-shell__header {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--ds-space-5);
  margin-bottom: var(--ds-space-10);
}

/* Header rendered inside a page column that already owns the vertical rhythm
   (the account pages stack it with space-y-10), so it drops its own margin. */
.page-shell__header--flush {
  margin-bottom: 0;
}

.page-shell__heading {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-2);
  min-width: 0;
}

/* Same computed type as a CMS page title: text-3xl (1.875rem), md:text-4xl
   (2.25rem), font-bold, leading-tight. */
.page-shell__title {
  font-size: 1.875rem;
  font-weight: var(--ds-weight-bold);
  line-height: 1.25;
  letter-spacing: var(--ds-tracking-normal);
  color: var(--color-text);
  text-wrap: balance;
  margin: 0;
}

@media (min-width: 768px) {
  .page-shell__title {
    font-size: 2.25rem;
  }
}

.page-shell__subtitle {
  font-size: var(--ds-text-base);
  color: var(--color-text-muted);
  margin: 0;
}

.page-shell__header-aside {
  flex: none;
}

/* ──────────────────────────────────────────────────────────────────────────
   product_showcase — landing layouts (spotlight / editorial).

   Every rule is scoped with [data-module="product_showcase"] so it cannot leak
   onto the catalogue grid, which reuses the same product-card classes.
   Spacing is fluid clamp() calibrated at 690px / 1440px, the same method the
   hero module uses (docs/guides/MODULE_CSS_GUIDE.md §3), so the blocks keep
   their proportions between the Lookbook iframe and a full desktop viewport.
   ────────────────────────────────────────────────────────────────────────── */

[data-module="product_showcase"] .product-feature {
  display: grid;
  grid-template-columns: 1fr;
  align-items: center;
  gap: clamp(1.5rem, calc(1rem + 2vw), 3.5rem);
}

@media (min-width: 1024px) {
  [data-module="product_showcase"] .product-feature {
    grid-template-columns: 1fr 1fr;
  }

  /* Zig-zag: the copy takes the first column and the image the second, without
     reordering the DOM (the image stays first for screen readers and for the
     LCP preload of the opening block). */
  [data-module="product_showcase"] .product-feature--reversed .product-feature__media {
    order: 2;
  }
}

/* The aspect ratio lives on the container (the editor's image_aspect_ratio, via
   the --product-card-aspect var set on the section), not on the <img>:
   optimize_image_tag wraps the image in picture.optimize-image, which is sized
   at 100% height by the shared stylesheet. */
[data-module="product_showcase"] .product-feature__media {
  position: relative;
  aspect-ratio: var(--product-card-aspect, 1 / 1);
  overflow: hidden;
  border-radius: var(--ds-radius-lg, 0.75rem);
  background: var(--ds-surface-sunken, #f2f2f2);
}

[data-module="product_showcase"] .product-feature__link {
  display: block;
  width: 100%;
  height: 100%;
}

[data-module="product_showcase"] .product-feature__image {
  width: 100%;
  height: 100%;
  object-fit: cover;
  transition: transform 0.4s ease;
}

[data-module="product_showcase"] .product-feature:hover .product-feature__image {
  transform: scale(1.03);
}

[data-module="product_showcase"] .product-feature__image--placeholder {
  display: grid;
  place-items: center;
  background: var(--ds-surface-sunken, #f2f2f2);
  color: var(--color-text-muted, #6b7280);
}

[data-module="product_showcase"] .product-feature__brand {
  font-size: 0.8125rem;
  letter-spacing: 0.08em;
  text-transform: uppercase;
  color: var(--color-text-muted, #6b7280);
}

[data-module="product_showcase"] .product-feature__title {
  /* ~28px at 690px · ~40px at 1440px */
  font-size: clamp(1.5rem, calc(1.1rem + 1.6vw), 2.5rem) !important;
  line-height: 1.15;
  font-weight: 600;
  margin: clamp(0.5rem, calc(0.35rem + 0.5vw), 1rem) 0 0 !important;
}

[data-module="product_showcase"] .product-feature__description {
  color: var(--color-text-muted, #6b7280);
  line-height: 1.6;
  margin: clamp(0.75rem, calc(0.55rem + 0.7vw), 1.25rem) 0 0 !important;
}

[data-module="product_showcase"] .product-feature__price {
  font-size: clamp(1.125rem, calc(1rem + 0.5vw), 1.5rem);
  font-weight: 600;
  margin: clamp(1rem, calc(0.75rem + 0.9vw), 1.75rem) 0 0 !important;
}

[data-module="product_showcase"] .product-feature__stock {
  font-size: 0.875rem;
  color: var(--color-text-muted, #6b7280);
  margin: 0.5rem 0 0 !important;
}

/* Spotlight with the full buy form: the add_to_cart block replaces the CTA
   link and keeps its rhythm. */
[data-module="product_showcase"] .product-feature__buy {
  margin-top: clamp(1.25rem, calc(1rem + 1.2vw), 2.5rem);
}

[data-module="product_showcase"] .product-feature__cta {
  margin-top: clamp(1.25rem, calc(1rem + 1.2vw), 2.5rem) !important;
  display: inline-flex;
  width: auto;
}

/* Spotlight: the secondary card grid sits under the featured block. */
[data-module="product_showcase"] .product-showcase__grid--secondary {
  margin-top: clamp(2rem, calc(1.5rem + 2.4vw), 4.5rem);
}

/* Editorial: one full-width block per product, separated by the same fluid
   rhythm the spotlight uses between its two halves. */
[data-module="product_showcase"] .product-showcase__editorial {
  display: grid;
  gap: clamp(2.5rem, calc(1.75rem + 3.2vw), 6rem);
}

@media (prefers-reduced-motion: reduce) {
  [data-module="product_showcase"] .product-feature__image,
  [data-module="product_showcase"] .product-feature:hover .product-feature__image {
    transition: none;
    transform: none;
  }
}

/*
 * Scroll Reveal — animation library for the scroll-reveal Stimulus controller.
 *
 * All animation variants are defined here. The controller applies one class
 * (e.g. "reveal-fade-up") to the <section> and adds "in" when it enters
 * the viewport. To add a new variant, define its hidden state and let the
 * shared .in rule handle the revealed state.
 */

/* ── Shared base state (opacity + transition for all variants) ─────────── */
.reveal-fade-up,
.reveal-fade-down,
.reveal-fade-in,
.reveal-slide-left,
.reveal-slide-right,
.reveal-zoom-in {
  opacity: 0;
  transition:
    opacity 0.65s cubic-bezier(0.4, 0, 0.2, 1),
    transform 0.65s cubic-bezier(0.4, 0, 0.2, 1);
}

/* ── Revealed state — same for all variants ────────────────────────────── */
.reveal-fade-up.in,
.reveal-fade-down.in,
.reveal-fade-in.in,
.reveal-slide-left.in,
.reveal-slide-right.in,
.reveal-zoom-in.in {
  opacity: 1;
  transform: none;
}

/* ── Individual hidden transforms ──────────────────────────────────────── */

/* Slides up from below */
.reveal-fade-up {
  transform: translateY(32px);
}

/* Slides down from above */
.reveal-fade-down {
  transform: translateY(-32px);
}

/* Opacity only — no movement */
.reveal-fade-in {
  transform: none;
}

/* Slides in from the left */
.reveal-slide-left {
  transform: translateX(-48px);
}

/* Slides in from the right */
.reveal-slide-right {
  transform: translateX(48px);
}

/* Scales up from slightly smaller */
.reveal-zoom-in {
  transform: scale(0.94);
}

/* ── Respect prefers-reduced-motion ────────────────────────────────────── */
@media (prefers-reduced-motion: reduce) {
  .reveal-fade-up,
  .reveal-fade-down,
  .reveal-fade-in,
  .reveal-slide-left,
  .reveal-slide-right,
  .reveal-zoom-in {
    opacity: 1;
    transform: none;
    transition: none;
  }
}

/* Skip link — "skip to content", the first tab stop on every page.
 *
 * Rendered by themes/default/views/layouts/_skip_link.html.erb.
 *
 * It must stay FOCUSABLE while invisible, so it is moved off-screen with a
 * clip-rect (the standard visually-hidden recipe) instead of display:none or
 * visibility:hidden, both of which remove it from the tab order entirely and
 * would make the link unreachable.
 *
 * On :focus-visible it becomes a real, opaque button pinned to the top-left,
 * above every sticky header (z-index above the main menu's own stacking).
 *
 * Colours come from the theme custom properties, so a client theme restyles it
 * for free.
 */

.skip-link {
  position: absolute;
  width: 1px;
  height: 1px;
  margin: -1px;
  padding: 0;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
  border: 0;
}

.skip-link:focus,
.skip-link:focus-visible {
  position: fixed;
  top: 0.75rem;
  left: 0.75rem;
  z-index: 9999;
  width: auto;
  height: auto;
  margin: 0;
  padding: 0.75rem 1.25rem;
  overflow: visible;
  clip: auto;
  clip-path: none;
  border-radius: 0.5rem;
  background: var(--color-primary, #1f2937);
  color: var(--color-on-primary, #ffffff);
  font-size: 1rem;
  font-weight: 600;
  line-height: 1.2;
  text-decoration: none;
  outline: 3px solid var(--color-text, #111827);
  outline-offset: 2px;
}

/* The main landmark is a programmatic focus target (tabindex="-1"); never draw
 * a focus ring around the whole page when the skip link sends focus there. */
#main-content:focus {
  outline: none;
}

/* ── Sticky buy bar ──────────────────────────────────────────────────────────
 * Fixed bottom bar the sticky-buy-bar controller slides in once the main buy
 * area has scrolled out of view. Hidden by default via transform + visibility
 * (visibility keeps the off-screen bar out of the focus order and the
 * accessibility tree, and still transitions cleanly). Sits UNDER the mini-cart
 * drawer (z-index 60): a successful add opens the drawer over the bar. */

.sticky-buy-bar {
  position: fixed;
  inset-inline: 0;
  bottom: 0;
  z-index: 40;
  background: var(--color-background);
  border-top: 1px solid var(--ds-hairline-strong);
  box-shadow: var(--ds-shadow-lg);
  /* Safe-area aware: on notched phones the bar padding grows past the home
   * indicator instead of hiding the CTA behind it. */
  padding: var(--ds-space-3)
           clamp(var(--ds-space-3), calc(0.5rem + 1.5vw), var(--ds-space-6))
           calc(var(--ds-space-3) + env(safe-area-inset-bottom, 0px));
  transform: translateY(110%);
  visibility: hidden;
  transition:
    transform var(--ds-duration-base) var(--ds-ease),
    visibility var(--ds-duration-base) var(--ds-ease);
}

.sticky-buy-bar--visible {
  transform: none;
  visibility: visible;
}

@media (prefers-reduced-motion: reduce) {
  .sticky-buy-bar {
    transition: none;
  }
}

.sticky-buy-bar__inner {
  display: flex;
  align-items: center;
  gap: clamp(var(--ds-space-3), calc(0.5rem + 1vw), var(--ds-space-5));
  max-width: 72rem;
  margin-inline: auto;
}

.sticky-buy-bar__media {
  flex-shrink: 0;
}

.sticky-buy-bar__image,
.sticky-buy-bar__media .optimize-image {
  display: block;
  width: 3rem;
  height: 3rem;
}

.sticky-buy-bar__image {
  object-fit: cover;
  border-radius: var(--ds-radius-sm);
  border: 1px solid var(--ds-hairline);
}

.sticky-buy-bar__image--placeholder {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  background: var(--ds-tint-primary);
  color: var(--color-text-muted);
}

/* The very narrow screens drop the thumb: title + price + CTA is the payload. */
@media (max-width: 22rem) {
  .sticky-buy-bar__media {
    display: none;
  }
}

.sticky-buy-bar__info {
  flex: 1;
  min-width: 0;
}

.sticky-buy-bar__title {
  font-size: var(--ds-text-sm);
  font-weight: var(--ds-weight-semibold);
  color: var(--color-text);
  white-space: nowrap;
  overflow: hidden;
  text-overflow: ellipsis;
}

.sticky-buy-bar__price .price {
  display: inline-flex;
  align-items: baseline;
  gap: var(--ds-space-2);
  font-size: var(--ds-text-sm);
}

/* The bar CTA reuses .add-to-cart__submit--primary so it IS the store's primary
 * button; only the geometry shrinks to bar scale (the form's own buttons are
 * full-width). */
.sticky-buy-bar__cta.add-to-cart__submit {
  width: auto;
  flex-shrink: 0;
  min-height: 2.75rem;
  padding-inline: clamp(var(--ds-space-4), calc(0.75rem + 1vw), var(--ds-space-6));
}

/* Storefront components — catalog and product detail.
 *
 * Built on the tokens in _design_system.css: every radius, duration, shadow and
 * hairline here is a --ds-* var, never a literal. Colour comes only from the
 * theme's --color-* vars, so a client palette flows through untouched.
 *
 * What changed from the first pass, and why:
 *  - Borders were `1px solid var(--color-secondary)`. The default secondary is
 *    #0f172a — near black. Every card, input and stepper was outlined in a hard
 *    dark line, which is the single loudest reason the storefront read as a
 *    wireframe. They are hairlines now, derived from the text colour.
 *  - Cards were a border with content inside. They are surfaces now: hairline +
 *    a two-layer shadow that deepens on hover, with the lift and the image scale
 *    on different durations so the card arrives before the photo settles.
 *  - Type was three sizes and one weight. It is a scale now, with tracking that
 *    tightens as size grows.
 *
 * House rules, unchanged:
 *  - Touch targets >= 44px, including the quantity steppers.
 *  - Focus is always visible, drawn with --ds-focus-ring so it follows the
 *    corner radius instead of boxing a rounded control in a rectangle.
 *  - Animate transform/opacity/shadow only. Never width, height, or top.
 *  - Body text >= 4.5:1. Muted text is for secondary information, never prose.
 *  - Prices are tabular-nums, so a column of them does not jitter.
 */

/* ── Product listing ─────────────────────────────────────────────────────── */

/* Type scale for the whole listing surface — sidebar, grid and card. It had
   drifted to eight sizes, several of them a single pixel apart (--ds-text-base
   0.9375 beside --ds-text-md 1rem, --ds-text-2xs 0.6875 beside --ds-text-xs
   0.75), which reads as sloppiness rather than hierarchy. Four steps now, and
   nothing on this page may introduce a fifth:
     --ds-text-xs (0.75)   micro: eyebrow brand, facet group labels, facet
                           counts, stock line, badges
     --ds-text-sm (0.8125) interface text: facet rows, clear-all, result count,
                           card CTA, pagination, struck price
     --ds-text-md (1rem)   titles and inputs: card title, "Filters", every form
                           control (also the iOS no-zoom floor)
     --ds-text-xl          the price, the one figure meant to carry
   The H1 stays with the shared page shell, which owns it for every page. */

/* The catalog no longer declares a container, a header layout or a title type
   of its own: it renders inside the shared page shell (page_shell.css), which
   owns the width, the gutters, the breadcrumb -> title rhythm and the H1 scale
   for every page, CMS or storefront. What is left here is genuinely catalog
   specific: the sort control and the sidebar/results grid. */

/* The sort control: a native <select> that does not look native. The chevron is
   ours (background-image), the platform arrow is removed, and the padding-right
   reserves room for it. */
.product-listing__sort {
  display: flex;
  align-items: center;
  gap: var(--ds-space-3);
  flex: none;
}

.product-listing__sort-label {
  font-size: var(--ds-text-sm);
  font-weight: var(--ds-weight-medium);
  color: var(--color-text-muted);
  white-space: nowrap;
}

.product-listing__sort select {
  appearance: none;
  min-height: 2.75rem;
  padding: 0 2.5rem 0 var(--ds-space-4);
  border: 1px solid var(--ds-hairline-strong);
  border-radius: var(--ds-radius-sm);
  background-color: var(--color-background);
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none' stroke='%2364748b' stroke-width='1.5' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m4 6 4 4 4-4'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right var(--ds-space-3) center;
  background-size: 1rem 1rem;
  color: var(--color-text);
  font-size: var(--ds-text-md);
  font-weight: var(--ds-weight-medium);
  font-family: inherit;
  cursor: pointer;
  box-shadow: var(--ds-shadow-xs);
  transition:
    border-color var(--ds-duration-fast) var(--ds-ease),
    box-shadow var(--ds-duration-fast) var(--ds-ease);
}

.product-listing__sort select:hover {
  border-color: color-mix(in srgb, var(--color-primary) 40%, var(--ds-hairline-strong));
}

.product-listing__layout {
  display: grid;
  gap: var(--ds-space-10);
  align-items: start;
}

/* The sidebar column is wider than a plain list of dropdowns needed: it now
   carries the whole category tree and every brand as visible rows, and a facet
   name that wraps onto three lines reads as noise. The results column keeps the
   remaining width, so the grid still reflows to its own auto-fill floor. */
@media (min-width: 1024px) {
  .product-listing__layout {
    grid-template-columns: 20rem minmax(0, 1fr);
    gap: var(--ds-space-10);
  }
}

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

.product-listing__results {
  min-width: 0;
}

/* Below the sidebar breakpoint the two columns stack, and the sidebar comes
   first in the DOM — so the whole filter panel filled the first screen and the
   products a customer came for started below the fold. Visual order only: the
   source order still puts filters before results, which is what a screen reader
   and the tab sequence should follow. */
@media (max-width: 1023px) {
  .product-listing__results {
    order: -1;
  }
}

/* ── Filter sidebar ──────────────────────────────────────────────────────── */

/* Deliberately NOT sticky. The panel lists every category and every brand in
   full — no inner scroll area — so on a real catalogue it is taller than the
   viewport, and pinning it would park the bottom facets permanently off-screen.
   It scrolls with the page instead. */
.filter-sidebar {
  min-width: 0;
}

/* Vertical rhythm is owned by the groups, not by a flex `gap` on the form. The
   groups are a mix of <div> and <fieldset>, and a rendered <legend> is NOT a
   flex item — it sits outside the fieldset's anonymous content box — so a gap
   reached the label in the div groups and not in the fieldset ones, leaving
   CATEGORIA sitting further from its first option than CERCA from its input.
   With gap: 0 every distance below is stated once, in margins that every
   element honours. */
.filter-sidebar__form {
  display: flex;
  flex-direction: column;
  gap: 0;
  padding: var(--ds-space-5);
  border: 1px solid var(--ds-hairline);
  border-radius: var(--ds-radius-lg);
  background: var(--ds-gradient-surface);
  box-shadow: var(--ds-shadow-xs);
}

/* Panel header: what this column is, and the one control that empties it. */
.filter-sidebar__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--ds-space-3);
  /* Same distance either side of the rule as the group dividers below. */
  padding-bottom: var(--ds-space-4);
  margin-bottom: var(--ds-space-4);
  border-bottom: 1px solid var(--ds-hairline);
}

.filter-sidebar__title {
  margin: 0;
  font-size: var(--ds-text-md);
  font-weight: var(--ds-weight-semibold);
  letter-spacing: var(--ds-tracking-tight);
  color: var(--color-text);
}

.filter-sidebar__clear {
  border: 0;
  padding: 0;
  background: none;
  color: var(--color-primary);
  font-size: var(--ds-text-sm);
  font-weight: var(--ds-weight-medium);
  font-family: inherit;
  cursor: pointer;
  border-radius: var(--ds-radius-xs);
  transition: opacity var(--ds-duration-fast) var(--ds-ease);
}

.filter-sidebar__clear:hover {
  opacity: 0.7;
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

/* display: block, not flex — a <legend> cannot be a flex item, so any layout
   that depends on the group being a flex container splits the two kinds of
   group apart again. */
.filter-sidebar__group {
  display: block;
  min-width: 0;
  border: 0;
  padding: 0;
  margin: 0;
}

/* A hairline between facet blocks: with lists instead of four dropdowns the
   panel is long, and space alone stops telling one facet from the next. Equal
   margin above and padding below, so the rule sits centred in the gap. */
.filter-sidebar__group + .filter-sidebar__group {
  margin-top: var(--ds-space-4);
  padding-top: var(--ds-space-4);
  border-top: 1px solid var(--ds-hairline);
}

/* Both bounds full width, stacked: a half-width number input truncates its own
   value the moment a price runs past four digits. */
.filter-sidebar__group--price .filter-sidebar__field + .filter-sidebar__field {
  margin-top: var(--ds-space-2);
}

/* A measurement range reads as one control, so its two bounds sit side by side
   — unlike the price, whose values are long enough to need the full width.
   Millimetres are three digits. [product_typologies] */
.filter-sidebar__range {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: var(--ds-space-2);
}

/* Custom-field facet groups [product_typologies]. Class names shared with the
   full-list facet rows (_filter_option) so the two stylings converge on merge. */
.filter-sidebar__options {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-1);
  list-style: none;
  margin: 0;
  padding: 0;
  max-height: 14rem;
  overflow-y: auto;
}

.filter-sidebar__option {
  display: flex;
  align-items: center;
  gap: var(--ds-space-2);
  font-size: var(--ds-text-sm);
  color: var(--color-text);
  cursor: pointer;
}

.filter-sidebar__checkbox {
  accent-color: var(--color-primary);
}

.filter-sidebar__option-name {
  min-width: 0;
}

.filter-sidebar__count {
  margin-left: auto;
  color: var(--color-text-muted);
  font-size: var(--ds-text-xs);
}

/* Print picker + the artwork laid over the product photo [printed_products] */
.print-picker {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-2);
  margin-block: var(--ds-space-4);
}

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

.print-picker__options {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ds-space-2);
}

.print-picker__option input {
  position: absolute;
  opacity: 0;
  pointer-events: none;
}

.print-picker__chip {
  display: flex;
  align-items: center;
  gap: var(--ds-space-2);
  padding: var(--ds-space-1) var(--ds-space-2);
  border: 1px solid var(--color-secondary);
  border-radius: var(--ds-radius-md);
  cursor: pointer;
  font-size: var(--ds-text-sm);
  background: var(--color-background);
}

.print-picker__option input:checked + .print-picker__chip {
  border-color: var(--color-primary);
  box-shadow: inset 0 0 0 1px var(--color-primary);
}

.print-picker__option input:focus-visible + .print-picker__chip {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

.print-picker__thumb {
  width: 2rem;
  height: 2rem;
  object-fit: contain;
  display: grid;
  place-items: center;
}

.print-picker__hint {
  margin: 0;
  font-size: var(--ds-text-xs);
  color: var(--color-text-muted);
}

/* Square artwork over a square-ish photo: position and scale are a share of the
   photo, so the preview holds at every breakpoint without a canvas. */
.print-overlay {
  position: absolute;
  top: 30%;
  left: 50%;
  width: 34%;
  transform: translateX(-50%);
  pointer-events: none;
}

.filter-sidebar__more {
  margin: var(--ds-space-1) 0 0;
  color: var(--color-text-muted);
  font-size: var(--ds-text-xs);
  font-style: italic;
}

/* The one distance from any group label to the control under it — <label> in
   the div groups, <legend> in the fieldsets, same number either way. */
.filter-sidebar__label {
  display: block;
  margin-bottom: var(--ds-space-2);
  /* Zeroes the browser's default legend padding-inline too, so a legend and a
     label start on the same left edge. */
  padding: 0;
  font-size: var(--ds-text-xs);
  font-weight: var(--ds-weight-semibold);
  letter-spacing: var(--ds-tracking-wide);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

/* Search field with an inset icon: the glyph is decorative, so it never takes
   the pointer, and the field reserves its lane with padding. */
.filter-sidebar__search {
  position: relative;
  min-width: 0;
}

.filter-sidebar__search-icon {
  position: absolute;
  inset-inline-start: var(--ds-space-3);
  top: 50%;
  width: 1.05rem;
  height: 1.05rem;
  transform: translateY(-50%);
  color: var(--color-text-muted);
  pointer-events: none;
}

/* ── Facet lists (category, brand) ───────────────────────────────────────── */

/* Every option renders: no max-height, no inner scrollbar. A customer sees the
   whole catalogue shape at once instead of discovering it by scrolling a 200px
   box inside a page that also scrolls. */
.filter-sidebar__list {
  display: flex;
  flex-direction: column;
  /* No gap. The rows are their own rhythm — 2.25rem each — and a stray pixel
     between them was neither a separation anyone reads nor part of the scale. */
  gap: 0;
  margin: 0;
  padding: 0;
  list-style: none;
  max-height: none;
  overflow: visible;
}

.filter-sidebar__item {
  min-width: 0;
}

.filter-sidebar__option {
  display: flex;
  align-items: center;
  gap: var(--ds-space-3);
  /* 2.25rem rows keep the whole tree on one screen where it fits; the label is
     the hit area, so the target is the full row, not the 16px radio. */
  min-height: 2.25rem;
  padding: var(--ds-space-1) var(--ds-space-3);
  /* Child categories step in under their parent; --facet-level comes from the
     partial, which computes depth in memory. */
  padding-inline-start: calc(var(--ds-space-3) + var(--facet-level, 0) * var(--ds-space-4));
  border-radius: var(--ds-radius-sm);
  color: var(--color-text);
  font-size: var(--ds-text-sm);
  cursor: pointer;
  transition:
    background-color var(--ds-duration-fast) var(--ds-ease),
    color var(--ds-duration-fast) var(--ds-ease);
}

.filter-sidebar__option:hover {
  background: var(--ds-tint-hover);
}

.filter-sidebar__option:has(.filter-sidebar__radio:checked) {
  background: var(--ds-tint-primary);
  color: var(--color-primary);
  font-weight: var(--ds-weight-semibold);
}

.filter-sidebar__radio {
  flex: none;
  width: 0.95rem;
  height: 0.95rem;
  margin: 0;
  accent-color: var(--color-primary);
  cursor: pointer;
}

.filter-sidebar__option-name {
  flex: 1 1 auto;
  min-width: 0;
  overflow-wrap: anywhere;
}

.filter-sidebar__count {
  flex: none;
  color: var(--color-text-muted);
  font-size: var(--ds-text-xs);
  font-variant-numeric: tabular-nums;
}

/* A facet the current search/price scope leaves empty stays selectable — it is
   still a valid place to go — but it stops competing for attention. */
.filter-sidebar__option--empty .filter-sidebar__option-name,
.filter-sidebar__option--empty .filter-sidebar__count {
  opacity: 0.5;
}

.filter-sidebar__field {
  width: 100%;
  min-height: 2.75rem;
  padding: var(--ds-space-2) var(--ds-space-3);
  border: 1px solid var(--ds-hairline-strong);
  border-radius: var(--ds-radius-sm);
  background: var(--color-background);
  color: var(--color-text);
  /* --ds-text-md is 1rem, which is also the floor iOS needs: anything smaller
     and it zooms the page on focus. The token, not a literal, so the field sits
     on the same step as the card titles instead of beside the scale. */
  font-size: var(--ds-text-md);
  font-family: inherit;
  text-align: left;
  transition:
    border-color var(--ds-duration-fast) var(--ds-ease),
    box-shadow var(--ds-duration-fast) var(--ds-ease);
}

/* Must sit AFTER .filter-sidebar__field: that rule sets `padding` as a
   shorthand, so at equal specificity it resets the inline-start padding this
   one adds, and the value typed into the search field ran under the icon. */
.filter-sidebar__field--search {
  padding-inline-start: 2.4rem;
}

.filter-sidebar__field::placeholder {
  color: color-mix(in srgb, var(--color-text-muted) 70%, transparent);
}

.filter-sidebar__field:hover {
  border-color: color-mix(in srgb, var(--color-primary) 40%, var(--ds-hairline-strong));
}

.filter-sidebar__field:focus,
.filter-sidebar__field:focus-visible {
  outline: none;
  border-color: var(--color-primary);
  box-shadow: 0 0 0 3px var(--ds-tint-primary-strong);
}

.filter-sidebar__submit {
  min-height: 2.75rem;
  padding-inline: var(--ds-space-4);
  border: 0;
  border-radius: var(--button-radius, var(--ds-radius-sm));
  background: var(--color-primary);
  color: var(--color-primary-contrast);
  font-size: var(--ds-text-sm);
  font-weight: var(--ds-weight-semibold);
  cursor: pointer;
}

/* ── Product grid ────────────────────────────────────────────────────────── */

/* Result count: a quiet line above the results, aligned with the first card. It
   is not a page subtitle — it belongs to the grid, and it is replaced together
   with it on every filter change. */
.product-grid__count {
  margin-bottom: var(--ds-space-4);
  color: var(--color-text-muted);
  font-size: var(--ds-text-sm);
  font-variant-numeric: tabular-nums;
}

.product-grid__items {
  display: grid;
  /* auto-fill with a floor: the admin column count is the *maximum*, and the
     grid reflows below it on narrow viewports without a single media query.
     One rule replaces the four --cols-N breakpoint blocks. */
  grid-template-columns: repeat(auto-fill, minmax(min(16rem, 100%), 1fr));
  gap: var(--ds-space-6) var(--ds-space-5);
}

@media (min-width: 640px) {
  .product-grid__items--cols-2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
}

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

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

.product-grid__empty {
  padding: var(--ds-space-16) var(--ds-space-6);
  border: 1px dashed var(--ds-hairline-strong);
  border-radius: var(--ds-radius-lg);
  text-align: center;
  color: var(--color-text-muted);
  font-size: var(--ds-text-md);
}

/* Pagy's series_nav, wherever the storefront paginates. It used to be scoped to
   the product grid (.product-grid__pagination), so the account order list —
   which renders the very same helper — got no styling at all and its anchors
   collapsed into the string "<12>": the gem's own it locale writes prev/next as
   &lt; and &gt;, and with no gap, no min-width and no radius they read as one
   word. Any nav wrapping series_nav takes this class. */
.pagination-bar {
  display: flex;
  justify-content: center;
  margin-top: var(--ds-space-12);
  padding-top: var(--ds-space-8);
  border-top: 1px solid var(--ds-hairline);
}

.pagination-bar .pagy,
.pagination-bar nav {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ds-space-1);
}

.pagination-bar a,
.pagination-bar span {
  display: inline-grid;
  place-items: center;
  min-width: 2.5rem;
  min-height: 2.5rem;
  padding-inline: var(--ds-space-2);
  border-radius: var(--ds-radius-sm);
  color: var(--color-text-muted);
  font-size: var(--ds-text-sm);
  font-weight: var(--ds-weight-medium);
  font-variant-numeric: tabular-nums;
  text-decoration: none;
  transition:
    background-color var(--ds-duration-fast) var(--ds-ease),
    color var(--ds-duration-fast) var(--ds-ease);
}

.pagination-bar a:hover {
  background: var(--ds-tint-hover);
  color: var(--color-text);
}

/* Pagy 43 sets no class attribute on its anchors: the current page is marked
   with aria-current and the gap with role="separator". The old .current / .gap
   selectors are dropped rather than kept alongside, matching admin.css. */
.pagination-bar [aria-current="page"] {
  background: var(--color-primary);
  color: var(--color-primary-contrast);
}

.pagination-bar [role="separator"] {
  min-width: 1.5rem;
}

/* ── Product card ────────────────────────────────────────────────────────── */

.product-card {
  position: relative;
  display: flex;
  flex-direction: column;
  border: 1px solid var(--ds-hairline);
  border-radius: var(--ds-radius-lg);
  background: var(--color-background);
  box-shadow: var(--ds-shadow-xs);
  /* Clips the image scale to the rounded corner without `overflow: hidden` on
     the card itself, which would also clip the focus ring and the badges. */
  isolation: isolate;
  transition:
    transform var(--ds-duration-base) var(--ds-ease),
    box-shadow var(--ds-duration-base) var(--ds-ease),
    border-color var(--ds-duration-base) var(--ds-ease);
}

.product-card:hover {
  transform: translateY(-3px);
  border-color: var(--ds-hairline-strong);
  box-shadow: var(--ds-shadow-md);
}

/* The whole card is the click target, via a stretched pseudo-element on the
   title link — so a click on the padding still opens the product, while the
   accessible name stays on the real <a> and text inside remains selectable. */
.product-card__title a::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
  z-index: 1;
}

/* Focus lands on the invisible stretched link; draw the ring on the card. */
.product-card:has(.product-card__title a:focus-visible) {
  box-shadow: var(--ds-focus-ring), var(--ds-shadow-md);
}

.product-card__media {
  position: relative;
  aspect-ratio: 4 / 5;
  border-radius: calc(var(--ds-radius-lg) - 1px) calc(var(--ds-radius-lg) - 1px) 0 0;
  background: var(--ds-surface-sunken);
  overflow: hidden;
}

.product-card__link {
  display: block;
  width: 100%;
  height: 100%;
}

/* `contain`, not `cover`. The box stays 4:5 so the grid keeps one rhythm, but
   what goes in it is fitted rather than filled: a supplier's landscape shot of a
   lock was being blown up until only its middle was left, and a card that crops
   the product out of the photo is not a smaller picture of the product, it is a
   different one. Garment photography is shot 4:5, so for every card that looked
   right before this changes nothing — contain and cover agree when the ratios
   agree. A wider or taller shot now sits inside the sunken background instead of
   being cut to fit it. */
.product-card__image {
  width: 100%;
  height: 100%;
  object-fit: contain;
  /* Slower than the card lift, so the photo settles *after* the card arrives.
     Matching durations makes the whole thing move as one flat plane. */
  transition: transform var(--ds-duration-slower) var(--ds-ease);
  /* The centre of the tile unless the card names a target. A composed product
     does: its photo is a rendered garment and the thing worth looking at is the
     print on it, a third of a shirt wide and never in the middle of the frame.
     Frontend::AttachmentHelper#print_zoom_style reads the box off the blob.
     [personalized_products] */
  transform-origin: var(--card-zoom-x, 50%) var(--card-zoom-y, 50%);
}

/* 1.045 is the lift for a photograph: enough to feel alive, not enough to be a
   zoom. A card that names a print asks for the real thing. */
.product-card:hover .product-card__image {
  transform: scale(var(--card-zoom, 1.045));
}

.product-card__image--placeholder {
  display: grid;
  place-items: center;
  background: var(--ds-surface-sunken);
  color: color-mix(in srgb, var(--color-text-muted) 45%, transparent);
}

/* Centred, the way the big fashion grids set a card: the title, the price and
   the availability line share one axis, so a row of cards reads as a row of
   labels rather than four ragged columns of text. The action row stays full
   width underneath. */
.product-card__body {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--ds-space-2);
  padding: var(--ds-space-4) var(--ds-space-4) var(--ds-space-5);
  flex: 1;
  text-align: center;
}

/* The children are flex items in a centred column, so anything that has to span
   the card says so. The price is a flex row of its own; centre its parts too. */
.product-card__price,
.product-card__actions {
  width: 100%;
}

.product-card__price .price {
  justify-content: center;
}

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

.product-card__title {
  font-size: var(--ds-text-md);
  font-weight: var(--ds-weight-semibold);
  line-height: var(--ds-leading-snug);
  letter-spacing: var(--ds-tracking-tight);
  color: var(--color-text);
  margin: 0;
}

.product-card__title a {
  color: inherit;
  text-decoration: none;
  transition: color var(--ds-duration-fast) var(--ds-ease);
}

.product-card:hover .product-card__title a {
  color: var(--color-primary);
}

.product-card__price {
  margin-top: auto;
  padding-top: var(--ds-space-1);
}

/* Availability under the price: small caps in the state's colour, no chip and no
   dot. A filled badge on the photograph read as clutter over the product, and it
   shouts the ordinary case ("in stock") as loudly as the exceptional one. The
   word carries the meaning on its own, so the colour is reinforcement — the card
   still says what it needs to in greyscale or to a colour-blind buyer. */
.product-card__stock {
  margin: 0;
  font-size: var(--ds-text-xs);
  font-weight: var(--ds-weight-semibold);
  letter-spacing: var(--ds-tracking-wide);
  text-transform: uppercase;
}

.product-card__stock--in-stock {
  color: var(--color-success);
}

/* The raw warning amber (#f59e0b by default) is ~2:1 on a white surface — fine
   as a badge fill behind dark ink, too weak as 12px text. Mixed toward the text
   colour it keeps reading as amber and clears AA. */
.product-card__stock--preorderable {
  color: color-mix(in srgb, var(--color-warning) 70%, var(--color-text));
}

.product-card__stock--out-of-stock {
  color: var(--color-danger);
}

.product-card__actions {
  position: relative;
  /* Above the stretched title link, or the button would never receive the click. */
  z-index: 2;
  margin-top: var(--ds-space-3);
}

.product-card__cta {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--ds-space-2);
  width: 100%;
  min-height: 2.75rem;
  padding-inline: var(--ds-space-4);
  border: 1px solid var(--ds-hairline-strong);
  border-radius: var(--button-radius, var(--ds-radius-sm));
  background: var(--color-background);
  color: var(--color-text);
  font-size: var(--ds-text-sm);
  font-weight: var(--ds-weight-semibold);
  text-decoration: none;
  transition:
    background-color var(--ds-duration-fast) var(--ds-ease),
    border-color var(--ds-duration-fast) var(--ds-ease),
    color var(--ds-duration-fast) var(--ds-ease);
}

.product-card__cta:hover {
  border-color: var(--color-primary);
  background: var(--ds-tint-primary);
  color: var(--color-primary);
}

.product-card__cta .hi {
  transition: transform var(--ds-duration-base) var(--ds-ease);
}

.product-card__cta:hover .hi {
  transform: translateX(2px);
}

/* ── Badges ──────────────────────────────────────────────────────────────── */

.product-card__badges {
  position: absolute;
  inset-block-start: var(--ds-space-3);
  inset-inline-start: var(--ds-space-3);
  /* Stop short of the wishlist heart in the opposite corner, and wrap rather
     than run off the card: "Disponibile in preordine" is longer than the card is
     wide, and it was being clipped mid-word by the media box. */
  inset-inline-end: 3.5rem;
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--ds-space-2);
  pointer-events: none;
}

.product-card__badges .product-card__badge {
  max-width: 100%;
  text-align: start;
  white-space: normal;
}

/* Not colour alone: a discount reads as a badge with a word in it, so it still
   says "sale" to someone who cannot see the red. Availability is not a badge —
   it is the line of small caps under the price. */
.price__badge,
.product-card__badge {
  display: inline-flex;
  align-items: center;
  gap: var(--ds-space-1);
  margin: 0;
  padding: 0.3rem 0.6rem;
  border-radius: var(--ds-radius-full);
  background: var(--color-danger);
  color: var(--color-danger-contrast);
  font-size: var(--ds-text-xs);
  font-weight: var(--ds-weight-bold);
  letter-spacing: var(--ds-tracking-wide);
  text-transform: uppercase;
  line-height: var(--ds-leading-none);
  box-shadow: var(--ds-shadow-sm);
  backdrop-filter: blur(8px);
}

/* ── Wishlist toggle ─────────────────────────────────────────────────────── */

.wishlist-button {
  position: absolute;
  inset-block-start: var(--ds-space-3);
  inset-inline-end: var(--ds-space-3);
  z-index: 3;
}

.wishlist-button__toggle {
  display: grid;
  place-items: center;
  width: 2.5rem;
  height: 2.5rem;
  border: 1px solid var(--ds-hairline);
  border-radius: var(--ds-radius-full);
  /* Frosted rather than opaque: it sits on the photo and should not punch a
     white hole in it. */
  background: color-mix(in srgb, var(--color-background) 78%, transparent);
  backdrop-filter: blur(10px) saturate(1.4);
  color: var(--color-text);
  cursor: pointer;
  padding: 0;
  box-shadow: var(--ds-shadow-sm);
  transition:
    transform var(--ds-duration-fast) var(--ds-ease-spring),
    background-color var(--ds-duration-fast) var(--ds-ease),
    color var(--ds-duration-fast) var(--ds-ease);
}

.wishlist-button__toggle:hover {
  transform: scale(1.08);
  background: var(--color-background);
  color: var(--color-danger);
}

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

.wishlist-button--active .wishlist-button__toggle {
  color: var(--color-danger);
}

.wishlist-button__icon {
  width: 1.25rem;
  height: 1.25rem;
}

/* The label is for screen readers; the heart carries the meaning visually. */
.wishlist-button__label {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* button_to wraps the button in a <form>; strip its layout so the toggle is not
   pushed off its corner. */
.wishlist-button form {
  margin: 0;
  display: contents;
}

/* ── The heart's reaction on a successful add ────────────────────────────── */

/* The node the ADD stream sends back carries celebrate-value, and
   wishlist_button_controller puts this class on for the length of the burst.
   Everything below is decoration: the alert carries the message, and a reader
   who cannot see any of it loses nothing. */
.wishlist-button__sparks {
  position: absolute;
  inset: 0;
  pointer-events: none;
}

/* The ring: one expanding circle in the danger colour the filled heart already
   uses, so the burst reads as the heart itself and not as a second element. */
.wishlist-button__sparks::before {
  content: "";
  position: absolute;
  inset: 0;
  border: 2px solid var(--color-danger);
  border-radius: var(--ds-radius-full);
  opacity: 0;
}

.wishlist-button--burst .wishlist-button__sparks::before {
  animation: wishlist-ring 520ms var(--ds-ease) forwards;
}

/* Six sparks, each flying out along the angle its inline --wishlist-spark-angle
   sets. They start stacked in the middle of the toggle; the rotation decides the
   direction and the translate the distance. */
.wishlist-button__spark {
  position: absolute;
  inset-block-start: 50%;
  inset-inline-start: 50%;
  width: 0.3rem;
  height: 0.3rem;
  margin: -0.15rem 0 0 -0.15rem;
  border-radius: var(--ds-radius-full);
  background: var(--color-danger);
  opacity: 0;
}

.wishlist-button--burst .wishlist-button__spark {
  animation: wishlist-spark 560ms var(--ds-ease) forwards;
}

.wishlist-button--burst .wishlist-button__toggle {
  animation: wishlist-pop 520ms var(--ds-ease-spring);
}

@keyframes wishlist-pop {
  0%   { transform: scale(1); }
  30%  { transform: scale(0.82); }
  60%  { transform: scale(1.18); }
  100% { transform: scale(1); }
}

@keyframes wishlist-ring {
  0%   { opacity: 0.85; transform: scale(0.7); }
  100% { opacity: 0;    transform: scale(1.75); }
}

@keyframes wishlist-spark {
  0% {
    opacity: 1;
    transform: rotate(var(--wishlist-spark-angle, 0deg)) translateY(0) scale(1);
  }
  70% { opacity: 1; }
  100% {
    opacity: 0;
    transform: rotate(var(--wishlist-spark-angle, 0deg)) translateY(-1.35rem) scale(0.4);
  }
}

/* A burst is pure delight and nothing else, so it is the first thing to go when
   the visitor has asked for less motion. The alert still appears. */
@media (prefers-reduced-motion: reduce) {
  .wishlist-button--burst .wishlist-button__toggle,
  .wishlist-button--burst .wishlist-button__spark,
  .wishlist-button--burst .wishlist-button__sparks::before {
    animation: none;
  }
}

/* The Swal alert this raises after saving a product. Swal owns the box; what is
   styled here is the centered product, message, and wishlist button. */
.wishlist-alert {
  width: min(92vw, 30rem);
  font-size: var(--ds-text-sm);
}

.wishlist-alert .swal2-html-container {
  min-width: 0;
  overflow: visible;
}

.wishlist-alert__body {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--ds-space-4);
  min-width: 0;
  text-align: center;
}

/* The product's name inside the sentence, not a heading above it: the copy is
   one interpolated string so a translation can put the name wherever its grammar
   wants it. */
.wishlist-alert__product {
  color: var(--color-text);
  font-weight: var(--ds-weight-semibold);
}

.wishlist-alert__text {
  margin: 0;
  min-width: 0;
  color: var(--color-text-muted);
  font-size: var(--ds-text-base);
  white-space: normal;
  overflow-wrap: anywhere;
}

.wishlist-alert__actions {
  display: flex;
  justify-content: center;
  gap: var(--ds-space-3);
  width: 100%;
}

/* Scoped through .wishlist-alert rather than carried by !important: the only
   thing that would otherwise win here is the storefront's own link colour, and a
   second class in the selector settles that without making these two rules
   impossible to override from a client theme. --button-radius is the shop's
   configured button radius (--radius-button does not exist), and the label sits
   on --color-primary, so its colour is the contrast ColorsHelper computes for
   that palette — never a fixed white, which disappears on a light primary. */
.wishlist-alert .wishlist-alert__link,
.wishlist-alert .wishlist-alert__confirm {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-height: 2.75rem;
  min-width: 7rem;
  padding: 0 var(--ds-space-6);
  border: 0;
  border-radius: var(--button-radius, var(--ds-radius-sm));
  background: var(--color-primary);
  color: var(--color-primary-contrast);
  font-weight: var(--ds-weight-semibold);
  white-space: nowrap;
  text-decoration: none;
}

.wishlist-alert .wishlist-alert__link:hover,
.wishlist-alert .wishlist-alert__confirm:hover {
  filter: brightness(0.96);
}

.wishlist-alert .wishlist-alert__link:focus-visible,
.wishlist-alert .wishlist-alert__confirm:focus-visible {
  outline: 2px solid var(--color-primary);
  outline-offset: 2px;
}

.wishlist-alert__confirm {
  cursor: pointer;
}

/* On the detail page the toggle is a quiet icon-only ghost at the right end of
   the share row, sized like the email and copy-link buttons it stands beside.
   The label stays sr-only (base rule). */
/* `relative`, not `static`: it takes exactly the same space in the share row
   either way, but the burst layer is positioned against this node — left static
   the sparks would fly out of whatever ancestor happened to be positioned. */
.product-detail__wishlist .wishlist-button {
  position: relative;
}

.product-detail__wishlist .wishlist-button__toggle {
  background: transparent;
  backdrop-filter: none;
  box-shadow: none;
  color: var(--color-text-muted);
}

.product-detail__wishlist .wishlist-button__toggle:hover {
  transform: none;
  background: var(--ds-tint-hover);
  color: var(--color-danger);
}

.product-detail__share .product-detail__wishlist {
  flex: none;
  display: inline-flex;
}

/* Same 2.25rem circle as the email and copy-link buttons, so the three read as
   one row of controls rather than a heart parked next to two buttons. */
.product-detail__share .wishlist-button__toggle {
  width: 2.25rem;
  height: 2.25rem;
  padding: 0;
  border: 1px solid var(--ds-hairline-strong);
  border-radius: 999px;
}

/* ── Price ───────────────────────────────────────────────────────────────── */

.price {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--ds-space-2);
  font-variant-numeric: tabular-nums;
}

.price__final {
  font-size: var(--ds-text-xl);
  font-weight: var(--ds-weight-bold);
  line-height: var(--ds-leading-none);
  letter-spacing: var(--ds-tracking-tighter);
  color: var(--color-text);
  font-variant-numeric: tabular-nums;
}

.price__original {
  font-size: var(--ds-text-sm);
  color: var(--color-text-muted);
  text-decoration: line-through;
  text-decoration-thickness: 1px;
  font-variant-numeric: tabular-nums;
}

.price__vat,
.price__from {
  font-size: var(--ds-text-xs);
  color: var(--color-text-muted);
}

.price__from {
  font-weight: var(--ds-weight-medium);
}

/* On the detail page the price is the headline, not a label -- and its VAT note
   sits on the same 13px rung as the meta line and the trust band, not a rung
   below. A card's note stays 12px: there the price itself is small.

   The figure is drawn at the H1's size, the same two literal steps (see
   .product-detail__title): the name and the price are the two things the page
   is about, and a 2rem figure under a 2.25rem name read as a subheading. */
.product-detail__price .price__final {
  font-size: 1.875rem;
  color: var(--color-primary);
}

@media (min-width: 768px) {
  .product-detail__price .price__final {
    font-size: 2.25rem;
  }
}

.product-detail__price .price__vat,
.product-detail__price .price__from {
  font-size: var(--ds-text-sm);
}

/* ── Product detail ──────────────────────────────────────────────────────── */

/* The detail page carries no container, breadcrumb or page-margin CSS of its
   own: it renders inside the shared page shell (page_shell.css), same as the
   catalog and the CMS pages. */

.product-detail__layout {
  display: grid;
  gap: var(--ds-space-10);
  align-items: start;
}

@media (min-width: 1024px) {
  .product-detail__layout {
    grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr);
    /* gap: var(--ds-space-16); */
  }

  /* The buy column follows on long pages: the decision is made at the top and
     the button should still be there when the description ends. */
  .product-detail__info {
    position: sticky;
    top: var(--ds-space-8);
  }
}

/* One rhythm down the column: every part -- name, meta line, price, options,
   buy form, share, trust -- is the same distance from the next. The parts used
   to carry their own margins on top of this gap (the price a 1.25rem
   padding-bottom, the spec sheet a 1.5rem margin-top), so the column read as a
   list of unrelated blocks. Anything that needs to sit closer belongs INSIDE a
   part, not to a gap of its own. */
.product-detail__info {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-3);
  min-width: 0;
}

/* The same artwork on the other garments, under the two columns. It reads as a
   band of the page, not as one more field of the buy column. */
.product-detail__siblings {
  margin-top: var(--ds-space-16);
  padding-top: var(--ds-space-10);
  border-top: 1px solid var(--ds-hairline);
}

.product-detail__siblings-title {
  font-size: var(--ds-text-xl);
  font-weight: var(--ds-weight-semibold);
  letter-spacing: var(--ds-tracking-tight);
  color: var(--color-text);
  margin: 0 0 var(--ds-space-6);
}

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

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

.product-detail__brand {
  text-align: center;
  font-size: var(--ds-text-xs);
  font-weight: var(--ds-weight-semibold);
  letter-spacing: var(--ds-tracking-wide);
  text-transform: uppercase;
  color: var(--color-primary);
  margin: 0;
  order: -1;
}

/* The product name is the page's H1 and the CMS modules under it bring their own
   H2s; when a product carries components the two used to be drawn on different
   scales -- the title ran to 2.75rem at the top of the clamp while the module
   heading below it stopped at 2.25rem, so the page read as two designs stacked.
   These values ARE the module heading's: `text-3xl md:text-4xl font-bold
   leading-tight tracking-tight`, the Tailwind classes in text_images_base's
   _texts_part and standard_contact. Literal rem on purpose -- the point is to
   equal those classes, and a token that drifts from them breaks the match. */
.product-detail__title {
  text-align: center;
  font-size: 1.875rem;
  font-weight: var(--ds-weight-bold);
  line-height: 1.25;
  letter-spacing: -0.025em;
  color: var(--color-text);
  text-wrap: balance;
  margin: 0;
}

@media (min-width: 768px) {
  .product-detail__title {
    font-size: 2.25rem;
  }
}

/* The price is the panel that closes the identity block: a light sunken ground
   with square corners, the figure in the brand colour. It replaces the hairline
   that used to run above it -- a rule AND a panel both separate, and two
   separators in a row read as an accident.

   The one part that breaks the column's single gap, on purpose: a filled band
   needs air around it that a line of text does not, so it doubles the column's
   gap on each side. Change the gap and this keeps its proportion. */
.product-detail__price {
  display: flex;
  align-items: baseline;
  justify-content: center;
  gap: var(--ds-space-3);
  margin-block: var(--ds-space-3);
  padding: var(--ds-space-4);
  border-radius: 0;
  background: var(--ds-surface-sunken);
}

.product-detail__stock {
  display: inline-flex;
  align-items: center;
  gap: var(--ds-space-2);
  align-self: flex-start;
  padding: 0.35rem 0.7rem;
  border-radius: var(--ds-radius-full);
  font-size: var(--ds-text-sm);
  font-weight: var(--ds-weight-medium);
  margin: 0;
}

.product-detail__stock--preorder {
  background: color-mix(in srgb, var(--color-warning) 12%, transparent);
  color: color-mix(in srgb, var(--color-warning) 70%, var(--color-text));
}

.product-detail__stock--out {
  background: var(--ds-surface-sunken);
  color: var(--color-text-muted);
}

.product-detail__stock--in {
  background: color-mix(in srgb, var(--color-success) 12%, transparent);
  color: color-mix(in srgb, var(--color-success) 70%, var(--color-text));
}

/* The three availability states of a variant product live side by side and the
   option_picker shows one at a time. `display: inline-flex` on the badge would
   beat the hidden attribute, so restate it. */
.product-detail__stock-states > [hidden] {
  display: none;
}

/* Units of the resolved combination, riding inside the in-stock badge as a
   quieter second line of information. */
.product-detail__stock-units {
  font-weight: var(--ds-weight-normal);
  opacity: 0.75;
}

.product-detail__stock-units[hidden] {
  display: none;
}

/* Full width of the shell, under the two columns and above the CMS module band:
   the description is prose about the product, and the 0.85fr buy column broke it
   into a ribbon of three-word lines. */
.product-detail__description {
  margin-top: var(--ds-space-12);
  padding-top: var(--ds-space-8);
  border-top: 1px solid var(--ds-hairline);
}

/* The band has its own heading, on the same rung as "the same artwork on the
   other garments" below it -- as a full-width section it is no longer a
   micro-label inside a column. */
.product-detail__description-title {
  font-size: var(--ds-text-xs);
  font-weight: var(--ds-weight-semibold);
  letter-spacing: var(--ds-tracking-wide);
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin: 0 0 var(--ds-space-3);
}

.product-detail__description-body {
  color: var(--color-text);
  font-size: var(--ds-text-md);
  line-height: var(--ds-leading-relaxed);
}

.product-detail__description-body > * + * {
  margin-top: var(--ds-space-4);
}

.product-detail__description-body a {
  color: var(--color-primary);
  text-underline-offset: 0.2em;
}

/* Technical sheet of a typed product [product_typologies]. No margin of its
   own: the column gap already spaces it. */

.product-specs {
  margin: 0;
  max-width: 65ch;
}

.product-specs__row {
  display: flex;
  justify-content: space-between;
  gap: var(--ds-space-4);
  padding: var(--ds-space-2) 0;
  border-bottom: 1px solid var(--color-border, var(--color-secondary));
  font-size: var(--ds-text-sm);
}

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

.product-specs__value {
  margin: 0;
  color: var(--color-text);
  font-weight: var(--ds-weight-medium);
  text-align: right;
}

/* One line under the name: availability | SKU | code. Rules between the items,
   not around them -- a border on the availability BADGE would cut across a
   rounded pill, so each fact is wrapped in its own item and the rule is drawn
   on the wrapper. A wrapped row drops the rule at the start of a new line: it
   would read as a rule against nothing. */
.product-detail__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--ds-space-3);
  font-size: var(--ds-text-sm);
  color: var(--color-text-muted);
  /* Queried by the rule below. Containment is safe here: this row holds no
     positioned descendant (the fixed sticky-buy-bar is a sibling further down
     the column, which is why the container is the ROW and not the column). */
  container-type: inline-size;
}

.product-detail__meta-item {
  display: inline-flex;
  align-items: center;
  gap: var(--ds-space-2);
}

.product-detail__meta-item + .product-detail__meta-item {
  border-left: 1px solid var(--ds-hairline-strong);
  padding-left: var(--ds-space-3);
}

/* Too narrow for the three facts on one line: the row wraps, and a wrapped item
   carries its rule to the START of the next line, where it reads as a rule
   against nothing. The width that matters is the ROW's, not the viewport's --
   the same row is 720px wide on a tablet, where the page is one column, and
   500px in the desktop buy column, so a media query would get one of the two
   wrong.

   32rem is measured, not guessed: the three facts of a long-code product
   ("Disponibile in preordine" + MN-OVERSIZEVERDE + MN OVERSIZEVERDE) measure
   530px and wrap in the 500px buy column, while the same row fits easily in the
   720px tablet column. Below the threshold the facts keep the wider gap and
   simply read as three centred phrases. */
@container (max-width: 32rem) {
  .product-detail__meta {
    gap: var(--ds-space-4);
  }

  .product-detail__meta-item + .product-detail__meta-item {
    border-left: 0;
    padding-left: 0;
  }
}

/* An empty availability item would leave a rule hanging off the last fact. */
.product-detail__meta-item:empty {
  display: none;
}

.product-detail__sku-value {
  font-variant-numeric: tabular-nums;
  color: var(--color-text);
}

/* Reassurance under the buy button: shipping, returns, secure payment. Icons
   carry no meaning on their own here — each has its label beside it. */
/* No frame: the band is reassurance, not a card, and the box competed with the
   buy button right above it. */
.product-detail__trust {
  display: grid;
  gap: var(--ds-space-3);
  /* No margin: the column gap owns the distance to the share row above. */
  margin: 0;
  padding: var(--ds-space-4);
  list-style: none;
  border-radius: var(--ds-radius-md);
  background: var(--ds-gradient-surface);
}

.product-detail__trust-item {
  display: flex;
  align-items: center;
  gap: var(--ds-space-3);
  font-size: var(--ds-text-sm);
  color: var(--color-text-muted);
}

.product-detail__trust-item .hi,
.product-detail__trust-item svg {
  width: 1.125rem;
  height: 1.125rem;
  flex: none;
  color: var(--color-primary);
}

/* ── Product gallery ─────────────────────────────────────────────────────── */

/* ONE box, whatever is in it. The stage states its own height and every slide
   fills it, so a 4:5 supplier portrait, a wide banner and the "no photo"
   placeholder all occupy the same rectangle: the page does not resize under the
   buyer as they click through colourways, and the thumbnail strip never moves.

   The photo is fitted with `contain`, not `cover`: a catalogue fed by suppliers
   never ships one ratio (the B&C shots are 4:5), and cropping cut the garment
   top and bottom. Letterboxing inside a fixed box costs some background and
   crops nothing. */
.product-gallery__stage {
  position: relative;
  height: min(70vh, 34rem);
  border: 1px solid var(--ds-hairline);
  border-radius: var(--ds-radius-xl);
  overflow: hidden;
  background: var(--ds-surface-sunken);
  box-shadow: var(--ds-shadow-sm);
}

/* The slide is a <button> (it opens the lightbox), so it inherits the button
   default `display: inline-block` and SHRINK-WRAPS to the photo. The image
   inside asks for width/height 100% — of a box that was sized by the image
   itself — so it stayed at its natural size and left the square stage empty
   around it on any viewport wider than the photo. The slide has to fill the
   stage first; then `cover` on the image means something. */
.product-gallery__slide {
  display: block;
  box-sizing: border-box;
  width: 100%;
  height: 100%;
  padding: 0;
  border: 0;
  background: none;
  cursor: zoom-in;
}

/* Inactive slides are hidden with the `hidden` ATTRIBUTE, whose UA rule a class
   selector of equal specificity would otherwise beat. */
.product-gallery__slide[hidden] {
  display: none;
}

.product-gallery__slide {
  padding: var(--ds-space-4);
  background: var(--color-background);
}

/* The stage owns the box; the media fits inside it. Letting the photo set the
   height instead put a 2000px supplier portrait past the fold, and made every
   colourway a different page length.

   <picture> is in the chain (optimize_image_tag wraps every photo) and is an
   INLINE box by default, so `height: 100%` on the img resolved against a box of
   automatic height and did nothing. It has to carry the height down. */
.product-gallery__slide picture {
  display: block;
  width: 100%;
  height: 100%;
}

.product-gallery__slide img,
.product-gallery__slide video {
  display: block;
  width: 100%;
  height: 100%;
  object-fit: contain;
  margin-inline: auto;
}

/* No photo to fit, same box to fill: the placeholder takes the stage the photos
   take and the note sits under it, so a colourway nobody shot changes the
   CONTENT of the box and never its size. `aspect-ratio` here sized the slide
   itself — a square as wide as the column, which on a laptop was taller than
   the stage and pushed the note and the thumbnails down the page. */
.product-gallery__slide--empty,
.product-gallery__slide--missing {
  display: grid;
  grid-template-rows: 1fr auto;
  gap: var(--ds-space-3);
  color: color-mix(in srgb, var(--color-text-muted) 55%, transparent);
}

/* The colour has no shot of its own. Said in words under the placeholder — the
   grey box alone is indistinguishable from a slow download. */
.product-gallery__missing-note {
  margin: 0;
  font-size: var(--ds-text-sm);
  color: var(--color-text-muted);
  text-align: center;
}

/* A fixed thumb count per row, per the owner's spec: the strip is a colour
   chart, and a chart that reflows to a different number of columns per viewport
   is read as a different chart. Grid, not flex-wrap: the count is the rule and
   the thumb size follows from it. */
.product-gallery__thumbs {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: var(--ds-space-3);
  margin-top: var(--ds-space-4);
}

/* Tablet */
@media (min-width: 640px) {
  .product-gallery__thumbs { grid-template-columns: repeat(10, minmax(0, 1fr)); }
}

/* Laptop: the gallery is half the row from here, so ten thumbs would be stamps. */
@media (min-width: 1024px) {
  .product-gallery__thumbs { grid-template-columns: repeat(6, minmax(0, 1fr)); }
}

/* Laptop L / desktop */
@media (min-width: 1440px) {
  .product-gallery__thumbs { grid-template-columns: repeat(8, minmax(0, 1fr)); }
}

.product-gallery__thumb {
  position: relative;
  /* The column sets the width; the square keeps the row even. */
  width: 100%;
  aspect-ratio: 1 / 1;
  border: 1px solid var(--ds-hairline);
  border-radius: var(--ds-radius-md);
  overflow: hidden;
  /* Transparent, not sunken: the shots are cut-outs on white, so a grey plate
     behind them reads as a grey box AROUND each shirt instead of a background. */
  background: transparent;
  cursor: pointer;
  padding: 0;
  transition:
    border-color var(--ds-duration-fast) var(--ds-ease),
    box-shadow var(--ds-duration-fast) var(--ds-ease),
    transform var(--ds-duration-fast) var(--ds-ease);
}

/* Same rule as the stage: a cropped thumbnail of a t-shirt is a rectangle of
   colour with no sleeves, and a row of them is unreadable. */
.product-gallery__thumb img {
  width: 100%;
  height: 100%;
  object-fit: contain;
  padding: var(--ds-space-1);
}

.product-gallery__thumb:hover {
  transform: translateY(-2px);
  border-color: var(--ds-hairline-strong);
  box-shadow: var(--ds-shadow-sm);
}

/* The selected thumb gets a ring drawn inside its own box, so selecting one does
   not shift the row by a border width. */
.product-gallery__thumb[aria-current="true"] {
  border-color: var(--color-primary);
  box-shadow: inset 0 0 0 2px var(--color-primary);
}

/* ── Variant selector ────────────────────────────────────────────────────── */

/* Same row shape as every other option: "VARIANTE" is one more question the
   page asks -- and the first one, so it takes a step off the price panel above
   it rather than opening the form flush against it. */
.variant-selector {
  display: grid;
  gap: var(--ds-space-2);
  margin-top: var(--ds-space-3);
}

@media (min-width: 640px) {
  .variant-selector {
    grid-template-columns: 7rem minmax(0, 1fr);
    gap: var(--ds-space-4);
  }

  .variant-selector__label {
    padding-top: var(--ds-space-2);
  }
}

.variant-selector__label,
.product-designer__title {
  font-size: var(--ds-text-sm);
  font-weight: var(--ds-weight-semibold);
  letter-spacing: var(--ds-tracking-wide);
  text-transform: uppercase;
  color: var(--color-text-muted);
  margin: 0;
}

/* Class names here follow _variant_selector.html.erb exactly: __swatches /
   __swatch / __swatch-color / __swatch-label. An earlier pass styled invented
   names (__options / __option), which matched nothing — so the swatch anchors
   were unstyled and each one rendered its variant photo at full width, pushing
   the buy button off the screen. */
.variant-selector__swatches {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ds-space-2);
  list-style: none;
  padding: 0;
  margin: 0;
}

.variant-selector__swatch {
  display: grid;
  place-items: center;
  width: 3.5rem;
  height: 3.5rem;
  overflow: hidden;
  border: 1px solid var(--ds-hairline-strong);
  border-radius: var(--ds-radius-sm);
  background: var(--ds-surface-sunken);
  color: var(--color-text);
  cursor: pointer;
  text-decoration: none;
  transition:
    border-color var(--ds-duration-fast) var(--ds-ease),
    box-shadow var(--ds-duration-fast) var(--ds-ease),
    transform var(--ds-duration-fast) var(--ds-ease);
}

.variant-selector__swatch:hover {
  transform: translateY(-2px);
  border-color: color-mix(in srgb, var(--color-primary) 45%, var(--ds-hairline-strong));
  box-shadow: var(--ds-shadow-sm);
}

/* Ring drawn inside the box so selecting a swatch does not shift the row. */
.variant-selector__swatch.is-active,
.variant-selector__swatch[aria-pressed="true"] {
  border-color: var(--color-primary);
  box-shadow: inset 0 0 0 2px var(--color-primary);
}

/* A colourway with a photo but no stated hex: the picture fills the 3.5rem
   square, like the colour would. Only a COLOUR group reaches this now — every
   other group holding a photo is drawn as the buttons below. */
.variant-selector__swatch-image {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* Picture buttons: the shape a group takes as soon as one option carries a
   photo -- a small thumbnail with the option's name BESIDE it, so the row reads
   as a row of buttons and not as a second product grid competing with the
   gallery. The stacked card it replaced was 6.5rem wide per option and pushed
   the buy row down a whole line on a phone. */
.variant-selector__swatches--cards {
  gap: var(--ds-space-2);
}

.variant-selector__card {
  display: inline-flex;
  align-items: center;
  gap: var(--ds-space-2);
  /* Tight on the image side, roomy on the text side: the thumbnail is already
     an inset block and does not need the padding twice. */
  padding: var(--ds-space-1) var(--ds-space-3) var(--ds-space-1) var(--ds-space-1);
  /* 44px: the same finger target as every other control on this page. */
  min-height: 2.75rem;
  max-width: 100%;
  border: 1px solid var(--ds-hairline-strong);
  border-radius: var(--button-radius, var(--ds-radius-sm));
  background: var(--color-background);
  color: var(--color-text);
  cursor: pointer;
  text-decoration: none;
  transition:
    border-color var(--ds-duration-fast) var(--ds-ease),
    box-shadow var(--ds-duration-fast) var(--ds-ease),
    transform var(--ds-duration-fast) var(--ds-ease);
}

.variant-selector__card:hover {
  transform: translateY(-2px);
  border-color: color-mix(in srgb, var(--color-primary) 45%, var(--ds-hairline-strong));
  box-shadow: var(--ds-shadow-sm);
}

/* Same inside ring as the chip, so picking a card does not shift the row. */
.variant-selector__card.is-active {
  border-color: var(--color-primary);
  box-shadow: inset 0 0 0 2px var(--color-primary);
}

.variant-selector__card-media {
  display: block;
  flex: none;
  width: 2rem;
  height: 2rem;
  overflow: hidden;
  border-radius: var(--ds-radius-xs);
  background: var(--ds-surface-sunken);
}

.variant-selector__card-image {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}

/* A sibling with no media row keeps the tile rather than collapsing the card. */
.variant-selector__card-image--placeholder {
  display: grid;
  place-items: center;
  color: var(--color-text-muted);
}

.variant-selector__card-image--placeholder .hi {
  width: 1rem;
  height: 1rem;
}

/* Names break between WORDS, not inside them: `anywhere` split the option that
   keeps its full title ("Mental Personalizzata", the member with nothing to
   distinguish it) into "Personalizzat / a". */
.variant-selector__card-label {
  font-size: var(--ds-text-sm);
  font-weight: var(--ds-weight-semibold);
  line-height: var(--ds-leading-tight);
  overflow-wrap: break-word;
  min-width: 0;
}

/* Colour chip when the product declares a color_web custom field: the chip
   fills the swatch box; the hex itself is data, painted inline by the partial.
   The inner hairline keeps a white colourway visible on a white surface. */
.variant-selector__swatch-color {
  width: 100%;
  height: 100%;
  display: block;
  box-shadow: inset 0 0 0 1px var(--ds-hairline);
}
/* Fallback when a colourway has no photo: initials rather than an empty box. */
.variant-selector__swatch-label {
  padding: var(--ds-space-1);
  font-size: var(--ds-text-2xs);
  font-weight: var(--ds-weight-semibold);
  line-height: var(--ds-leading-tight);
  text-align: center;
  overflow: hidden;
}

/* An option drawn as WORDS, whatever put it in that state — a package group, a
   size group, or simply a sibling with no colour and no photo. A 3.5rem square
   is the right shape for a colour chip and the wrong one for a label:
   "Confezione da 24PZ" wraps onto three cramped lines inside it. A labelled
   option is a pill that grows to its text instead, and the active one is FILLED
   rather than ringed — with no image to carry the selection, the fill is what
   makes the current choice obvious. The modifier is per OPTION, so a group
   holding both photos and labels draws each one the right way. */
.variant-selector__swatch--label {
  width: auto;
  height: auto;
  min-height: 2.75rem;
  padding: var(--ds-space-2) var(--ds-space-4);
}

.variant-selector__swatch--label .variant-selector__swatch-label {
  font-size: var(--ds-text-sm);
  white-space: nowrap;
  overflow: visible;
}

.variant-selector__swatch--label.is-active {
  background: var(--color-primary);
  color: var(--color-primary-contrast);
  box-shadow: none;
}

.variant-selector__select {
  width: 100%;
  min-height: 2.75rem;
  padding: var(--ds-space-2) var(--ds-space-3);
  border: 1px solid var(--ds-hairline-strong);
  border-radius: var(--ds-radius-sm);
  background: var(--color-background);
  color: var(--color-text);
  font-size: 1rem;
  font-family: inherit;
}

/* ── Size selector ───────────────────────────────────────────────────────── */
/* Radio pills: one tap, the whole row visible, the choice obvious. The native
   radio is visually hidden but stays the accessible control (keyboard + screen
   reader); the label is the target. */
/* One option row, one shape across the shop: the label in its own column with
   the controls beside it from `sm` up, stacked under it on a phone. This is the
   configurator's row, adopted as the standard for every buyable product — a
   shop has one product page, and the questions it asks (which colour, which
   size, which variant) must look the same wherever they are asked. */
.add-to-cart__size {
  display: grid;
  gap: var(--ds-space-2);
}

@media (min-width: 640px) {
  .add-to-cart__size {
    grid-template-columns: 7rem minmax(0, 1fr);
    gap: var(--ds-space-4);
  }

  /* The label reads against the first row of controls, not against the top of a
     block that wraps onto three lines. */
  .add-to-cart__size-label {
    padding-top: var(--ds-space-2);
  }
}

.add-to-cart__size-label {
  font-size: var(--ds-text-sm);
  font-weight: var(--ds-weight-semibold);
  letter-spacing: var(--ds-tracking-wide);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.add-to-cart__size-options {
  display: flex;
  flex-wrap: wrap;
  gap: var(--ds-space-2);
}

.add-to-cart__size-option {
  cursor: pointer;
}

.add-to-cart__size-option input {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Sized like the configurator's pills, which is the row this page now follows:
   a size is a short label, and a wall of nine of them reads better compact than
   at full button height. */
.add-to-cart__size-option > span {
  display: grid;
  place-items: center;
  min-width: 2.5rem;
  min-height: 2.25rem;
  padding-inline: var(--ds-space-3);
  border: 1px solid var(--ds-hairline-strong);
  border-radius: var(--ds-radius-sm);
  background: var(--color-background);
  color: var(--color-text);
  font-size: var(--ds-text-base);
  font-weight: var(--ds-weight-medium);
  font-variant-numeric: tabular-nums;
  transition:
    border-color var(--ds-duration-fast) var(--ds-ease),
    background-color var(--ds-duration-fast) var(--ds-ease),
    box-shadow var(--ds-duration-fast) var(--ds-ease);
}

.add-to-cart__size-option:hover > span {
  border-color: color-mix(in srgb, var(--color-primary) 45%, var(--ds-hairline-strong));
}

.add-to-cart__size-option input:checked + span {
  border-color: var(--color-primary);
  background: var(--ds-tint-primary);
  color: var(--color-primary);
  box-shadow: inset 0 0 0 1px var(--color-primary);
}

.add-to-cart__size-option input:focus-visible + span {
  box-shadow: var(--ds-focus-ring);
}

/* ── Stated attribute (gender) ───────────────────────────────────────────── */
/* A fact the buyer reads, not a question he answers: same row shape as every
   option (label column, values beside it), and the value drawn as the pills
   around it so the row belongs to the block — but flat and sunken, with no
   hover or checked state, because there is nothing here to choose. */
.product-attribute__value {
  justify-self: start;
  display: grid;
  place-items: center;
  min-height: 2.25rem;
  padding-inline: var(--ds-space-3);
  margin: 0;
  border: 1px solid var(--ds-hairline-strong);
  border-radius: var(--ds-radius-sm);
  background: var(--ds-surface-sunken);
  color: var(--color-text);
  font-size: var(--ds-text-sm);
  font-weight: var(--ds-weight-medium);
  letter-spacing: 0.02em;
  text-transform: uppercase;
}

/* ── Option picker (real variants) ───────────────────────────────────────── */
/* Size/colour picker for a product sold through ProductVariant rows. The pills
   reuse the size-option rules above; only the colour chips, the disabled state
   and the availability note are picker-specific. */
/* The picker is a WRAPPER, not a band: `display: contents` hands its two axes
   (colour, size) straight to the buy form, so they sit at the form's own rhythm
   instead of a second, tighter one of their own. The option rows — variant,
   colour, size, documents, gift — then read as one block of questions, which is
   what they are, and there is one gap to change rather than two to keep in step.
   Everything else it holds (the hidden variant field, the JSON payload, the
   price templates, the availability note) is display:none until it speaks. */
.option-picker {
  display: contents;
}

/* A combination that cannot be bought: still visible (the range is part of the
   story) and still CLICKABLE, so picking it answers with words instead of doing
   nothing. WHY it cannot be bought is two different facts and the buyer is
   entitled to both — option_picker#applyStates writes the reason onto the label
   as data-state, the title attribute says it on hover, and the availability
   badge says it in full. */
.add-to-cart__size-option[data-state] > span {
  opacity: 0.45;
  background: var(--color-background);
}

/* Sold out: we sell this one, there is none left. The diagonal strike reads as
   "crossed off the list". */
.add-to-cart__size-option[data-state="sold-out"] > span {
  background:
    linear-gradient(to top right,
      transparent calc(50% - 1px),
      var(--ds-hairline-strong) calc(50% - 1px),
      var(--ds-hairline-strong) calc(50% + 1px),
      transparent calc(50% + 1px)),
    var(--color-background);
}

/* Not available: this combination is not made at all (black comes in S only).
   No strike — nothing was crossed off, it was never on the list. The dashed
   border says "outside the range" without competing with the sold-out mark. */
.add-to-cart__size-option[data-state="unavailable"] > span {
  border-style: dashed;
}

.add-to-cart__size-option[data-state]:hover > span {
  border-color: var(--ds-hairline-strong);
}

/* Swatch AND name inside the shared pill: the admin-entered hex on a small
   square, the colour's own name beside it. Twenty greens are indistinguishable
   as bare chips, and the sold-out strike needs something to cross out. The pill
   frame, the checked state and the two unavailable states all come from the
   .add-to-cart__size-option rules above — this only lays out the contents. */
/* Selector carries the label class on purpose: the shared pill rule is
   `.add-to-cart__size-option > span { display: grid }`, which outranks a lone
   class and stacks the swatch ON TOP of the name. Same weight plus one, so the
   row wins. */
.option-picker__option > .option-picker__color {
  display: flex;
  align-items: center;
  gap: var(--ds-space-2);
  justify-content: flex-start;
  /* One line, always: "BOTTLE GREEN" wrapping under its own swatch turns a row
     of pills into a ragged grid of different heights. */
  flex-wrap: nowrap;
  white-space: nowrap;
}

.option-picker__color-dot {
  flex: none;
  width: 1.5rem;
  height: 1.5rem;
  border: 1px solid var(--ds-hairline-strong);
  border-radius: var(--ds-radius-xs);
}

.option-picker__color-name {
  text-transform: uppercase;
  letter-spacing: 0.02em;
  font-size: var(--ds-text-sm);
  white-space: nowrap;
}

/* The picked colour is filled, not tinted: on a wall of thirty pills a light
   tint reads as "slightly warmer", and the buyer has to hunt for their choice.
   Hover previews the same fill so the target is obvious before the click. */
/* :not([data-state]) on purpose — an unbuyable combination must keep its strike
   and its dashed border under the pointer, so the fill is only ever offered for
   something the buyer can actually have. */
.option-picker__option:not([data-state]):hover > span,
.option-picker__option:not([data-state]) input:checked + span {
  border-color: var(--color-primary);
  background: var(--color-primary);
  color: var(--color-primary-contrast);
  box-shadow: none;
}

/* The swatch keeps its own hex against the filled pill: a white shirt on a dark
   fill still needs an edge. */
.option-picker__option:not([data-state]):hover .option-picker__color-dot,
.option-picker__option:not([data-state]) input:checked + span .option-picker__color-dot {
  border-color: var(--color-primary-contrast);
}

.option-picker__availability {
  font-size: var(--ds-text-sm);
  color: var(--color-text-muted);
}

.option-picker__availability--out {
  color: var(--color-danger);
  font-weight: var(--ds-weight-medium);
}

/* ── Size grid (b2b multi-line buy) ──────────────────────────────────────── */
/* The colour x size quantity matrix that replaces the picker on the b2b
   checkout profile [T-07]. A wholesale reorder is a spreadsheet gesture, so it
   gets a table: one axis per variant axis, one box per purchasable row, one
   submit. Wide size ranges scroll INSIDE the grid — the product page must never
   scroll sideways. */
.size-grid {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-2);
}

.size-grid__hint {
  font-size: var(--ds-text-sm);
  color: var(--color-text-muted);
}

.size-grid__scroll {
  overflow-x: auto;
  border: 1px solid var(--ds-hairline);
  border-radius: var(--ds-radius-sm);
}

.size-grid__table {
  width: 100%;
  border-collapse: collapse;
  font-size: var(--ds-text-sm);
}

.size-grid__table th,
.size-grid__table td {
  padding: var(--ds-space-2);
  border-bottom: 1px solid var(--ds-hairline);
  text-align: center;
  white-space: nowrap;
}

.size-grid__table tbody tr:last-child th,
.size-grid__table tbody tr:last-child td {
  border-bottom: 0;
}

.size-grid__table thead th {
  font-size: var(--ds-text-xs);
  font-weight: var(--ds-weight-semibold);
  letter-spacing: var(--ds-tracking-wide);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

/* The axis column stays readable while the sizes scroll past it. */
.size-grid__axis {
  position: sticky;
  left: 0;
  z-index: 1;
  background: var(--color-background);
  text-align: left;
  font-weight: var(--ds-weight-medium);
}

.size-grid__cell {
  vertical-align: top;
}

.size-grid__input {
  width: 4rem;
  min-height: 2.75rem;
  padding: var(--ds-space-1) var(--ds-space-2);
  border: 1px solid var(--ds-hairline-strong);
  border-radius: var(--ds-radius-sm);
  background: var(--color-background);
  color: var(--color-text);
  font-family: inherit;
  font-size: var(--ds-text-base);
  font-variant-numeric: tabular-nums;
  text-align: center;
}

.size-grid__input:focus-visible {
  border-color: var(--color-primary);
  box-shadow: var(--ds-focus-ring);
}

.size-grid__stock,
.size-grid__price {
  display: block;
  margin-top: var(--ds-space-1);
  font-size: var(--ds-text-xs);
  color: var(--color-text-muted);
  font-variant-numeric: tabular-nums;
}

/* A combination that cannot be bought stays visible: the range is part of what
   the buyer needs to see. Same "sold out" diagonal the picker uses. */
.size-grid__empty {
  display: grid;
  place-items: center;
  width: 4rem;
  min-height: 2.75rem;
  margin-inline: auto;
  border: 1px solid var(--ds-hairline);
  border-radius: var(--ds-radius-sm);
  background:
    linear-gradient(to top right,
      transparent calc(50% - 1px),
      var(--ds-hairline-strong) calc(50% - 1px),
      var(--ds-hairline-strong) calc(50% + 1px),
      transparent calc(50% + 1px)),
    var(--color-background);
}

.size-grid__empty-text {
  position: absolute;
  width: 1px;
  height: 1px;
  padding: 0;
  margin: -1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.size-grid__total {
  font-size: var(--ds-text-sm);
  font-weight: var(--ds-weight-medium);
  color: var(--color-text);
  font-variant-numeric: tabular-nums;
}

/* ── Add to cart ─────────────────────────────────────────────────────────── */

/* The form owns the rhythm between its own parts (variants, designer, stepper,
   buttons). Without this they butted straight into each other — the stepper sat
   flush on top of the buy button, reading as one welded control. */
.add-to-cart {
  display: flex;
  flex-direction: column;
  align-items: stretch;
  gap: var(--ds-space-6);
}

/* Grid-card mode: one button, nothing to space. It also runs at card width
   (~16rem at four columns), where the detail page's 1rem label and 1.5rem
   padding wrap "Add to cart" onto two lines. */
.add-to-cart--compact {
  gap: 0;
}

.add-to-cart--compact .add-to-cart__submit {
  min-height: 2.75rem;
  padding-inline: var(--ds-space-3);
  font-size: var(--ds-text-base);
}

.add-to-cart__quantity {
  align-self: flex-start;
  display: inline-flex;
  align-items: stretch;
  border: 1px solid var(--ds-hairline-strong);
  border-radius: var(--ds-radius-sm);
  overflow: hidden;
  background: var(--color-background);
}

.add-to-cart__step {
  /* 44px: the smallest a finger reliably hits. */
  min-width: 2.75rem;
  min-height: 2.75rem;
  display: grid;
  place-items: center;
  background: transparent;
  border: 0;
  color: var(--color-text-muted);
  cursor: pointer;
  transition:
    background-color var(--ds-duration-fast) var(--ds-ease),
    color var(--ds-duration-fast) var(--ds-ease);
}

.add-to-cart__step:hover {
  background: var(--ds-tint-hover);
  color: var(--color-text);
}

.add-to-cart__step:active {
  background: var(--ds-tint-active);
}

.add-to-cart__step .hi {
  width: 1rem;
  height: 1rem;
}

.add-to-cart__quantity-input {
  /* Wide enough for a typed three-digit reorder, not just the stepper's 1-9. */
  width: 5rem;
  min-height: 2.75rem;
  border: 0;
  border-inline: 1px solid var(--ds-hairline);
  background: transparent;
  text-align: center;
  color: var(--color-text);
  /* 16px: anything smaller and iOS zooms the page on focus. */
  font-size: 1rem;
  font-weight: var(--ds-weight-semibold);
  font-family: inherit;
  font-variant-numeric: tabular-nums;
}

.add-to-cart__quantity-input::-webkit-outer-spin-button,
.add-to-cart__quantity-input::-webkit-inner-spin-button {
  appearance: none;
  margin: 0;
}

/* Quantity, price and the impulse button on one line — the configurator's buy
   row. It wraps rather than squeezes: where the column is narrow the button
   takes the next line at full width. */
.add-to-cart__buy {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--ds-space-3);
}

.add-to-cart__price {
  margin-left: auto;
  white-space: nowrap;
}

/* The row's price is a figure beside the controls, not the page headline the
   product title sits under. */
.add-to-cart__price .price__final {
  font-size: var(--ds-text-lg);
  font-weight: var(--ds-weight-bold);
}

.add-to-cart__price .price__vat {
  display: none;
}

.add-to-cart__buy .add-to-cart__submit {
  width: auto;
  flex: 1 1 12rem;
}

.add-to-cart__actions {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-3);
}

/* The two buy buttons are ONE decision in two flavours -- "buy now" over "add
   to cart" -- so they sit as a pair rather than at the form's full step. The
   pull-up is written against the two tokens it reconciles: change the form gap
   and this still lands on --ds-space-3.
   
   Scoped to a DIRECT child of the buy form, because it only cancels THAT form's
   gap. The personalizer reuses this block inside its own form, which is a plain
   block with no gap to cancel: applied there the negative margin dragged "add to
   cart" up under the sticky buy row and the two overlapped. */
.add-to-cart > .add-to-cart__actions {
  margin-top: calc(var(--ds-space-3) - var(--ds-space-8));
}

.add-to-cart__submit {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--ds-space-2);
  width: 100%;
  min-height: 3rem;
  padding-inline: var(--ds-space-6);
  border: 1px solid transparent;
  border-radius: var(--button-radius, var(--ds-radius-sm));
  font-size: var(--ds-text-md);
  font-weight: var(--ds-weight-semibold);
  letter-spacing: var(--ds-tracking-tight);
  cursor: pointer;
  font-family: inherit;
  transition:
    transform var(--ds-duration-fast) var(--ds-ease),
    box-shadow var(--ds-duration-base) var(--ds-ease),
    background-color var(--ds-duration-fast) var(--ds-ease),
    border-color var(--ds-duration-fast) var(--ds-ease),
    color var(--ds-duration-fast) var(--ds-ease);
}

.add-to-cart__submit--primary {
  /* Solid primary, matching .btn-primary (the hero's "Scopri la collezione") so
   * every primary button on the store reads as the same button. It used to be a
   * gradient with a sheen, which set it visibly apart from the rest. */
  background: var(--color-primary);
  color: var(--color-primary-contrast);
  box-shadow: var(--ds-shadow-sm);
}

.add-to-cart__submit--primary:hover {
  transform: translateY(-1px);
  background: var(--color-primary-hover);
  box-shadow: var(--ds-shadow-md);
}

.add-to-cart__submit--primary:active {
  transform: translateY(0);
  box-shadow: var(--ds-shadow-xs);
}

.add-to-cart__submit--secondary {
  background: var(--color-background);
  border-color: var(--ds-hairline-strong);
  color: var(--color-text);
}

.add-to-cart__submit--secondary:hover {
  border-color: var(--color-primary);
  background: var(--ds-tint-primary);
  color: var(--color-primary);
}

.add-to-cart__submit:disabled {
  opacity: 0.5;
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
}

.add-to-cart__flash {
  font-size: var(--ds-text-sm);
  color: var(--color-text-muted);
}

.add-to-cart__flash:empty {
  display: none;
}

/* ── Product designer ────────────────────────────────────────────────────── */

.product-designer {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-4);
  border: 1px solid var(--ds-hairline);
  border-radius: var(--ds-radius-md);
  padding: var(--ds-space-5);
  background: var(--ds-gradient-surface);
}

.product-designer:empty {
  display: none;
}

.product-designer__field {
  display: flex;
  flex-direction: column;
  gap: var(--ds-space-2);
}

.product-designer__field--inline {
  flex-direction: row;
  align-items: center;
  gap: var(--ds-space-3);
  cursor: pointer;
}

.product-designer__label {
  font-size: var(--ds-text-sm);
  font-weight: var(--ds-weight-medium);
  color: var(--color-text);
}

.product-designer__input,
.product-designer__select,
.product-designer__file {
  width: 100%;
  min-height: 2.75rem;
  padding: var(--ds-space-2) var(--ds-space-3);
  border: 1px solid var(--ds-hairline-strong);
  border-radius: var(--ds-radius-sm);
  background: var(--color-background);
  color: var(--color-text);
  font-size: 1rem;
  font-family: inherit;
  transition:
    border-color var(--ds-duration-fast) var(--ds-ease),
    box-shadow var(--ds-duration-fast) var(--ds-ease);
}

.product-designer__input:focus,
.product-designer__select:focus {
  outline: none;
  border-color: var(--color-primary);
  box-shadow: 0 0 0 3px var(--ds-tint-primary-strong);
}

.product-designer__file::file-selector-button {
  margin-inline-end: var(--ds-space-3);
  padding: var(--ds-space-2) var(--ds-space-3);
  border: 1px solid var(--ds-hairline-strong);
  border-radius: var(--ds-radius-xs);
  background: var(--color-background);
  color: var(--color-text);
  font-family: inherit;
  font-size: var(--ds-text-sm);
  font-weight: var(--ds-weight-medium);
  cursor: pointer;
}

.product-designer__checkbox {
  width: 1.15rem;
  height: 1.15rem;
  accent-color: var(--color-primary);
  flex-shrink: 0;
}

.product-designer__preview {
  padding: var(--ds-space-4);
  border: 1px dashed var(--ds-hairline-strong);
  border-radius: var(--ds-radius-sm);
  min-height: 4rem;
}

.product-designer__preview:empty,
.product-designer__preview[aria-hidden="true"]:not(:has(*:not(:empty))) {
  display: none;
}

.product-designer__preview-images {
  display: flex;
  gap: var(--ds-space-2);
  flex-wrap: wrap;
}

.product-designer__preview-images img {
  width: 3.5rem;
  height: 3.5rem;
  object-fit: cover;
  border-radius: var(--ds-radius-xs);
}

/* ── Focus ───────────────────────────────────────────────────────────────── */

/* Never remove the ring without giving one back: keyboard users navigate by it.
   Drawn as a box-shadow so it follows each control's own border-radius. */
.product-card__title a:focus-visible,
.product-card__cta:focus-visible,
.product-listing__sort select:focus-visible,
.product-gallery__thumb:focus-visible,
.variant-selector__option:focus-visible,
.add-to-cart__step:focus-visible,
.add-to-cart__quantity-input:focus-visible,
.add-to-cart__submit:focus-visible,
.product-designer__checkbox:focus-visible,
.product-designer__file:focus-visible,
.wishlist-button__toggle:focus-visible,
.filter-sidebar__submit:focus-visible,
.filter-sidebar__clear:focus-visible,
/* The radio is the focusable node, but the row is what a keyboard user sees
   move, so the ring is drawn on the row. */
.filter-sidebar__option:has(.filter-sidebar__radio:focus-visible),
.pagination-bar a:focus-visible,
.cart-addons input:focus-visible {
  outline: none;
  box-shadow: var(--ds-focus-ring);
}

/* The stretched link's ring is drawn on the card, so hide its own. */
.product-card__title a:focus-visible {
  box-shadow: none;
  outline: none;
}

/* ── Reduced motion ──────────────────────────────────────────────────────
   _design_system.css already collapses every duration globally. What remains is
   the transforms, which are instant rather than absent under that rule and so
   must be suppressed by hand. */
@media (prefers-reduced-motion: reduce) {
  .product-card:hover,
  .product-card:hover .product-card__image,
  .product-gallery__thumb:hover,
  .wishlist-button__toggle:hover,
  .add-to-cart__submit--primary:hover,
  .product-card__cta:hover .hi {
    transform: none;
  }
}

/* ── Blank media placeholder ─────────────────────────────────────────────────
 * The intentional empty state for a missing image (Frontend::AttachmentHelper
 * #blank_media_placeholder). Replaces the grey "NO IMAGE" JPEG that used to fill
 * every card and hero on a catalogue without photography. A soft token-tinted
 * surface with a faint centred icon: reads as "no photo yet", not as broken.
 * Fills whatever slot the caller sized. */
.blank-media {
  display: flex;
  align-items: center;
  justify-content: center;
  width: 100%;
  height: 100%;
  min-height: 100%;
  background:
    linear-gradient(135deg,
      color-mix(in srgb, var(--color-secondary) 6%, var(--color-surface)) 0%,
      color-mix(in srgb, var(--color-secondary) 12%, var(--color-surface)) 100%);
  color: color-mix(in srgb, var(--color-text) 22%, transparent);
}

.blank-media__icon {
  width: clamp(1.75rem, 6%, 3rem);
  height: auto;
}

/* When the slot has no intrinsic height (a bare grid cell), give the
 * placeholder a sensible square so it does not collapse to the icon. */
.blank-media:only-child {
  aspect-ratio: 1 / 1;
}

/* ── Cart count badge ────────────────────────────────────────────────────────
 * The header cart icon's item counter. The class existed with no rule, so the
 * count rendered as a bare number floating beside the bag — hard to read as a
 * cart badge. A small filled disc in the primary colour, pinned to the top-right
 * of the icon, the way every storefront shows it. */
.frontend-cart-badge {
  position: absolute;
  top: -0.5rem;
  right: -0.6rem;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 1.15rem;
  height: 1.15rem;
  padding: 0 0.3rem;
  border-radius: 999px;
  /* Visible on the header whatever its colour: the badge takes the menu's own
   * text colour as its fill (white on a dark header, dark on a light one) and
   * inverts the number against it, so a dark primary on a dark menu can never
   * make it disappear. */
  background: var(--frontend-menu-text, var(--color-primary));
  color: var(--frontend-menu-surface, var(--color-on-primary));
  font-size: 0.7rem;
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  box-shadow: 0 0 0 2px var(--frontend-menu-dark-background, var(--color-background));
}

.frontend-cart-badge[hidden] { display: none; }

/* ── Product share ──────────────────────────────────────────────────────── */
/* Share block, right under the buy buttons: WhatsApp is the loud option (the
   owner's customers share by chat), email and copy-link are the quiet ones. */
.product-detail__share {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--ds-space-3);
  /* Off the buy buttons: sharing is what the visitor does INSTEAD of buying
     now, so it does not sit at the column's tight gap under the CTA it would
     otherwise read as a third step of. */
  margin-top: var(--ds-space-3);
}
.product-detail__share-whatsapp {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--ds-space-2);
  flex: 1;
  min-height: 2.75rem;
  padding-inline: var(--ds-space-5);
  border: 1px solid var(--ds-hairline-strong);
  border-radius: var(--button-radius, var(--ds-radius-sm));
  font-size: var(--ds-text-sm);
  font-weight: var(--ds-weight-semibold);
  color: var(--color-text);
  text-decoration: none;
  transition: background-color var(--ds-duration-fast) var(--ds-ease),
              border-color var(--ds-duration-fast) var(--ds-ease);
}
/* WhatsApp brand green on the glyph only, never as a theme token. */
.product-detail__share-whatsapp .bi--whatsapp {
  color: #25d366;
  flex: none;
}
.product-detail__share-whatsapp:hover {
  background: var(--ds-tint-hover);
  border-color: color-mix(in srgb, var(--color-primary) 45%, var(--ds-hairline-strong));
}
.product-detail__share-channels {
  display: inline-flex;
  align-items: center;
  gap: var(--ds-space-2);
}
.product-detail__share-link {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  padding: 0;
  border-radius: 999px;
  border: 1px solid var(--ds-hairline-strong);
  background: transparent;
  cursor: pointer;
  color: var(--color-text);
  transition: background-color var(--ds-duration-fast) var(--ds-ease),
              color var(--ds-duration-fast) var(--ds-ease);
}
.product-detail__share-link:hover {
  background: var(--color-primary);
  color: var(--color-primary-contrast);
  border-color: var(--color-primary);
}
/* Copy-link feedback: share_copy_controller flips is-copied for a moment. */
.product-detail__share-link .product-detail__share-idle,
.product-detail__share-link .product-detail__share-copied {
  display: inline-flex;
}
.product-detail__share-link .product-detail__share-copied {
  display: none;
}
.product-detail__share-link.is-copied .product-detail__share-idle {
  display: none;
}
.product-detail__share-link.is-copied .product-detail__share-copied {
  display: inline-flex;
  color: var(--color-success, currentColor);
}

/* ── Floating cart (menu-less pages) ─────────────────────────────────────────
 * Landing pages hide the header, which is where the cart lives. The same
 * mini_cart component is pinned top-right instead, so a chrome-less page still
 * has the badge, the drawer, and a way into the cart. Sits under the drawer
 * (z-index 60) and clear of the sticky buy bar (z-index 40). */
.frontend-floating-cart {
  position: fixed;
  top: 1rem;
  right: 1rem;
  z-index: 50;
  display: flex;
  align-items: center;
  justify-content: center;
  width: 3rem;
  height: 3rem;
  border-radius: 9999px;
  background: var(--color-surface, #fff);
  box-shadow: 0 6px 20px color-mix(in srgb, var(--color-text) 18%, transparent);
}

/* The button inherits the header's menu-link colours, which no longer exist on
   a page without a header: repaint it against the floating surface. */
.frontend-floating-cart button {
  color: var(--color-text);
}

/* ── Mini-cart drawer ────────────────────────────────────────────────────────
 * The add-to-cart feedback: a right-side slide-over, not the old top-right
 * dropdown that was easy to miss. Opens on a successful add (mini-cart
 * controller toggles `hidden`); the panel slides in from the edge each time it
 * is shown, over a dimmed page. */
.mini-cart-drawer {
  position: fixed;
  inset: 0;
  z-index: 60;
}
.mini-cart-drawer.hidden { display: none; }

.mini-cart-drawer__backdrop {
  position: absolute;
  inset: 0;
  background: color-mix(in srgb, var(--color-text) 45%, transparent);
  animation: mini-cart-fade var(--ds-duration-base, 0.25s) var(--ds-ease, ease);
}

/* One deliberate geometry: a full-height drawer pinned to the right edge. The
   panel is the only surface (the body inside it is transparent), so the drawer
   never reads as a floating card sitting on a white slab. */
.mini-cart-drawer__panel {
  position: absolute;
  top: 0;
  right: 0;
  height: 100%;
  width: min(24rem, 100vw);
  display: flex;
  flex-direction: column;
  background: var(--color-background, #fff);
  box-shadow: var(--ds-shadow-xl, 0 20px 40px rgba(0,0,0,.2));
  animation: mini-cart-slide var(--ds-duration-base, 0.25s) var(--ds-ease, ease);
}

.mini-cart-drawer__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  flex: 0 0 auto;
  padding: 1rem;
  border-bottom: 1px solid var(--ds-hairline, rgba(0, 0, 0, 0.1));
}

.mini-cart-drawer__title {
  margin: 0;
  font-size: 1rem;
  font-weight: 600;
  color: var(--color-text);
}

.mini-cart-drawer__close {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 2.25rem;
  height: 2.25rem;
  border-radius: 9999px;
  color: var(--color-text);
  background: transparent;
  cursor: pointer;
  transition: background-color var(--ds-duration-fast, 0.15s) var(--ds-ease, ease);
}
.mini-cart-drawer__close:hover {
  background: color-mix(in srgb, var(--color-text) 8%, transparent);
}
.mini-cart-drawer__close:focus-visible {
  outline: 2px solid var(--color-primary, currentColor);
  outline-offset: 2px;
}

/* The Turbo Stream target. It fills what is left under the header, and its item
   list is the part that scrolls, so the total and the actions stay in view. */
.mini-cart-drawer__body {
  display: flex;
  flex-direction: column;
  flex: 1 1 auto;
  min-height: 0;
  padding: 1rem;
}

.mini-cart-drawer__items {
  flex: 1 1 auto;
  min-height: 0;
  overflow-y: auto;
}

@keyframes mini-cart-fade { from { opacity: 0 } to { opacity: 1 } }
@keyframes mini-cart-slide { from { transform: translateX(100%) } to { transform: translateX(0) } }

/* ── Checkout ────────────────────────────────────────────────────────────── */

/* Turbo frames are custom elements and browsers lay unknown elements out
   inline, where vertical margins do not apply. The one-page checkout stacks
   its contact and address blocks as frames inside a space-y column, so the
   rhythm utilities silently skipped them and the blocks crowded whatever
   followed. Scoped to the checkout column: frames used inline elsewhere
   (buttons, badges) stay inline. */
.checkout-sections > turbo-frame {
  display: block;
}

/* The new-shipping-address panel on the checkout. An overlay rather than a
   native <dialog>: showModal() paints in the top layer, above everything, and
   searchable-select renders its option lists into document.body — inside a
   dialog every picker in the panel (country, province, city, dialling code)
   would open behind it. At --ds-z-modal the panel sits over the page and under
   those lists, so they stay usable.

   The scrim is mixed from the text colour, like the mini-cart drawer's, so it
   dims with the theme instead of stating a colour of its own. */
.checkout-address-modal {
  position: fixed;
  inset: 0;
  z-index: var(--ds-z-modal);
  display: none;
  align-items: flex-start;
  justify-content: center;
  overflow-y: auto;
  padding: 1rem;
  background: color-mix(in srgb, var(--color-text) 45%, transparent);
}

.checkout-address-modal:not(.hidden) {
  display: flex;
}

@media (min-width: 640px) {
  .checkout-address-modal {
    align-items: center;
    padding: 1.5rem;
  }
}

/* ── Product documents ───────────────────────────────────────────────────── */

/* Files the shop attached to the product (a technical sheet, a care card). Same
   option-row shape as the size and colour axes: one label column, the content
   beside it, so the buy column keeps one grammar. */
.product-detail__documents {
  display: grid;
  gap: var(--ds-space-2);
}

@media (min-width: 640px) {
  .product-detail__documents {
    grid-template-columns: 7rem minmax(0, 1fr);
    gap: var(--ds-space-4);
  }

  .product-detail__documents-label {
    padding-top: var(--ds-space-2);
  }
}

.product-detail__documents-label {
  font-size: var(--ds-text-sm);
  font-weight: var(--ds-weight-semibold);
  letter-spacing: var(--ds-tracking-wide);
  text-transform: uppercase;
  color: var(--color-text-muted);
}

.product-detail__documents-list {
  display: grid;
  gap: var(--ds-space-2);
  list-style: none;
  margin: 0;
  padding: 0;
}

.product-detail__document {
  display: inline-flex;
  align-items: center;
  gap: var(--ds-space-2);
  min-height: 2.75rem;
  padding: var(--ds-space-2) var(--ds-space-3);
  border: 1px solid var(--ds-hairline-strong);
  border-radius: var(--button-radius, var(--ds-radius-sm));
  color: var(--color-text);
  font-size: var(--ds-text-sm);
  font-weight: var(--ds-weight-medium);
  text-decoration: none;
  transition:
    border-color var(--ds-duration-fast) var(--ds-ease),
    background-color var(--ds-duration-fast) var(--ds-ease);
}

.product-detail__document:hover {
  background: var(--ds-tint-hover);
  border-color: color-mix(in srgb, var(--color-primary) 45%, var(--ds-hairline-strong));
}

.product-detail__document .hi {
  flex: none;
  color: var(--color-primary);
}

.product-detail__document-name {
  min-width: 0;
  overflow-wrap: break-word;
}

/* The extension, not the byte size: what the buyer needs to know is what the
   link is about to open. */
.product-detail__document-kind {
  flex: none;
  font-size: var(--ds-text-2xs);
  font-weight: var(--ds-weight-semibold);
  letter-spacing: var(--ds-tracking-wide);
  color: var(--color-text-muted);
}

/* ── Gift box row (plain buy form) ───────────────────────────────────────── */

/* The row is an option axis like size and colour, so it reuses their shape; only
   the control differs. The configurator has its own copy of this row inside
   fashion_personalizer and is untouched by these rules. */
.add-to-cart__gift-choice {
  display: flex;
  align-items: center;
  gap: var(--ds-space-2);
  min-height: 2.75rem;
}

.add-to-cart__gift-option {
  display: inline-flex;
  align-items: center;
  gap: var(--ds-space-2);
  cursor: pointer;
  font-size: var(--ds-text-sm);
  color: var(--color-text);
}

.add-to-cart__gift-option input {
  width: 1rem;
  height: 1rem;
  cursor: pointer;
  accent-color: var(--color-primary);
}

/* CSS-only hint: the icon holds the note, shown on hover and on keyboard focus
   so it is not mouse-only. */
.add-to-cart__gift-info {
  position: relative;
  display: inline-flex;
  color: var(--color-text-muted);
  cursor: help;
}

.add-to-cart__gift-hint {
  position: absolute;
  bottom: 100%;
  left: 50%;
  z-index: 10;
  width: 15rem;
  margin-bottom: var(--ds-space-2);
  padding: var(--ds-space-2) var(--ds-space-3);
  transform: translateX(-50%);
  border-radius: var(--ds-radius-sm);
  background: var(--color-primary);
  color: var(--color-primary-contrast);
  font-size: var(--ds-text-xs);
  line-height: var(--ds-leading-snug);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--ds-duration-fast) var(--ds-ease);
}

.add-to-cart__gift-info:hover .add-to-cart__gift-hint,
.add-to-cart__gift-info:focus-visible .add-to-cart__gift-hint {
  opacity: 1;
  visibility: visible;
}

turbo-frame.subpages-preview-page-frame[src]:not([complete]) {
  display: block;
  grid-column: 1 / -1;
  min-height: 4rem;
  width: 100%;
}

turbo-frame.subpages-preview-page-frame[complete],
turbo-frame.subpages-preview-page-frame:not([src]) {
  display: contents;
}

/* Swal dialog, the parts the storefront's own typography would otherwise fight.
 *
 * The font preset sets `h1..h6 { font-family: var(--font-display) }` and
 * `p, li, a, … { font-family: var(--font-body) }`. A Swal title is an <h2>, so a
 * link inside it (the reorder dialog links the word "carrello" to the cart page)
 * dropped out of the display font mid-sentence and read as a different typeface
 * pasted into the heading. Here the link inherits the sentence it sits in and is
 * coloured like a link, nothing more.
 *
 * Specificity, not order: sweetalert2.css is injected on demand and therefore
 * lands after this bundle, but its own rules are written with :where() (zero
 * specificity) and the preset's are element selectors, so `.swal2-title a`
 * (0,1,1) wins over both wherever it is loaded.
 */
.swal2-title a,
.swal2-html-container a {
  font-family: inherit;
  font-weight: inherit;
  font-style: inherit;
  /* The storefront's link colour, same as every other inline link on the
     account pages (`text-[LinkText]`). */
  color: LinkText;
  text-decoration: underline;
  text-underline-offset: 2px;
}

.swal2-title a:hover,
.swal2-html-container a:hover {
  opacity: 0.8;
}

/* Testimonials module — social proof cards, carousel and spotlight.
 *
 * Composes the storefront design system (--ds-* tokens, _design_system.css):
 * hairlines, surfaces, radii, type scale and shadows all come from there, so
 * a client palette re-skins the module without a rule here changing.
 *
 * Every rule is scoped with [data-module="testimonials"] (see
 * docs/guides/MODULE_CSS_GUIDE.md). Fluid spacing uses clamp() calibrated at
 * ~690px and ~1440px like the hero module.
 */

/* Header-to-quotes gap grows with the viewport. */
[data-module="testimonials"] .testimonials-header {
  margin-bottom: clamp(2rem, 1.5rem + 2vw, 3.5rem);
}

[data-module="testimonials"] .testimonials-grid {
  gap: clamp(1rem, 0.75rem + 1vw, 1.75rem);
}

/* ── Card ─────────────────────────────────────────────────────────────────
   Explicit surface + text pair: the card stays legible when the shared
   backgrounds group paints the section dark, because it never inherits the
   section's inverted text colour. */
[data-module="testimonials"] .testimonial-card {
  padding: clamp(1.25rem, 1rem + 1vw, 2rem);
  color: var(--color-text);
  background: var(--ds-surface);
  border: 1px solid var(--ds-hairline);
  border-radius: var(--ds-radius-lg);
  box-shadow: var(--ds-shadow-xs);
  transition: box-shadow var(--ds-duration-base) var(--ds-ease);
}

[data-module="testimonials"] .testimonial-card:hover {
  box-shadow: var(--ds-shadow-sm);
}

[data-module="testimonials"] .testimonial-quote {
  margin-top: clamp(0.75rem, 0.6rem + 0.5vw, 1.125rem);
  font-size: var(--ds-text-base);
  line-height: var(--ds-leading-relaxed);
}

/* Opening quote glyph — drawn, not typed, so it never reaches the i18n copy. */
[data-module="testimonials"] .testimonial-quote::before {
  content: "\201C";
  display: block;
  height: 0.6em;
  font-size: 2.25em;
  line-height: 1;
  color: var(--color-primary);
  opacity: 0.35;
}

/* ── Author row ──────────────────────────────────────────────────────────── */
[data-module="testimonials"] .testimonial-author {
  gap: 0.75rem;
  margin-top: clamp(1.25rem, 1rem + 0.75vw, 1.75rem);
}

[data-module="testimonials"] .testimonial-avatar {
  width: 3rem;
  height: 3rem;
  object-fit: cover;
  border-radius: var(--ds-radius-full);
  border: 1px solid var(--ds-hairline-strong);
  flex-shrink: 0;
}

[data-module="testimonials"] .testimonial-author-name {
  font-size: var(--ds-text-base);
  font-style: normal; /* cite defaults to italic */
  font-weight: var(--ds-weight-semibold);
}

/* Opacity instead of a fixed grey: stays readable over dark sections too. */
[data-module="testimonials"] .testimonial-author-role {
  font-size: var(--ds-text-sm);
  opacity: 0.7;
}

/* ── Rating stars ────────────────────────────────────────────────────────── */
[data-module="testimonials"] .testimonial-rating {
  display: inline-flex;
  gap: 0.125rem;
}

[data-module="testimonials"] .testimonial-star {
  color: var(--ds-hairline-strong);
}

[data-module="testimonials"] .testimonial-star--filled {
  color: var(--color-primary);
}

/* ── Spotlight ───────────────────────────────────────────────────────────
   No card surface: the quote sits directly on the section background and
   inherits its text colour, so it is dark-background friendly by default. */
[data-module="testimonials"] .testimonial-spotlight-item {
  padding: clamp(0.5rem, 0.25rem + 1vw, 1.5rem) clamp(2.5rem, 2rem + 2vw, 4rem);
}

[data-module="testimonials"] .testimonial-spotlight-quote {
  max-width: 46ch;
  margin-top: clamp(1rem, 0.75rem + 1vw, 1.75rem);
  font-size: var(--ds-text-xl);
  line-height: var(--ds-leading-snug);
  letter-spacing: var(--ds-tracking-tight);
  font-weight: var(--ds-weight-medium);
  text-wrap: balance;
}

[data-module="testimonials"] .testimonial-author--spotlight {
  gap: 0.5rem;
  margin-top: clamp(1.5rem, 1.125rem + 1.5vw, 2.5rem);
}

[data-module="testimonials"] .testimonial-avatar--spotlight {
  width: 4rem;
  height: 4rem;
}

/* The spotlight arrows sit inside the side padding reserved above; keep them
   visible without hover on touch-less precision (the shared .carousel__arrow
   fades in on hover — a lone big quote gives no affordance otherwise). */
[data-module="testimonials"] .testimonials-spotlight .carousel__arrow {
  opacity: 1;
  transform: translateY(-50%) scale(1);
}

.frontend-main-menu {
  --frontend-menu-dark-background: var(--color-primary);
  --frontend-menu-logo-height: 3.65rem;
  --frontend-menu-surface: var(--color-primary) !important;
  --frontend-menu-shell-background: var(--frontend-menu-surface) !important;
  --frontend-menu-mobile-background: var(--frontend-menu-surface) !important;
  --frontend-menu-surface-hover: var(--color-primary) !important;
  --frontend-menu-border: transparent !important;
  --frontend-menu-shell-shadow: none !important;
  color: var(--frontend-menu-text);
  position: relative;
  transition: color 0.2s ease;
  z-index: 1000;
}

.frontend-main-menu__shell {
  background: var(--frontend-menu-shell-background);
  box-shadow: var(--frontend-menu-shell-shadow);
  border-bottom: 1px solid var(--frontend-menu-border);
  transition: background-color 0.2s ease, box-shadow 0.2s ease, border-color 0.2s ease, transform 0.2s ease;
}

.frontend-main-menu__topbar {
  background: var(--frontend-menu-shell-background);
  border-bottom: 1px solid var(--frontend-menu-border);
  transition: background-color 0.2s ease, border-color 0.2s ease;
}

.frontend-main-menu__announcement {
  background: var(--frontend-menu-shell-background);
  border-top: 1px solid var(--frontend-menu-border);
  border-bottom: 1px solid var(--frontend-menu-border);
  transition: background-color 0.2s ease, border-color 0.2s ease;
}

.frontend-main-menu__announcement-body {
  font-size: 0.875rem;
  line-height: 1.5;
}

.frontend-main-menu__announcement-body a {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: 0.2em;
}

.frontend-main-menu__topbar-inner {
  flex-wrap: wrap;
}

.frontend-main-menu__burger {
  color: var(--frontend-menu-text);
}

.frontend-main-menu__burger-icon {
  display: inline-flex;
  flex-direction: column;
  gap: 0.3rem;
  width: 1.45rem;
}

.frontend-main-menu__burger-line {
  background: currentColor;
  border-radius: 999px;
  display: block;
  height: 2px;
  transform-origin: center;
  transition: opacity 0.18s ease, transform 0.22s ease;
  width: 100%;
}

.frontend-main-menu__mobile-nav--open .frontend-main-menu__burger-line:first-child {
  transform: translateY(0.42rem) rotate(45deg);
}

.frontend-main-menu__mobile-nav--open .frontend-main-menu__burger-line:nth-child(2) {
  opacity: 0;
  transform: scaleX(0);
}

.frontend-main-menu__mobile-nav--open .frontend-main-menu__burger-line:last-child {
  transform: translateY(-0.42rem) rotate(-45deg);
}

.frontend-main-menu__mobile-backdrop {
  background: color-mix(in srgb, black 42%, transparent);
  bottom: 0;
  left: 0;
  opacity: 0;
  pointer-events: none;
  position: fixed;
  right: 0;
  top: 7.7rem;
  transition: opacity 0.22s ease, visibility 0.22s ease;
  visibility: hidden;
  z-index: 1000;
}

.frontend-main-menu__mobile-panel {
  background: var(--frontend-menu-mobile-background);
  box-shadow: 24px 0 48px color-mix(in srgb, black 16%, transparent);
  display: block;
  height: calc(100vh - 6.5rem);
  height: calc(100dvh - 6.5rem);
  left: 0;
  max-width: none;
  opacity: 0;
  overflow-y: auto;
  overscroll-behavior: contain;
  pointer-events: none;
  position: fixed;
  top: 7.7rem;
  transform: translateX(-100%);
  transition: opacity 0.22s ease, transform 0.22s ease, visibility 0.22s ease;
  visibility: hidden;
  width: 100vw;
  z-index: 1001;
  -webkit-overflow-scrolling: touch;
}

details.frontend-main-menu__mobile-nav:not([open]) .frontend-main-menu__mobile-backdrop,
details.frontend-main-menu__mobile-nav:not([open]) .frontend-main-menu__mobile-panel {
  display: block;
}

.frontend-main-menu__mobile-nav--open .frontend-main-menu__mobile-backdrop {
  opacity: 1;
  pointer-events: auto;
  visibility: visible;
}

.frontend-main-menu__mobile-nav--open .frontend-main-menu__mobile-panel {
  opacity: 1;
  pointer-events: auto;
  transform: translateX(0);
  visibility: visible;
}

.frontend-main-menu__mobile-parent {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
}

.frontend-main-menu__mobile-item {
  display: contents;
  interpolate-size: allow-keywords;
}

.frontend-main-menu__mobile-toggle {
  color: var(--frontend-menu-text);
}

.frontend-main-menu__mobile-submenu {
  grid-column: 1 / -1;
  max-height: 0;
  opacity: 0;
  overflow: hidden;
  transform: translateY(-0.35rem);
  transition: max-height 0.22s ease, opacity 0.18s ease, transform 0.18s ease;
}

.frontend-main-menu__mobile-item--open .frontend-main-menu__mobile-submenu {
  max-height: 32rem;
  opacity: 1;
  transform: translateY(0);
}

@media (min-width: 640px) {
  .frontend-main-menu__mobile-panel {
    max-width: min(24rem, 86vw);
    width: 100%;
  }
}

.frontend-main-menu__desktop-row {
  display: flex;
  align-items: center;
  gap: 1rem;
}

.frontend-main-menu__desktop-row--left {
  flex: 1 1 auto;
  justify-content: space-between;
}

.frontend-main-menu__desktop-row--center {
  position: relative;
  justify-content: center;
}

.frontend-main-menu__desktop-row--centered-nav {
  justify-content: space-between;
  position: relative;
}

.frontend-main-menu__desktop-row--centered-nav .frontend-main-menu__nav {
  left: 50%;
  position: absolute;
  transform: translateX(-50%);
}

.frontend-main-menu__desktop-row--centered-nav .frontend-main-menu__actions,
.frontend-main-menu__desktop-row--centered-nav > .frontend-main-menu__languages {
  margin-left: auto;
}

.frontend-main-menu__desktop-row--center .frontend-main-menu__actions,
.frontend-main-menu__desktop-row--center > .frontend-main-menu__languages {
  position: absolute;
  right: 0;
}

.frontend-main-menu__desktop-row--right {
  flex: 1 1 auto;
  justify-content: flex-end;
}

.frontend-main-menu__desktop-row--right-nav {
  flex: 1 1 auto;
  justify-content: flex-start;
}

.frontend-main-menu__desktop-row--right-nav .frontend-main-menu__nav {
  margin-left: auto;
}

.frontend-main-menu__list {
  display: flex;
  align-items: center;
  gap: 0.5rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

.frontend-main-menu__list--center {
  justify-content: center;
}

.frontend-main-menu__list--right {
  justify-content: flex-end;
}

.frontend-main-menu__top-list {
  align-items: center;
  display: flex;
  flex-wrap: wrap;
  gap: 0.25rem;
  justify-content: flex-end;
  list-style: none;
  margin: 0;
  padding: 0;
}

.frontend-main-menu__summary::-webkit-details-marker,
.frontend-main-menu__mobile-summary::-webkit-details-marker,
.frontend-main-menu__language-summary::-webkit-details-marker {
  display: none;
}

.frontend-main-menu__summary,
.frontend-main-menu__mobile-summary,
.frontend-main-menu__language-summary {
  list-style: none;
}

.frontend-main-menu__link,
.frontend-main-menu__top-link,
.frontend-main-menu__summary,
.frontend-main-menu__mobile-link,
.frontend-main-menu__language-summary {
  color: var(--frontend-menu-text);
}

.frontend-main-menu__link {
  font-weight: 600;
  text-transform: uppercase;
}

.frontend-main-menu__link:visited,
.frontend-main-menu__top-link:visited,
.frontend-main-menu__top-dropdown-link:visited,
.frontend-main-menu__mobile-link:visited,
.frontend-main-menu__dropdown-link:visited,
.frontend-main-menu__language-option:visited {
  color: var(--frontend-menu-text);
}

.frontend-main-menu__dropdown-copy:visited {
  color: var(--frontend-menu-muted);
}

.frontend-main-menu__item-active {
  background: transparent;
  color: var(--color-secondary) !important;
}

.frontend-main-menu__item[open] .frontend-main-menu__summary,
.frontend-main-menu__mobile-item[open] .frontend-main-menu__mobile-summary,
.frontend-main-menu__language-picker[open] .frontend-main-menu__language-summary {
  background: var(--frontend-menu-surface-hover);
}

.frontend-main-menu__summary-icon,
.frontend-main-menu__top-icon,
.frontend-main-menu__mobile-icon,
.frontend-main-menu__language-icon {
  color: var(--frontend-menu-muted);
  transition: transform 0.2s ease;
}

.frontend-main-menu__item[open] .frontend-main-menu__summary .frontend-main-menu__summary-icon,
.frontend-main-menu__top-item:hover .frontend-main-menu__top-icon,
.frontend-main-menu__top-item:focus-within .frontend-main-menu__top-icon,
.frontend-main-menu__mobile-item--open .frontend-main-menu__mobile-toggle .frontend-main-menu__mobile-icon,
.frontend-main-menu__language-picker[open] .frontend-main-menu__language-summary .frontend-main-menu__language-icon,
.frontend-main-menu__mobile-nav--open .frontend-main-menu__mobile-summary .frontend-main-menu__mobile-icon {
  transform: rotate(180deg);
}

.frontend-main-menu__dropdown {
  background: var(--frontend-menu-surface);
  border: 1px solid var(--frontend-menu-border);
  box-shadow: 0 20px 48px color-mix(in srgb, black 14%, transparent);
  position: relative;
  z-index: 1001;
}

.frontend-main-menu__top-dropdown {
  background: var(--frontend-menu-surface);
  border: 1px solid var(--frontend-menu-border);
  border-radius: 1rem;
  box-shadow: 0 20px 48px color-mix(in srgb, black 14%, transparent);
  padding: 0.5rem;
  position: relative;
  z-index: 1001;
}

.frontend-main-menu__language-picker,
.frontend-main-menu__languages,
.frontend-main-menu__actions {
  position: relative;
  cursor: pointer;
}

.frontend-main-menu__language-dropdown {
  background: var(--frontend-menu-surface);
  border: 1px solid var(--frontend-menu-border);
  border-radius: 1rem;
  box-shadow: 0 20px 48px color-mix(in srgb, black 14%, transparent);
  min-width: 10rem;
  opacity: 0;
  padding: 0.5rem;
  pointer-events: none;
  position: absolute;
  right: 0;
  top: calc(100% + 0.75rem);
  transform: translateY(-0.35rem);
  transition: opacity 0.18s ease, transform 0.18s ease, visibility 0.18s ease;
  visibility: hidden;
  z-index: 1002;
}

.frontend-main-menu__language-picker--open .frontend-main-menu__language-dropdown {
  opacity: 1;
  pointer-events: auto;
  transform: translateY(0);
  visibility: visible;
}

.frontend-main-menu__dropdown-wrap {
  opacity: 0;
  pointer-events: none;
  transform: translateY(-0.25rem);
  transition: opacity 0.16s ease, transform 0.16s ease, visibility 0.16s ease;
  visibility: hidden;
  z-index: 1001;
}

.frontend-main-menu__top-dropdown-wrap {
  opacity: 0;
  pointer-events: none;
  transform: translateY(-0.25rem);
  transition: opacity 0.16s ease, transform 0.16s ease, visibility 0.16s ease;
  visibility: hidden;
  z-index: 1001;
}

.frontend-main-menu__item:hover .frontend-main-menu__dropdown-wrap,
.frontend-main-menu__item:focus-within .frontend-main-menu__dropdown-wrap {
  opacity: 1;
  pointer-events: auto;
  transform: translateY(0);
  visibility: visible;
}

.frontend-main-menu__top-item:hover .frontend-main-menu__top-dropdown-wrap,
.frontend-main-menu__top-item:focus-within .frontend-main-menu__top-dropdown-wrap {
  opacity: 1;
  pointer-events: auto;
  transform: translateY(0);
  visibility: visible;
}

.frontend-main-menu__item:hover .frontend-main-menu__summary-icon,
.frontend-main-menu__item:focus-within .frontend-main-menu__summary-icon {
  transform: rotate(180deg);
}

.frontend-main-menu__dropdown-link,
.frontend-main-menu__dropdown-title,
.frontend-main-menu__top-dropdown-link,
.frontend-main-menu__mobile-link,
.frontend-main-menu__language-option {
  color: var(--frontend-menu-text);
}

.frontend-main-menu__dropdown-copy {
  color: var(--frontend-menu-muted);
}

.frontend-main-menu__mobile-link {
  font-size: 16px;
  font-weight: 600;
  text-align: center;
  text-transform: uppercase;
}

.frontend-main-menu__dropdown-link:hover,
.frontend-main-menu__dropdown-copy:hover,
.frontend-main-menu__top-dropdown-link:hover,
.frontend-main-menu__mobile-link:hover,
.frontend-main-menu__language-option:hover {
  background: var(--frontend-menu-surface-hover);
}

.frontend-main-menu__link,
.frontend-main-menu__top-link,
.frontend-main-menu__mobile-link,
.frontend-main-menu__language-summary,
.frontend-main-menu__mobile-toggle {
  transition: background-color 0.18s ease, color 0.18s ease, box-shadow 0.18s ease;
}

@media (min-width: 1024px) and (max-width: 1279px) {
  .frontend-main-menu__list {
    gap: 0.15rem;
  }

  .frontend-main-menu__link {
    font-size: 0.92rem;
    padding-inline: 0.75rem;
  }

  .frontend-main-menu__button-link {
    padding-inline: 0.85rem;
  }
}

.frontend-main-menu__link:hover,
.frontend-main-menu__link:focus-visible,
.frontend-main-menu__top-link:hover,
.frontend-main-menu__top-link:focus-visible,
.frontend-main-menu__mobile-link:hover,
.frontend-main-menu__mobile-link:focus-visible,
.frontend-main-menu__language-summary:hover,
.frontend-main-menu__language-summary:focus-visible,
.frontend-main-menu__mobile-toggle:hover,
.frontend-main-menu__mobile-toggle:focus-visible {
  background: var(--frontend-menu-surface-hover);
}

.frontend-main-menu__item:hover > .frontend-main-menu__link,
.frontend-main-menu__item:focus-within > .frontend-main-menu__link {
  background: var(--frontend-menu-surface-hover);
}

.frontend-main-menu__link,
.frontend-main-menu__top-link {
  position: relative;
}

.frontend-main-menu__button-link {
  border: 2px solid #fff;
  font-weight: 600 !important;
  text-align: center;
  text-transform: uppercase;

  &:hover {
    border: 2px solid var(--color-secondary);
  }
}

@media (max-width: 1023px) {
  .frontend-main-menu__button-link {
    font-size: 16px;
  }
}

.frontend-main-menu__link:hover,
.frontend-main-menu__link:focus-visible,
.frontend-main-menu__top-link:hover,
.frontend-main-menu__top-link:focus-visible,
.frontend-main-menu__item:hover > .frontend-main-menu__link,
.frontend-main-menu__item:focus-within > .frontend-main-menu__link {
  background: transparent;
  color: var(--color-secondary);
}

.frontend-main-menu__link::after,
.frontend-main-menu__top-link::after {
  background: var(--color-secondary);
  bottom: -0.4rem;
  content: "";
  height: 3px;
  left: 0.5rem;
  opacity: 0;
  pointer-events: none;
  position: absolute;
  right: 0.5rem;
  transform: scaleX(0.35);
  transform-origin: center;
  transition: opacity 0.18s ease, transform 0.18s ease;
}

.frontend-main-menu__item-active::after,
.frontend-main-menu__link:hover::after,
.frontend-main-menu__link:focus-visible::after,
.frontend-main-menu__top-link:hover::after,
.frontend-main-menu__top-link:focus-visible::after,
.frontend-main-menu__item:hover > .frontend-main-menu__link::after,
.frontend-main-menu__item:focus-within > .frontend-main-menu__link::after {
  opacity: 1;
  transform: scaleX(1);
}

.frontend-main-menu__logo-image {
  display: block;
  height: var(--frontend-menu-logo-height) !important;
  max-height: var(--frontend-menu-logo-height);
  width: auto;
}

.frontend-main-menu__logo-link {
  align-items: center;
  display: inline-flex;
  min-height: var(--frontend-menu-logo-height);
}

.frontend-main-menu__logo-fallback {
  color: var(--frontend-menu-text);
}

.frontend-main-menu__language-flag {
  border: 1px solid var(--frontend-menu-border);
}

.frontend-main-menu__flag-fallback {
  background: var(--frontend-menu-surface-hover);
  border: 1px solid var(--frontend-menu-border);
  color: var(--frontend-menu-text);
}

.frontend-main-menu--fixed,
.frontend-main-menu--transparent-fixed {
  position: fixed;
  inset-inline: 0;
  top: 0;
  z-index: 1000;
}

.frontend-main-menu--transparent {
  position: absolute;
  inset-inline: 0;
  top: 0;
  z-index: 1000;
}

.frontend-main-menu--transparent .frontend-main-menu__shell,
.frontend-main-menu--transparent .frontend-main-menu__announcement,
.frontend-main-menu--transparent .frontend-main-menu__topbar,
.frontend-main-menu--transparent-fixed .frontend-main-menu__shell,
.frontend-main-menu--transparent-fixed .frontend-main-menu__announcement,
.frontend-main-menu--transparent-fixed .frontend-main-menu__topbar {
  background: transparent;
  box-shadow: none;
  border-color: transparent;
}

.frontend-main-menu--transparent-fixed.frontend-main-menu--scrolled .frontend-main-menu__shell,
.frontend-main-menu--transparent-fixed.frontend-main-menu--scrolled .frontend-main-menu__announcement,
.frontend-main-menu--transparent-fixed.frontend-main-menu--scrolled .frontend-main-menu__topbar {
  background: var(--frontend-menu-shell-background);
  box-shadow: var(--frontend-menu-shell-shadow);
  border-color: var(--frontend-menu-border);
}

.frontend-main-menu--squish {
  position: fixed;
  inset-inline: 0;
  top: 0;
  z-index: 1000;
  padding-top: 1rem;
}

.frontend-main-menu--squish .frontend-main-menu__shell,
.frontend-main-menu--squish .frontend-main-menu__announcement,
.frontend-main-menu--squish .frontend-main-menu__topbar {
  margin-inline: auto;
  max-width: min(94rem, calc(100% - 2rem));
  border-radius: 1.5rem;
  backdrop-filter: blur(18px);
}

/* ───────────────────────────────────────────────────────────────────────────
   askcredit — custom frontend CSS (thin override)

   This file is loaded AFTER all of themes/default/assets/stylesheets/*.css, so
   any rule here cascade-wins over the base theme. Put ONLY askcredit-specific
   tweaks here (backgrounds, spacing, bespoke component styling). The base
   button/carousel/hero/etc. styles come from default automatically.

   The dev working on askcredit edits files under themes/askcredit/ ONLY — never
   touches themes/default or themes/pasciuti, so two devs never collide.
   ─────────────────────────────────────────────────────────────────────────── */

body {
  background-color: var(--color-primary);
}

.askcredit-hidden {
  display: none;
}

.askcredit-multiselect-dropdown {
  background-color: var(--color-background);
  border-color: var(--color-secondary);
  position: fixed;
}

.askcredit-multiselect-divider {
  border-color: var(--color-secondary);
}

.askcredit-multiselect-list {
  max-height: 200px;
}

.askcredit-visually-offscreen {
  height: 0;
  left: -9999px;
  overflow: hidden;
  position: absolute;
}

.askcredit-subtitle-regular {
  font-weight: 400 !important;
}

.askcredit-prose-current {
  --tw-prose-body: currentColor;
  --tw-prose-headings: currentColor;
  --tw-prose-links: var(--color-primary);
  --tw-prose-bold: currentColor;
}

.askcredit-static-cards-timeline__title {
  font-size: 28px;
  font-weight: 700;
}

.askcredit-static-cards-timeline__subtitle {
  font-size: 28px;
  font-weight: 800;
}

/* This code is to set the static HTML content stored in the body field on the /it/l-evento page -- start */
@media (max-width: 767px) {
  .askcredit-prose-current > div[style*="max-width:1600px"] {
    padding: 16px 0 !important;
  }

  .askcredit-prose-current div[style*="grid-template-columns:240px 1fr"] {
    grid-template-columns: 1fr !important;
    column-gap: 0 !important;
    row-gap: 12px !important;
    margin-bottom: 24px !important;
  }

  .askcredit-prose-current div[style*="width:210px"][style*="border-radius:999px"] {
    font-size: 18px !important;
    justify-content: center !important;
    margin-top: 0 !important;
    max-width: 100% !important;
    min-height: 42px !important;
    padding: 8px 14px !important;
    white-space: normal !important;
    width: 100% !important;
  }

  .askcredit-prose-current h3[style*="font-size:28px"] {
    font-size: 20px !important;
    line-height: 1.2 !important;
    letter-spacing: 0.2px !important;
  }

  .askcredit-prose-current p[style*="font-size:20px"] {
    font-size: 16px !important;
    line-height: 1.45 !important;
  }

  .askcredit-prose-current p[style*="max-width:1320px"] {
    max-width: 100% !important;
  }
}
/* This code is to set the static HTML content stored in the body field on the /it/l-evento page -- end */

.askcredit-page-stack--flush-first-gap > .main-standard-page {
  margin-top: calc(var(--mobile-gap) * -1);
}

.askcredit-page-stack--homepage-gap > :first-child {
  margin-top: var(--mobile-gap);
}

@media (min-width: 768px) {
  .askcredit-page-stack--flush-first-gap > .main-standard-page {
    margin-top: calc(var(--desktop-gap) * -1);
  }

  .askcredit-page-stack--homepage-gap > :first-child {
    margin-top: var(--desktop-gap);
  }
}

.carousel__dot.active,
.blaze-pagination button.active {
  background-color: var(--color-secondary);
  border-color: var(--color-secondary);
}

.askcredit-gallery-smooth-slider {
  overflow: hidden;
}

.askcredit-page-404__button {
  color: #ffffff;
  text-transform: uppercase;
}

.askcredit-page-404__button:hover {
  background-color: var(--color-secondary);
  border-color: var(--color-secondary);
  color: #ffffff;
}

.askcredit-gallery-smooth-slider__track {
  animation: askcreditGallerySmoothScroll var(--askcredit-gallery-smooth-duration) linear infinite;
  display: flex;
  gap: var(--askcredit-gallery-gap, 20px);
  width: max-content;
  will-change: transform;
}

.askcredit-gallery-smooth-slider__track--reverse {
  animation-direction: reverse;
}

.askcredit-gallery-masonry {
  column-count: var(--askcredit-gallery-masonry-columns-mobile, 1);
  column-gap: var(--images-gap, 1rem);
}

.askcredit-gallery-masonry__item {
  break-inside: avoid;
  display: inline-block;
  width: 100%;
}

turbo-frame.masonry-loader-frame {
  column-span: all;
}

@media (min-width: 768px) {
  .askcredit-gallery-masonry {
    column-count: var(--askcredit-gallery-masonry-columns-tablet, 2);
  }
}

@media (min-width: 1024px) {
  .askcredit-gallery-masonry {
    column-count: var(--askcredit-gallery-masonry-columns-desktop, 4);
  }
}

.askcredit-gallery-smooth-slider__item {
  flex: 0 0 calc((100vw - (var(--askcredit-gallery-gap, 20px) * (var(--askcredit-gallery-slides-mobile, 1) - 1))) / var(--askcredit-gallery-slides-mobile, 1));
  min-width: 0;
}

@media (min-width: 768px) {
  .askcredit-gallery-smooth-slider__item {
    flex-basis: calc((100vw - (var(--askcredit-gallery-gap, 20px) * (var(--askcredit-gallery-slides-tablet, 2) - 1))) / var(--askcredit-gallery-slides-tablet, 2));
  }
}

@media (min-width: 1024px) {
  .askcredit-gallery-smooth-slider__item {
    flex-basis: calc((100vw - (var(--askcredit-gallery-gap, 20px) * (var(--askcredit-gallery-slides-desktop, 3) - 1))) / var(--askcredit-gallery-slides-desktop, 3));
  }
}

@keyframes askcreditGallerySmoothScroll {
  from {
    transform: translate3d(0, 0, 0);
  }

  to {
    transform: translate3d(calc(-50% - (var(--askcredit-gallery-gap, 20px) / 2)), 0, 0);
  }
}
