/*!
 * AimsCraft — Multi-purpose Ecommerce Frontend
 * File:      assets/css/components.css
 * Developer: Shahid Malla <https://shahidmalla.com>
 * Company:   AimsCraft — owner of this source code
 * Copyright: © ElySpace. All rights reserved.
 */
/* ==========================================================================
   AimsCraft — components.css
   Every reusable component in the design system.
   Depends on: tokens.css (design tokens), base.css (reset/type/a11y),
               utilities.css (helpers).
   Rules of the house:
     - single-class selectors, no ids, no !important
     - never a raw hex; always var(--token) (data-URI masks carry alpha only)
     - JS binds to data-* attributes, never to these classes
     - every component is correct in light + dark and under reduced motion
   Table of contents
     01. Shared primitives (masks, spinner keyframes)
     02. Buttons
     03. Forms
     04. Feedback
     05. Surfaces
     06. Product
     07. Navigation & overlay
     08. Marketing
     09. Reduced motion
   ========================================================================== */

/* ==========================================================================
   01. SHARED PRIMITIVES
   ========================================================================== */

/* Icon glyphs used by pure-CSS parts (checkbox tick, select chevron, steppers).
   Delivered as mask-images so they inherit currentColor and stay theme-correct.
   The colour inside the data URI is irrelevant — only its alpha channel is read. */
:root {
  --i-check: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='M5 12.5 9.5 17 19 7'/%3E%3C/svg%3E");
  --i-chevron-down: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m6 9.5 6 6 6-6'/%3E%3C/svg%3E");
  --i-chevron-right: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round' stroke-linejoin='round'%3E%3Cpath d='m9.5 6 6 6-6 6'/%3E%3C/svg%3E");
  --i-minus: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M5 12h14'/%3E%3C/svg%3E");
  --i-plus: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='M12 5v14M5 12h14'/%3E%3C/svg%3E");
  --i-close: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='2' stroke-linecap='round'%3E%3Cpath d='m6.5 6.5 11 11m0-11-11 11'/%3E%3C/svg%3E");
  --i-dash: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 24 24' fill='none' stroke='%23000' stroke-width='3' stroke-linecap='round'%3E%3Cpath d='M6 12h12'/%3E%3C/svg%3E");
  /* component-local derived surfaces (token maths, never literals) */
  --c-hover: color-mix(in oklab, var(--ink) 6%, transparent);
  --c-press: color-mix(in oklab, var(--ink) 11%, transparent);
  --c-veil: color-mix(in oklab, var(--surface) 72%, transparent);
}

@keyframes ss-spin { to { transform: rotate(360deg); } }
@keyframes ss-shimmer { 0% { background-position: 100% 50%; } 100% { background-position: -100% 50%; } }
@keyframes ss-fade-in { from { opacity: 0; } to { opacity: 1; } }
@keyframes ss-pop-in { from { opacity: 0; transform: translateY(6px) scale(.98); } to { opacity: 1; transform: none; } }
@keyframes ss-toast-in { from { opacity: 0; transform: translateY(12px) scale(.97); } to { opacity: 1; transform: none; } }
@keyframes ss-bar-indeterminate { 0% { transform: translateX(-100%); } 100% { transform: translateX(320%); } }
@keyframes ss-marquee { from { transform: translateX(0); } to { transform: translateX(-50%); } }
@keyframes ss-pulse-ring { 0% { box-shadow: 0 0 0 0 var(--brand-ring); } 70%,100% { box-shadow: 0 0 0 10px transparent; } }

/* ==========================================================================
   02. BUTTONS
   ========================================================================== */

.btn {
  --btn-h: var(--control-md);
  --btn-px: var(--sp-5);
  --btn-fs: var(--fs-sm);
  --btn-radius: var(--r-md);
  --btn-bg: var(--surface-2);
  --btn-fg: var(--ink);
  --btn-bd: var(--line);
  --btn-sh: 0 0 0 0 transparent;   /* valid no-op: `none` cannot compose in a list */
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  min-height: var(--btn-h);
  padding: 0 var(--btn-px);
  border: 1px solid var(--btn-bd);
  border-radius: var(--btn-radius);
  background: var(--btn-bg);
  color: var(--btn-fg);
  box-shadow: var(--btn-sh);
  font-family: var(--font-sans);
  font-size: var(--btn-fs);
  font-weight: 600;
  line-height: var(--lh-tight);
  letter-spacing: var(--ls-tight);
  max-inline-size: 100%;
  text-align: center;
  text-decoration: none;
  white-space: nowrap;
  cursor: pointer;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  touch-action: manipulation;
  transition:
    background-color var(--dur-2) var(--ease),
    border-color var(--dur-2) var(--ease),
    color var(--dur-2) var(--ease),
    box-shadow var(--dur-2) var(--ease),
    transform var(--dur-1) var(--ease);
}

.btn:hover { background: color-mix(in oklab, var(--btn-bg) 92%, var(--ink)); }
.btn:active { transform: translateY(1px); }
/* The ring is an OUTLINE, never another box-shadow layer.
   `box-shadow: var(--btn-sh), 0 0 0 3px …` is invalid whenever --btn-sh is
   `none` — a comma list cannot contain `none` — so the entire declaration
   computed to `none`. Combined with `outline: none` on the same rule, that left
   892 of the site's 1296 buttons with NO keyboard focus indicator at all:
   every ghost, secondary, outline, link, lime and danger button. An outline
   cannot be cancelled this way and follows the border-radius. */
.btn:focus-visible {
  outline: var(--ring-w) solid var(--focus-ink);
  outline-offset: 2px;
}
.btn[disabled],
.btn[aria-disabled="true"],
.btn.is-disabled {
  opacity: .5;
  cursor: not-allowed;
  transform: none;
  box-shadow: none;
  pointer-events: none;
}
.btn > .icon { flex: none; }

/* --- tones ---------------------------------------------------------------- */
/* The primary button follows --action, not --brand. For most accents those are
   the same colour; for aimscraft the identity is lime while the buttons are
   near-black, which is how the brand's own site draws them. */
.btn--primary {
  --btn-bg: var(--action);
  --btn-fg: var(--action-ink);
  --btn-bd: transparent;
  --btn-sh: var(--sh-action);
}
.btn--primary:hover { background: var(--action-hover); }

/* The one lime call-to-action the design uses, in the hero. */
.btn--lime {
  --btn-bg: var(--brand);
  --btn-fg: var(--brand-ink);
  --btn-bd: transparent;
  --btn-sh: 0 0 0 0 transparent;   /* valid no-op: `none` cannot compose in a list */
}
.btn--lime:hover { background: var(--brand-hover); }

.btn--secondary {
  --btn-bg: var(--surface-3);
  --btn-fg: var(--ink);
  /* Was `transparent`. --surface-3 sits 1.12:1 against the page, so with no
     border the button had no edge at all — "Mark all as read" read as a label,
     not a control. Borrow the same hairline .btn--outline uses so the two
     secondary treatments agree. */
  --btn-bd: var(--line-2);
}

.btn--ghost {
  --btn-bg: transparent;
  --btn-fg: var(--ink);
  --btn-bd: transparent;
}
.btn--ghost:hover { background: var(--c-hover); }

.btn--outline {
  --btn-bg: transparent;
  --btn-fg: var(--ink);
  --btn-bd: var(--line-2);
}
.btn--outline:hover {
  background: var(--c-hover);
  border-color: var(--ink-3);
}

.btn--danger {
  --btn-bg: var(--danger);
  --btn-fg: var(--ink-inv);
  --btn-bd: transparent;
}
.btn--danger:hover { background: color-mix(in oklab, var(--danger) 88%, var(--ink)); }

.btn--success {
  --btn-bg: var(--success);
  --btn-fg: var(--ink-inv);
  --btn-bd: transparent;
}
.btn--success:hover { background: color-mix(in oklab, var(--success) 88%, var(--ink)); }

.btn--link {
  --btn-bg: transparent;
  --btn-fg: var(--brand-text);
  --btn-bd: transparent;
  --btn-h: auto;
  --btn-px: 0;
  min-height: 0;
  padding: 0;
  text-decoration: underline;
  text-underline-offset: .22em;
  text-decoration-thickness: 1.5px;
  text-decoration-color: color-mix(in oklab, var(--brand) 45%, transparent);
}
.btn--link:hover {
  background: transparent;
  color: var(--brand-hover);
  text-decoration-color: currentColor;
}
.btn--link:active { transform: none; }

/* --- sizes ---------------------------------------------------------------- */
.btn--sm { --btn-h: var(--control-sm); --btn-px: var(--sp-3); --btn-fs: var(--fs-xs); --btn-radius: var(--r-sm); gap: var(--sp-1); }
.btn--lg { --btn-h: var(--control-lg); --btn-px: var(--sp-6); --btn-fs: var(--fs-md); --btn-radius: var(--r-md); }
.btn--xl { --btn-h: var(--control-xl); --btn-px: var(--sp-8); --btn-fs: var(--fs-lg); --btn-radius: var(--r-lg); gap: var(--sp-3); }
/* On touch screens a small button is still a finger target: the 36px step left
   every card and panel action under the 44px floor (Sep 2026 audit). */
@media (pointer: coarse) { .btn--sm { --btn-h: var(--control-md); } }

/* --- shapes --------------------------------------------------------------- */
.btn--icon {
  --btn-px: 0;
  width: var(--btn-h);
  min-width: var(--btn-h);
  padding: 0;
  gap: 0;
}
.btn--block { display: flex; width: 100%; }
/* Tracked uppercase, the way the brand sets its primary calls to action
   (SHOP PREMIUM COLLECTION, ADD TO CART, SHOP NOW). Not every button: the
   design keeps quieter actions — Buy Products, View article — in sentence
   case, so this is a modifier rather than a change to .btn itself. */
.btn--caps {
  font-size: var(--fs-xs);
  letter-spacing: var(--ls-wider);
  text-transform: uppercase;
}

/* On a narrow phone a long label ("Browse the products behind these reviews")
   cannot stay on one line without pushing past the viewport. Below 480px the
   label wraps and the button grows instead of overflowing; padding-block keeps
   a wrapped label off the border. */
@media (max-width: 479.98px) {
  .btn {
    white-space: normal;
    padding-block: var(--sp-2);
    text-wrap: balance;
  }
}
.btn--pill { --btn-radius: var(--r-full); }
.btn--raised { --btn-sh: var(--sh-2); }

/* --- loading -------------------------------------------------------------- */
.btn.is-loading {
  color: transparent;
  pointer-events: none;
}
.btn.is-loading > * { visibility: hidden; }
.btn.is-loading::after {
  content: "";
  position: absolute;
  inset: 50% auto auto 50%;
  width: 1.1em;
  height: 1.1em;
  margin: -.55em 0 0 -.55em;
  border: 2px solid color-mix(in oklab, var(--btn-fg) 30%, transparent);
  border-top-color: var(--btn-fg);
  border-radius: var(--r-full);
  color: var(--btn-fg);
  visibility: visible;
  animation: ss-spin 640ms linear infinite;
}
.btn--primary.is-loading::after,
.btn--danger.is-loading::after,
.btn--success.is-loading::after { --btn-fg: var(--brand-ink); }

/* --- affordances ---------------------------------------------------------- */
.btn__label { display: inline-block; }
.btn__badge {
  display: inline-grid;
  place-items: center;
  min-width: 18px;
  height: 18px;
  padding: 0 var(--sp-1);   /* was 5px, off the spacing grid */
  border-radius: var(--r-full);
  background: var(--brand);
  color: var(--brand-ink);
  font-size: var(--fs-2xs);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.btn-group {
  display: inline-flex;
  align-items: stretch;
}
.btn-group > .btn { border-radius: 0; margin-inline-start: -1px; }
.btn-group > .btn:first-child { border-start-start-radius: var(--r-md); border-end-start-radius: var(--r-md); margin-inline-start: 0; }
.btn-group > .btn:last-child { border-start-end-radius: var(--r-md); border-end-end-radius: var(--r-md); }
.btn-group > .btn:hover,
.btn-group > .btn:focus-visible { position: relative; z-index: 1; }

/* ==========================================================================
   03. FORMS
   ========================================================================== */

.field {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--sp-2);
  min-width: 0;
}
.field--inline {
  flex-direction: row;
  align-items: center;
  gap: var(--sp-3);
}
.field--grow { flex: 1 1 0; }

.label {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  color: var(--ink-2);
  font-size: var(--fs-xs);
  font-weight: 600;
  letter-spacing: var(--ls-wide);
}
.label--req::after {
  content: "*";
  color: var(--danger);
  font-weight: 700;
}
/* A parenthetical that is part of the label's own text. Distinct from
   .label--optional, whose ::after SUPPLIES the word "optional" — putting that
   class on a span that already said it printed the word twice. */
.label__hint {
  color: var(--ink-3);
  font-weight: 500;
  font-size: var(--fs-xs);   /* a sentence, not a badge: the caption step, not the 13px floor */
}

.label--optional::after {
  content: "optional";
  color: var(--ink-3);
  font-weight: 500;
  font-size: var(--fs-2xs);
  text-transform: lowercase;
}

/* --- text controls -------------------------------------------------------- */
.input,
.textarea,
.select {
  width: 100%;
  min-height: var(--control-md);   /* was 46px, off the control ladder */
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
  transition:
    border-color var(--dur-2) var(--ease),
    box-shadow var(--dur-2) var(--ease),
    background-color var(--dur-2) var(--ease);
}
.input::placeholder,
.textarea::placeholder { color: var(--ink-3); opacity: 1; }
.input:hover,
.textarea:hover,
.select:hover { border-color: var(--line-2); }
.input:focus,
.textarea:focus,
.select:focus {
  outline: none;
  border-color: var(--brand);
  box-shadow: 0 0 0 3px var(--brand-ring);
}
.input[disabled],
.textarea[disabled],
.select[disabled] {
  background: var(--surface-2);
  color: var(--ink-3);
  cursor: not-allowed;
}
.input[readonly],
.textarea[readonly] { background: var(--surface-2); }

.input--sm { min-height: 38px; padding: var(--sp-2) var(--sp-3); font-size: var(--fs-xs); border-radius: var(--r-sm); }
.input--lg { min-height: var(--control-lg); padding: var(--sp-4) var(--sp-5); font-size: var(--fs-md); }
.input--mono { font-family: var(--font-mono); letter-spacing: var(--ls-wide); }

.textarea {
  min-height: 120px;
  resize: vertical;
  line-height: var(--lh-normal);
}
.textarea--tall { min-height: 200px; }

/* --- select (custom chevron, currentColor via mask) ----------------------- */
.select {
  appearance: none;
  -webkit-appearance: none;
  padding-inline-end: var(--sp-10);
  cursor: pointer;
}
.select::-ms-expand { display: none; }

.select-wrap { position: relative; display: block; min-width: 0; }

/* The .field fallback anchors the arrow to the BOTTOM of the field, which is
   only where the control is when the select is the last thing in it. A hint
   after the select used to push the arrow down into the hint text, where it
   read as a stray glyph mid-sentence. Selects that are followed by anything go
   in a .select-wrap, which anchors the arrow to the control itself. */
.select-wrap::after,
.field:has(> .select:last-child)::after {
  content: "";
  position: absolute;
  inset-inline-end: var(--sp-4);
  bottom: 15px;
  width: 18px;
  height: 18px;
  background-color: var(--ink-3);
  -webkit-mask: var(--i-chevron-down) center / contain no-repeat;
  mask: var(--i-chevron-down) center / contain no-repeat;
  pointer-events: none;
  transition: background-color var(--dur-2) var(--ease);
}
.select-wrap::after { top: 50%; bottom: auto; transform: translateY(-50%); }
.select-wrap:hover::after,
.field:has(> .select:hover)::after,
.field:has(> .select:focus)::after { background-color: var(--ink); }

/* --- input groups & addons ------------------------------------------------ */
.input-group {
  display: flex;
  align-items: stretch;
  min-width: 0;
}
.input-group > .input,
.input-group > .select {
  border-radius: 0;
  margin-inline-start: -1px;
  min-width: 0;
}
/* Declared BEFORE the first/last-child rules on purpose: those give the group
   its outer corners, and a trailing .btn (the password reveal) must keep them.
   With this rule last it flattened all four corners of the end cap. */
.input-group > .btn { border-radius: 0; margin-inline-start: -1px; }
.input-group > :first-child { border-start-start-radius: var(--r-md); border-end-start-radius: var(--r-md); margin-inline-start: 0; }
.input-group > :last-child { border-start-end-radius: var(--r-md); border-end-end-radius: var(--r-md); }
.input-group > .input:focus,
.input-group > .select:focus { position: relative; z-index: 1; }
.input-group__addon {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  padding: 0 var(--sp-4);
  border: 1px solid var(--line);
  background: var(--surface-2);
  color: var(--ink-2);
  font-size: var(--fs-sm);
  font-weight: 600;
  white-space: nowrap;
}

/* --- search input --------------------------------------------------------- */
.search-input {
  position: relative;
  display: flex;
  align-items: center;
  min-width: 0;
}
.search-input > .input {
  padding-inline: var(--sp-12) var(--sp-10);
  border-radius: var(--r-full);
  background: var(--surface-2);
  border-color: transparent;
}
.search-input > .input:focus {
  background: var(--surface);
  border-color: var(--brand);
}
.search-input__icon {
  position: absolute;
  inset-inline-start: var(--sp-4);
  display: grid;
  place-items: center;
  color: var(--ink-3);
  pointer-events: none;
}
.search-input__clear {
  position: absolute;
  inset-inline-end: var(--sp-2);
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border: 0;
  border-radius: var(--r-full);
  background: transparent;
  color: var(--ink-3);
  cursor: pointer;
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--dur-2) var(--ease), background-color var(--dur-2) var(--ease);
}
.search-input__clear:hover { background: var(--c-hover); color: var(--ink); }
.search-input__clear:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--brand-ring); opacity: 1; visibility: visible; }
.search-input:has(.input:not(:placeholder-shown)) .search-input__clear { opacity: 1; visibility: visible; }
.search-input--lg > .input { min-height: var(--control-lg); font-size: var(--fs-md); }

/* --- floating label ------------------------------------------------------- */
/* markup: <div class="field field--floating"><input class="input" placeholder=" "><label class="label">…</label></div> */
.field--floating { gap: 0; }
.field--floating > .input,
.field--floating > .textarea,
.field--floating > .select {
  padding-top: var(--sp-6);
  padding-bottom: var(--sp-2);
  min-height: 60px;
}
.field--floating > .label {
  position: absolute;
  inset-inline-start: var(--sp-4);
  top: 50%;
  transform: translateY(-50%);
  transform-origin: left center;
  color: var(--ink-3);
  font-weight: 500;
  font-size: var(--fs-sm);
  pointer-events: none;
  transition: transform var(--dur-2) var(--ease), color var(--dur-2) var(--ease), font-size var(--dur-2) var(--ease);
}
.field--floating > .input:focus + .label,
.field--floating > .input:not(:placeholder-shown) + .label,
.field--floating > .textarea:focus + .label,
.field--floating > .textarea:not(:placeholder-shown) + .label,
.field--floating > .select:focus + .label,
.field--floating > .select + .label {
  top: 16px;
  transform: translateY(-50%) scale(.82);
  color: var(--ink-2);
  font-weight: 600;
}
.field--floating > .textarea + .label { top: 26px; }
.field--floating > .textarea:focus + .label,
.field--floating > .textarea:not(:placeholder-shown) + .label { top: 18px; }

/* --- checkbox / radio ----------------------------------------------------- */
.checkbox,
.radio {
  display: inline-flex;
  align-items: flex-start;
  gap: var(--sp-3);
  min-height: 28px;
  color: var(--ink);
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
  cursor: pointer;
  user-select: none;
}
.checkbox__input,
.radio__input {
  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;
}
.checkbox__mark,
.radio__mark {
  position: relative;
  flex: none;
  display: grid;
  place-items: center;
  width: 20px;
  height: 20px;
  margin-top: 2px;
  border: 1.5px solid var(--line-2);
  background: var(--surface);
  transition:
    background-color var(--dur-2) var(--ease),
    border-color var(--dur-2) var(--ease),
    box-shadow var(--dur-2) var(--ease);
}
.checkbox__mark { border-radius: var(--r-xs); }
.radio__mark { border-radius: var(--r-full); }
.checkbox__mark::after {
  content: "";
  width: 14px;
  height: 14px;
  background-color: var(--brand-ink);
  -webkit-mask: var(--i-check) center / contain no-repeat;
  mask: var(--i-check) center / contain no-repeat;
  transform: scale(.4);
  opacity: 0;
  transition: transform var(--dur-2) var(--ease-out-back), opacity var(--dur-1) var(--ease);
}
.radio__mark::after {
  content: "";
  width: 9px;
  height: 9px;
  border-radius: var(--r-full);
  background: var(--brand-ink);
  transform: scale(.2);
  opacity: 0;
  transition: transform var(--dur-2) var(--ease-out-back), opacity var(--dur-1) var(--ease);
}
.checkbox:hover .checkbox__mark,
.radio:hover .radio__mark { border-color: var(--ink-3); }
.checkbox__input:checked + .checkbox__mark,
.radio__input:checked + .radio__mark {
  background: var(--brand);
  border-color: var(--brand);
}
.checkbox__input:checked + .checkbox__mark::after,
.radio__input:checked + .radio__mark::after { transform: scale(1); opacity: 1; }
.checkbox__input:indeterminate + .checkbox__mark {
  background: var(--brand);
  border-color: var(--brand);
}
.checkbox__input:indeterminate + .checkbox__mark::after {
  -webkit-mask-image: var(--i-dash);
  mask-image: var(--i-dash);
  transform: scale(1);
  opacity: 1;
}
.checkbox__input:focus-visible + .checkbox__mark,
.radio__input:focus-visible + .radio__mark {
  outline: none;
  box-shadow: 0 0 0 3px var(--brand-ring);
}
.checkbox__input[disabled] + .checkbox__mark,
.radio__input[disabled] + .radio__mark { background: var(--surface-3); border-color: var(--line); }
.checkbox:has(.checkbox__input[disabled]),
.radio:has(.radio__input[disabled]) { opacity: .55; cursor: not-allowed; }
.checkbox__text,
.radio__text { display: block; padding-top: 2px; }
.checkbox__hint,
.radio__hint { display: block; color: var(--ink-3); font-size: var(--fs-xs); }

/* card-style pickable rows (shipping/payment options) */
.checkbox--card,
.radio--card {
  align-items: center;
  gap: var(--sp-4);
  width: 100%;
  padding: var(--sp-4);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
  transition: border-color var(--dur-2) var(--ease), background-color var(--dur-2) var(--ease), box-shadow var(--dur-2) var(--ease);
}
.checkbox--card:hover,
.radio--card:hover { border-color: var(--line-2); }
.checkbox--card:has(.checkbox__input:checked),
.radio--card:has(.radio__input:checked) {
  border-color: var(--brand);
  background: var(--brand-soft);
  box-shadow: 0 0 0 1px var(--brand);
}

/* --- switch --------------------------------------------------------------- */
.switch {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3);
  color: var(--ink);
  font-size: var(--fs-sm);
  cursor: pointer;
  user-select: none;
}
.switch__input {
  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;
}
.switch__track {
  position: relative;
  flex: none;
  width: 46px;
  height: 27px;
  padding: 3px;
  border: 1px solid var(--line-2);
  border-radius: var(--r-full);
  background: var(--surface-3);
  transition: background-color var(--dur-2) var(--ease), border-color var(--dur-2) var(--ease), box-shadow var(--dur-2) var(--ease);
}
.switch__thumb {
  display: block;
  width: 19px;
  height: 19px;
  border-radius: var(--r-full);
  background: var(--surface);
  box-shadow: var(--sh-1);
  transition: transform var(--dur-2) var(--ease-out-back);
}
.switch__input:checked + .switch__track {
  background: var(--brand);
  border-color: var(--brand);
}
.switch__input:checked + .switch__track .switch__thumb { transform: translateX(19px); }
.switch__input:focus-visible + .switch__track { outline: none; box-shadow: 0 0 0 3px var(--brand-ring); }
.switch:has(.switch__input[disabled]) { opacity: .55; cursor: not-allowed; }
.switch--sm .switch__track { width: 38px; height: 23px; }
.switch--sm .switch__thumb { width: 15px; height: 15px; }
.switch--sm .switch__input:checked + .switch__track .switch__thumb { transform: translateX(15px); }
.switch__text { line-height: var(--lh-snug); }

/* --- quantity stepper ----------------------------------------------------- */
.qty {
  display: inline-flex;
  align-items: stretch;
  border: 1px solid var(--line);
  border-radius: var(--r-full);
  background: var(--surface);
  overflow: hidden;
}
.qty:focus-within { border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-ring); }
.qty__btn {
  display: grid;
  place-items: center;
  width: 44px;
  min-height: 44px;
  border: 0;
  background: transparent;
  color: var(--ink-2);
  cursor: pointer;
  -webkit-tap-highlight-color: transparent;
  transition: background-color var(--dur-2) var(--ease), color var(--dur-2) var(--ease);
}
.qty__btn::before {
  content: "";
  width: 16px;
  height: 16px;
  background-color: currentColor;
  -webkit-mask: var(--i-minus) center / contain no-repeat;
  mask: var(--i-minus) center / contain no-repeat;
}
.qty__btn:last-child::before {
  -webkit-mask-image: var(--i-plus);
  mask-image: var(--i-plus);
}
/* The static markup relies on this masked ::before glyph, but store.js renders
   a real <svg class="icon"> inside the same button when it builds a cart line.
   Both drew, and because .qty__btn is a grid they stacked into two rows — every
   stepper in the mini bag showed its minus/plus twice. A real icon wins. */
.qty__btn:has(> .icon)::before { content: none; }
.qty__btn:hover { background: var(--c-hover); color: var(--ink); }
.qty__btn:focus-visible { outline: none; background: var(--brand-soft); color: var(--brand-text); }
.qty__btn[disabled] { opacity: .4; cursor: not-allowed; }
.qty__input {
  width: 46px;
  min-width: 0;
  padding: 0;
  border: 0;
  border-inline: 1px solid var(--line);
  background: transparent;
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--fs-sm);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  text-align: center;
  appearance: textfield;
  -moz-appearance: textfield;
}
.qty__input::-webkit-outer-spin-button,
.qty__input::-webkit-inner-spin-button { appearance: none; margin: 0; }
.qty__input:focus { outline: none; }
.qty--sm .qty__btn { width: 34px; min-height: 34px; }
.qty--sm .qty__input { width: 36px; font-size: var(--fs-xs); }
.qty--lg .qty__btn { width: var(--control-lg); min-height: var(--control-lg); }
.qty--lg .qty__input { width: 56px; font-size: var(--fs-md); }
.qty--block { display: flex; width: 100%; }
.qty--block .qty__input { flex: 1; width: auto; }

/* --- file drop ------------------------------------------------------------ */
.file-drop {
  position: relative;
  display: grid;
  place-items: center;
  gap: var(--sp-2);
  padding: var(--sp-10) var(--sp-6);
  border: 1.5px dashed var(--line-2);
  border-radius: var(--r-lg);
  background: var(--surface-2);
  text-align: center;
  cursor: pointer;
  transition: border-color var(--dur-2) var(--ease), background-color var(--dur-2) var(--ease);
}
.file-drop:hover { border-color: var(--brand); background: var(--brand-soft); }
.file-drop:focus-within { outline: none; border-color: var(--brand); box-shadow: 0 0 0 3px var(--brand-ring); }
.file-drop.is-dragover {
  border-color: var(--brand);
  border-style: solid;
  background: var(--brand-soft);
}
.file-drop__input {
  position: absolute;
  inset: 0;
  opacity: 0;
  cursor: pointer;
}
.file-drop__icon {
  display: grid;
  place-items: center;
  width: 48px;
  height: 48px;
  border-radius: var(--r-full);
  background: var(--surface);
  color: var(--brand-text);
  box-shadow: var(--sh-1);
}
.file-drop__title { color: var(--ink); font-size: var(--fs-sm); font-weight: 600; }
.file-drop__hint { color: var(--ink-3); font-size: var(--fs-xs); }
.file-drop__list { display: grid; gap: var(--sp-2); width: 100%; margin-top: var(--sp-3); }
.file-drop__file {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r-sm);
  background: var(--surface);
  color: var(--ink-2);
  font-size: var(--fs-xs);
  text-align: start;
}

/* --- range / dual price slider -------------------------------------------- */
.range {
  --range-min: 0%;
  --range-max: 100%;
  display: grid;
  gap: var(--sp-4);
}
.range__slider {
  position: relative;
  height: 28px;
  display: grid;
  align-items: center;
}
.range__track {
  position: absolute;
  inset-inline: 0;
  height: 5px;
  border-radius: var(--r-full);
  background: var(--surface-3);
}
.range__fill {
  position: absolute;
  /* Same 5px as the track, centred the same way. inset-block:0 stretched it
     to the slider's full 28px, so the price filter showed a fat orange bar
     with the thumbs sunk inside it rather than a line with two handles. */
  block-size: 5px;
  inset-inline-start: var(--range-min);
  inset-inline-end: calc(100% - var(--range-max));
  border-radius: var(--r-full);
  background: var(--brand);
}
.range__input {
  position: absolute;
  inset-inline: 0;
  width: 100%;
  height: 28px;
  margin: 0;
  background: transparent;
  appearance: none;
  -webkit-appearance: none;
  pointer-events: none;
}
.range__input::-webkit-slider-runnable-track { height: 28px; background: transparent; border: 0; }
.range__input::-moz-range-track { height: 28px; background: transparent; border: 0; }
.range__input::-webkit-slider-thumb {
  appearance: none;
  -webkit-appearance: none;
  width: 22px;
  height: 22px;
  margin-top: 3px;
  border: 2px solid var(--brand);
  border-radius: var(--r-full);
  background: var(--surface);
  box-shadow: var(--sh-2);
  cursor: grab;
  pointer-events: auto;
  transition: transform var(--dur-1) var(--ease), box-shadow var(--dur-2) var(--ease);
}
.range__input::-moz-range-thumb {
  width: 22px;
  height: 22px;
  border: 2px solid var(--brand);
  border-radius: var(--r-full);
  background: var(--surface);
  box-shadow: var(--sh-2);
  cursor: grab;
  pointer-events: auto;
}
.range__input:hover::-webkit-slider-thumb { transform: scale(1.08); }
.range__input:active::-webkit-slider-thumb { cursor: grabbing; transform: scale(1.14); }
.range__input:focus-visible::-webkit-slider-thumb { box-shadow: 0 0 0 4px var(--brand-ring); }
.range__input:focus-visible::-moz-range-thumb { box-shadow: 0 0 0 4px var(--brand-ring); }
.range__input:focus { outline: none; }
.range__values {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  color: var(--ink-2);
  font-size: var(--fs-xs);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
}
.range__value {
  padding: var(--sp-1) var(--sp-3);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface-2);
}

/* --- OTP ------------------------------------------------------------------ */
.otp-input {
  display: flex;
  gap: var(--sp-2);
}
.otp-input__box {
  width: 52px;
  height: 60px;
  padding: 0;
  /* --line is a hairline between surfaces; as the only edge of an empty input
     it sat at ~1.2:1 against the page and the six boxes barely existed. */
  border: 1.5px solid var(--line-2);
  border-radius: var(--r-md);
  background: var(--surface);
  color: var(--ink);
  font-family: var(--font-mono);
  font-size: var(--fs-2xl);
  font-weight: 600;
  text-align: center;
  appearance: textfield;
  -moz-appearance: textfield;
  transition: border-color var(--dur-2) var(--ease), box-shadow var(--dur-2) var(--ease), background-color var(--dur-2) var(--ease);
}
.otp-input__box::-webkit-outer-spin-button,
.otp-input__box::-webkit-inner-spin-button { appearance: none; margin: 0; }
.otp-input__box:focus {
  outline: none;
  border-color: var(--brand);
  background: var(--brand-soft);
  box-shadow: 0 0 0 3px var(--brand-ring);
}
.otp-input__box:not(:placeholder-shown) { border-color: var(--brand); }
.otp-input--sm .otp-input__box { width: 42px; height: 50px; font-size: var(--fs-lg); }

/* --- hint / error / validity --------------------------------------------- */
/* A hint is prose 43 times out of 52 and an icon + label the other 9. Flex was
   the default, which made every text run and every inline <a> its own flex
   item: "Already have an account? Sign in to prefill…" broke into three
   independently-wrapping columns. Prose is the default now, and the icon-led
   form opts back into the row. */
.hint {
  display: block;
  color: var(--ink-3);
  font-size: var(--fs-xs);
  line-height: var(--lh-snug);
}
/* The icon rides along in the text flow rather than making the hint a flex
   container. As a flex row every text run and every inline <a> became its own
   item, so a sentence with a link broke into side-by-side columns with a hole
   in the middle — visible on the login and account-profile hints. */
.hint > svg,
.hint > .icon {
  vertical-align: -0.15em;
  margin-inline-end: var(--sp-1);
}
.error {
  display: flex;
  align-items: center;
  gap: var(--sp-1);
  color: var(--danger);
  font-size: var(--fs-xs);
  font-weight: 600;
  line-height: var(--lh-snug);
}
.field.has-error .input,
.field.has-error .textarea,
.field.has-error .select,
.input.has-error,
.textarea.has-error,
.select.has-error { border-color: var(--danger); }
.field.has-error .input:focus,
.field.has-error .textarea:focus,
.field.has-error .select:focus,
.input.has-error:focus { box-shadow: 0 0 0 3px color-mix(in oklab, var(--danger) 24%, transparent); }
.field.is-valid .input,
.field.is-valid .select { border-color: var(--success); }
.field.is-loading .input { background-image: none; opacity: .7; pointer-events: none; }

/* app.js leads every inline message with an alert icon. A message that wraps
   to two lines keeps the icon on the first line instead of centring it. */
.field__msg:has(> .icon),
.error:has(> .icon) { align-items: flex-start; }
.field__msg > .icon,
.error > .icon { margin-block-start: 1px; }

/* A checkbox or radio has no border of its own to turn red; its mark does. */
.checkbox__input[aria-invalid="true"] + .checkbox__mark,
.radio__input[aria-invalid="true"] + .radio__mark { border-color: var(--danger); }

/* --- error summary --------------------------------------------------------
   Built by app.js (§23) at the top of a form, or of a checkout step, when a
   submit or a Continue finds problems: a heading that counts them and one
   link per field. It is focused on arrival (tabindex="-1"), so it carries the
   same focus ring as everything else, and it shrinks as problems are fixed. */
.error-summary {
  display: grid;
  gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-5);
  border: 1px solid color-mix(in oklab, var(--danger) 36%, transparent);
  border-inline-start: 4px solid var(--danger);
  border-radius: var(--r-md);
  background: var(--danger-soft);
  color: var(--ink);
  animation: ss-pop-in var(--dur-2) var(--ease) both;
}
.error-summary[hidden] { display: none; }
/* straight inside a form (checkout) it needs its own space; inside a stack or
   a grid step the parent's gap already provides it */
form > .error-summary { margin-block-end: var(--sp-5); }
.error-summary__title {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-2);
  margin: 0;
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--fs-md);
  font-weight: 700;
  letter-spacing: normal;
  line-height: var(--lh-snug);
}
.error-summary__title > .icon { color: var(--danger); margin-block-start: 1px; }
.error-summary__list {
  display: grid;
  gap: var(--sp-2);
  margin: 0;
  padding: 0;
  padding-inline-start: calc(var(--icon-md) + var(--sp-2));   /* under the heading's text, not its icon */
  list-style: none;
}
.error-summary__link {
  color: var(--danger-ink);
  font-size: var(--fs-sm);
  font-weight: 600;
  line-height: var(--lh-snug);
  text-decoration: underline;
  text-decoration-thickness: 1px;
  text-underline-offset: 3px;
}
.error-summary__link:hover { text-decoration-thickness: 2px; }
@media (prefers-reduced-motion: reduce) {
  .error-summary { animation: none; }
}

/* ==========================================================================
   04. FEEDBACK
   ========================================================================== */

/* --- badge ---------------------------------------------------------------- */
.badge {
  --badge-bg: var(--surface-3);
  --badge-fg: var(--ink-2);
  display: inline-flex;
  /* A badge is a pill around its own text, never a full-width bar. Inside a
     column flex container (.card, .stack) the default align-items: stretch
     was blowing it out to the card width — "Awaiting pickup" rendered as a
     260px band. A definite inline size wins over stretch in both flex and
     grid, and changes nothing in a row where it was already shrink-to-fit. */
  inline-size: fit-content;
  align-items: center;
  gap: var(--sp-1);
  padding: 3px var(--sp-2);
  border: 1px solid transparent;
  border-radius: var(--r-full);
  background: var(--badge-bg);
  color: var(--badge-fg);
  font-size: var(--fs-2xs);
  font-weight: 700;
  line-height: 1.5;
  letter-spacing: var(--ls-wide);
  white-space: nowrap;
  vertical-align: middle;
}
.badge--brand { --badge-bg: var(--brand-soft); --badge-fg: var(--brand-text); }
.badge--success { --badge-bg: var(--success-soft); --badge-fg: var(--success); }
.badge--warning { --badge-bg: var(--warning-soft); --badge-fg: var(--warning-ink); }
.badge--danger { --badge-bg: var(--danger-soft); --badge-fg: var(--danger); }
.badge--info { --badge-bg: var(--info-soft); --badge-fg: var(--info); }
.badge--neutral { --badge-bg: var(--surface-3); --badge-fg: var(--ink-2); }
.badge--accent { --badge-bg: color-mix(in oklab, var(--accent-2) 16%, transparent); --badge-fg: var(--accent-2); }
.badge--solid { --badge-bg: var(--brand); --badge-fg: var(--brand-ink); }
.badge--outline {
  background: transparent;
  border-color: color-mix(in oklab, currentColor 35%, transparent);
}
.badge--dot::before {
  content: "";
  width: 6px;
  height: 6px;
  border-radius: var(--r-full);
  background: currentColor;
}
.badge--sm { padding: 2px var(--sp-1); font-size: var(--fs-2xs); }
.badge--lg { padding: var(--sp-1) var(--sp-3); font-size: var(--fs-xs); }
.badge--square { border-radius: var(--r-xs); }

/* --- chip ----------------------------------------------------------------- */
.chip {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: 36px;
  padding: 0 var(--sp-4);
  border: 1px solid var(--line);
  border-radius: var(--r-full);
  background: var(--surface);
  color: var(--ink-2);
  font-size: var(--fs-xs);
  font-weight: 600;
  white-space: nowrap;
  transition: background-color var(--dur-2) var(--ease), border-color var(--dur-2) var(--ease), color var(--dur-2) var(--ease);
}
.chip--selectable { cursor: pointer; -webkit-tap-highlight-color: transparent; }
.chip--selectable:hover { border-color: var(--line-2); background: var(--surface-2); color: var(--ink); }
.chip--selectable:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--brand-ring); }
.chip.is-selected {
  border-color: var(--brand);
  background: var(--brand-soft);
  color: var(--brand-text);
}
.chip.is-disabled { opacity: .5; pointer-events: none; }
.chip--removable { padding-inline-end: var(--sp-1); }
.chip__close {
  display: grid;
  place-items: center;
  width: 26px;
  height: 26px;
  border: 0;
  border-radius: var(--r-full);
  background: transparent;
  color: inherit;
  cursor: pointer;
  transition: background-color var(--dur-1) var(--ease);
}
.chip__close::before {
  content: "";
  width: 12px;
  height: 12px;
  background-color: currentColor;
  -webkit-mask: var(--i-close) center / contain no-repeat;
  mask: var(--i-close) center / contain no-repeat;
}
.chip__close:hover { background: var(--c-press); }
.chip__close:focus-visible { outline: none; box-shadow: 0 0 0 2px var(--brand-ring); }
.chip__dot { width: 10px; height: 10px; border-radius: var(--r-full); background: currentColor; }
.chip--sm { min-height: 28px; padding: 0 var(--sp-3); font-size: var(--fs-2xs); }
.chip-row {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
}

/* --- alert ---------------------------------------------------------------- */
.alert {
  --alert-bg: var(--surface-2);
  --alert-fg: var(--ink);
  --alert-accent: var(--ink-3);
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: start;
  gap: var(--sp-3);
  padding: var(--sp-4);
  border: 1px solid color-mix(in oklab, var(--alert-accent) 26%, transparent);
  border-radius: var(--r-md);
  background: var(--alert-bg);
  color: var(--alert-fg);
  font-size: var(--fs-sm);
  line-height: var(--lh-snug);
}
.alert--success { --alert-bg: var(--success-soft); --alert-accent: var(--success); }
.alert--warning { --alert-bg: var(--warning-soft); --alert-accent: var(--warning); }
.alert--danger { --alert-bg: var(--danger-soft); --alert-accent: var(--danger); }
.alert--info { --alert-bg: var(--info-soft); --alert-accent: var(--info); }
.alert--brand { --alert-bg: var(--brand-soft); --alert-accent: var(--brand); }
.alert__icon {
  display: grid;
  place-items: center;
  color: var(--alert-accent);
  margin-top: 1px;
}
.alert__body { display: grid; gap: var(--sp-1); min-width: 0; }
.alert__title { color: var(--ink); font-size: var(--fs-sm); font-weight: 700; }
.alert__text { color: var(--ink-2); line-height: var(--lh-normal); }   /* inherited 1.25 cramped two-line alerts */
/* WebKit draws its own clear "×" in type=search fields, beside the custom one */
.search-input .input::-webkit-search-cancel-button { -webkit-appearance: none; appearance: none; }
.alert__actions { display: flex; flex-wrap: wrap; gap: var(--sp-2); margin-top: var(--sp-2); }
.alert__close {
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border: 0;
  border-radius: var(--r-full);
  background: transparent;
  color: var(--ink-3);
  cursor: pointer;
}
.alert__close::before {
  content: "";
  width: 14px; height: 14px;
  background-color: currentColor;
  -webkit-mask: var(--i-close) center / contain no-repeat;
  mask: var(--i-close) center / contain no-repeat;
}
.alert__close:hover { background: var(--c-hover); color: var(--ink); }
.alert__close:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--brand-ring); }
.alert--bare { border-color: transparent; background: transparent; padding-inline: 0; }

/* --- toast ---------------------------------------------------------------- */
.toast-stack {
  position: fixed;
  inset-block-end: calc(var(--tabbar-h) + var(--safe-b) + var(--sp-4));
  inset-inline: var(--sp-4);
  width: auto;
  transform: none;          /* app.js's fallback centres with translateX(-50%); with inset-inline that would push the stack off-screen */
  z-index: var(--z-toast);
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: var(--sp-2);
  pointer-events: none;
}
.toast {
  --toast-accent: var(--brand);
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: var(--sp-3);
  width: min(420px, 100%);
  padding: var(--sp-3) var(--sp-4);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  box-shadow: var(--sh-4);
  color: var(--ink);
  font-size: var(--fs-sm);
  pointer-events: auto;
  animation: ss-toast-in var(--dur-3) var(--ease-out-back) both;
}
.toast--success { --toast-accent: var(--success); }
.toast--danger { --toast-accent: var(--danger); }
.toast--warning { --toast-accent: var(--warning); }
.toast--info { --toast-accent: var(--info); }
.toast__icon {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border-radius: var(--r-full);
  background: color-mix(in oklab, var(--toast-accent) 16%, transparent);
  color: var(--toast-accent);
}
.toast__body { display: grid; gap: 2px; min-width: 0; }
.toast__title { font-weight: 700; line-height: var(--lh-tight); }
.toast__msg { color: var(--ink-2); font-size: var(--fs-xs); line-height: var(--lh-snug); }
.toast__close {
  display: grid;
  place-items: center;
  width: 28px; height: 28px;
  border: 0;
  border-radius: var(--r-full);
  background: transparent;
  color: var(--ink-3);
  cursor: pointer;
}
.toast__close::before {
  content: "";
  width: 13px; height: 13px;
  background-color: currentColor;
  -webkit-mask: var(--i-close) center / contain no-repeat;
  mask: var(--i-close) center / contain no-repeat;
}
.toast__close:hover { background: var(--c-hover); color: var(--ink); }
.toast.is-out { animation: ss-toast-in var(--dur-2) var(--ease) reverse both; }

@media (min-width: 768px) {
  .toast-stack {
    inset-block-end: var(--sp-6);
    inset-inline: auto var(--sp-6);
    align-items: flex-end;
  }
}

/* --- progress ------------------------------------------------------------- */
.progress {
  position: relative;
  height: 8px;
  border-radius: var(--r-full);
  background: var(--surface-3);
  overflow: hidden;
}
.progress__bar {
  /* The bar is a <div> in the shipping meter but a <span> in the review
     histograms. An inline box ignores width and height, so every `<span
     class="progress__bar" style="width:100%">` rendered as an empty track —
     the rating distribution conveyed nothing at all. */
  display: block;
  height: 100%;
  width: 0;
  border-radius: var(--r-full);
  background: var(--brand);
  transition: width var(--dur-4) var(--ease);
}
.progress--sm { height: 4px; }
.progress--lg { height: 12px; }
.progress--success .progress__bar { background: var(--success); }
.progress--danger .progress__bar { background: var(--danger); }
.progress--indeterminate .progress__bar {
  width: 30%;
  animation: ss-bar-indeterminate 1.2s var(--ease) infinite;
}

/* checkout stepper */
.progress--steps {
  height: auto;
  border-radius: 0;
  background: transparent;
  overflow: visible;
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
  gap: var(--sp-2);
}
.progress__step {
  display: grid;
  justify-items: center;
  gap: var(--sp-2);
  position: relative;
  color: var(--ink-3);
  font-size: var(--fs-xs);
  font-weight: 600;
  text-align: center;
}
.progress__step::before {
  content: "";
  position: absolute;
  top: 13px;
  inset-inline-start: calc(-50% + 16px);
  width: calc(100% - 32px);
  height: 2px;
  border-radius: var(--r-full);
  background: var(--line);
}
.progress__step:first-child::before { display: none; }
.progress__step-dot {
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border: 2px solid var(--line-2);
  border-radius: var(--r-full);
  background: var(--surface);
  color: var(--ink-3);
  font-size: var(--fs-2xs);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
  transition: background-color var(--dur-2) var(--ease), border-color var(--dur-2) var(--ease), color var(--dur-2) var(--ease);
}
.progress__step-label { line-height: var(--lh-snug); }
.progress__step.is-active { color: var(--ink); }
.progress__step.is-active .progress__step-dot {
  border-color: var(--brand);
  background: var(--brand);
  color: var(--brand-ink);
  box-shadow: 0 0 0 4px var(--brand-ring);
}
.progress__step.is-done { color: var(--ink-2); }
.progress__step.is-done .progress__step-dot {
  border-color: var(--brand);
  background: var(--brand);
  color: transparent;
}
.progress__step.is-done .progress__step-dot::after {
  content: "";
  position: absolute;
  width: 14px; height: 14px;
  background-color: var(--brand-ink);
  -webkit-mask: var(--i-check) center / contain no-repeat;
  mask: var(--i-check) center / contain no-repeat;
}
.progress__step.is-done::before,
.progress__step.is-active::before { background: var(--brand); }

/* --- skeleton ------------------------------------------------------------- */
.skeleton {
  position: relative;
  display: block;
  border-radius: var(--r-sm);
  background-color: var(--surface-3);
  background-image: linear-gradient(
    90deg,
    transparent 0%,
    color-mix(in oklab, var(--surface) 70%, transparent) 50%,
    transparent 100%
  );
  background-size: 200% 100%;
  background-repeat: no-repeat;
  animation: ss-shimmer 1.4s linear infinite;
  overflow: hidden;
}
.skeleton--text { height: .85em; border-radius: var(--r-xs); }
.skeleton--text + .skeleton--text { margin-top: var(--sp-2); }
.skeleton--title { height: 1.3em; width: 60%; border-radius: var(--r-xs); }
.skeleton--line-2 { width: 88%; }
.skeleton--line-3 { width: 72%; }
.skeleton--circle { border-radius: var(--r-full); aspect-ratio: 1; }
.skeleton--img { aspect-ratio: 4 / 5; border-radius: var(--r-lg); width: 100%; }
.skeleton--btn { height: 44px; border-radius: var(--r-md); }
.skeleton--chip { height: 36px; width: 96px; border-radius: var(--r-full); }

/* --- spinner -------------------------------------------------------------- */
.spinner {
  display: inline-block;
  width: 20px;
  height: 20px;
  border: 2px solid color-mix(in oklab, currentColor 22%, transparent);
  border-top-color: currentColor;
  border-radius: var(--r-full);
  color: var(--brand-text);
  animation: ss-spin 640ms linear infinite;
  vertical-align: middle;
}
.spinner--sm { width: 14px; height: 14px; border-width: 1.5px; }
.spinner--lg { width: 34px; height: 34px; border-width: 3px; }
.spinner--inv { color: var(--ink-inv); }
.spinner--ink { color: var(--ink); }

/* --- empty state ---------------------------------------------------------- */
.empty-state {
  display: grid;
  justify-items: center;
  gap: var(--sp-3);
  padding: var(--sp-12) var(--sp-5);
  text-align: center;
}
.empty-state__icon {
  display: grid;
  place-items: center;
  width: 72px;
  height: 72px;
  border-radius: var(--r-full);
  background: var(--surface-2);
  color: var(--ink-3);
}
.empty-state__title {
  color: var(--ink);
  font-family: var(--font-display);
  font-size: var(--fs-2xl);
  font-weight: 400;
  line-height: var(--lh-tight);
}
.empty-state__text {
  max-width: 42ch;
  color: var(--ink-3);
  font-size: var(--fs-sm);
  line-height: var(--lh-relaxed);
}
.empty-state__actions { display: flex; flex-wrap: wrap; justify-content: center; gap: var(--sp-3); margin-top: var(--sp-2); }
.empty-state--sm { padding: var(--sp-8) var(--sp-4); }
.empty-state--sm .empty-state__icon { width: 52px; height: 52px; }

/* ==========================================================================
   05. SURFACES
   ========================================================================== */

.card {
  position: relative;
  display: flex;
  flex-direction: column;
  min-width: 0;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  box-shadow: var(--sh-1);
  overflow: hidden;
  transition: box-shadow var(--dur-3) var(--ease), border-color var(--dur-3) var(--ease), transform var(--dur-3) var(--ease);
}
.card--flat { border-color: transparent; box-shadow: none; background: var(--surface-2); }
.card--outline { box-shadow: none; background: transparent; }
.card--hover:hover { box-shadow: var(--sh-3); border-color: var(--line-2); transform: translateY(-3px); }
.card--hover:focus-within { box-shadow: var(--sh-3); border-color: var(--brand); }
.card--pad { padding: var(--sp-5); }
.card__media {
  position: relative;
  display: block;
  aspect-ratio: 16 / 10;
  background: var(--surface-2);
  overflow: hidden;
}
.card__media > img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.card__header {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--sp-5) var(--sp-5) 0;
}
.card__body { display: grid; gap: var(--sp-2); padding: var(--sp-5); }
.card__footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-5);
  border-top: 1px solid var(--line);
  background: var(--surface-2);
}
.card__title {
  color: var(--ink);
  /* sans, not the condensed display serif an h2 carries: at 18px the serif read
     as a cramped label */
  font-family: var(--font-sans);
  font-size: var(--fs-lg);
  font-weight: 700;
  line-height: var(--lh-snug);
  letter-spacing: var(--ls-tight);
}
.card__text { color: var(--ink-2); font-size: var(--fs-sm); line-height: var(--lh-relaxed); }
.card__link::after {
  content: "";
  position: absolute;
  inset: 0;
  border-radius: inherit;
}

.panel {
  display: flex;
  flex-direction: column;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  overflow: hidden;
}
.panel__head {
  display: flex;
  flex-wrap: wrap;   /* title + badge could not share 238px at 320px */
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-5);
  border-bottom: 1px solid var(--line);
  background: var(--surface-2);
}
/* An <h2> for the outline, a UI label to the eye — so it drops the display
   serif the h2 rule hands down. At 14px that serif read as a mistake. */
.panel__title { color: var(--ink); font-family: var(--font-sans); font-size: var(--fs-sm); font-weight: 700; letter-spacing: var(--ls-wide); }
.panel__body { padding: var(--sp-5); }
/* Panels sit in equal-height grid rows, so a short panel is stretched to match
   its tallest neighbour. Without this the slack piled up *below* the foot: the
   shaded foot bar floated in the middle of the card with empty white under it.
   The body absorbs the extra height instead, which pins the foot to the
   bottom edge where it belongs. */
.panel__body { flex: 1 1 auto; }
.panel__foot {
  padding: var(--sp-4) var(--sp-5);
  border-top: 1px solid var(--line);
  background: var(--surface-2);
}
.panel--flush .panel__body { padding: 0; }

.tile {
  display: grid;
  /* Grid rows stretch the <li>, so the tile inside has to fill it too or the
     shorter tiles in a row end 20px above their neighbours. */
  block-size: 100%;
  align-content: start;
  gap: var(--sp-2);
  padding: var(--sp-5);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
  color: inherit;
  text-decoration: none;
  transition: border-color var(--dur-2) var(--ease), background-color var(--dur-2) var(--ease), transform var(--dur-2) var(--ease);
}
.tile:hover { border-color: var(--brand); background: var(--brand-soft); transform: translateY(-2px); }
.tile:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--brand-ring); }
.tile__icon {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border-radius: var(--r-md);
  background: var(--brand-soft);
  color: var(--brand-text);
}
.tile__title { color: var(--ink); font-size: var(--fs-sm); font-weight: 700; }
.tile__text { color: var(--ink-3); font-size: var(--fs-xs); line-height: var(--lh-snug); }
.tile--center { justify-items: center; text-align: center; }

.stat {
  display: grid;
  align-content: start;   /* stretched cards must not spread their rows */
  gap: var(--sp-1);
  padding: var(--sp-5);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
}
.stat__label {
  color: var(--ink-3);
  font-size: var(--fs-2xs);
  font-weight: 700;
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
}
/* font-size lives in a :where() so it carries no specificity: the markup asks
   for a smaller step with .u-fs-lg in a few places (the track-order stat row),
   and a plain .stat__value rule silently out-ranked every one of those. */
:where(.stat__value) { font-size: var(--fs-3xl); }
.stat__value {
  color: var(--ink);
  /* GSTINs, FSSAI licences and other unbreakable identifiers land here at
     26-36px; in a 118px column they would otherwise paint straight past the
     card and drag the whole document into a sideways scroll. */
  overflow-wrap: anywhere;
  font-weight: 700;
  line-height: var(--lh-tight);
  font-variant-numeric: tabular-nums;
  letter-spacing: var(--ls-tight);
}
.stat__delta {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  font-size: var(--fs-xs);
  font-weight: 700;
}
.stat__delta--up { color: var(--success); }
.stat__delta--down { color: var(--danger); }
.stat__hint { color: var(--ink-3); font-size: var(--fs-xs); }
/* "4.7/5" — the unit rides inside .stat__value and inherits its 36px, so the
   denominator was shouting exactly as loud as the score. em, not a token, so it
   tracks whatever size the value is set to. */
.stat__unit { font-size: .5em; font-weight: var(--fw-medium); color: var(--ink-3); }

.divider {
  height: 1px;
  border: 0;
  margin: 0;
  background: var(--line);
}
.divider--vertical { width: 1px; height: auto; align-self: stretch; }
.divider--label {
  display: flex;
  align-items: center;
  gap: var(--sp-4);
  height: auto;
  background: transparent;
  color: var(--ink-3);
  font-size: var(--fs-xs);
  font-weight: 600;
  white-space: nowrap;
}
.divider--label::before,
.divider--label::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--line);
}
.divider--soft { background: color-mix(in oklab, var(--line) 55%, transparent); }

.ribbon {
  position: absolute;
  inset-block-start: var(--sp-3);
  inset-inline-start: 0;
  z-index: 2;
  padding: var(--sp-1) var(--sp-3);
  border-start-end-radius: var(--r-xs);
  border-end-end-radius: var(--r-xs);
  background: var(--ink);
  color: var(--ink-inv);
  font-size: var(--fs-2xs);
  font-weight: 700;
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
  box-shadow: var(--sh-1);
}
.ribbon--sale { background: var(--danger); color: var(--ink-inv); }
.ribbon--new { background: var(--success); color: var(--ink-inv); }
.ribbon--brand { background: var(--brand); color: var(--brand-ink); }
.ribbon--corner {
  inset-block-start: var(--sp-4);
  inset-inline-start: auto;
  inset-inline-end: 0;
  border-radius: var(--r-xs) 0 0 var(--r-xs);
}

/* --- price ---------------------------------------------------------------- */
.price {
  display: inline-flex;
  align-items: baseline;
  flex-wrap: wrap;
  gap: var(--sp-2);
  font-variant-numeric: tabular-nums;
}
.price__now {
  color: var(--ink);
  font-size: var(--fs-lg);
  font-weight: 700;
  letter-spacing: var(--ls-tight);
}
.price__old {
  color: var(--ink-3);
  font-size: var(--fs-sm);
  font-weight: 500;
  text-decoration: line-through;
  text-decoration-thickness: 1px;
}
.price__save {
  padding: 2px var(--sp-2);
  border-radius: var(--r-full);
  background: var(--danger-soft);
  color: var(--danger);
  font-size: var(--fs-2xs);
  font-weight: 700;
}
.price__unit { color: var(--ink-3); font-size: var(--fs-xs); font-weight: 500; }
.price--sale .price__now { color: var(--danger); }
/* on a card the "% off" chip already says reduced; a red figure beside it
   doubled the alarm. The product page keeps the red. */
.product-card .price--sale .price__now { color: var(--ink); }
.price--sm .price__now { font-size: var(--fs-sm); }
.price--sm .price__old { font-size: var(--fs-xs); }
.price--lg .price__now { font-size: var(--fs-3xl); }
.price--lg .price__old { font-size: var(--fs-md); }

/* --- rating --------------------------------------------------------------- */
.rating {
  display: inline-flex;
  /* the 5 stars + score + count row needs ~155px and a 2-up card body is 106px
     at 320px, so it has to be allowed to wrap and to shrink */
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-1) var(--sp-2);
  min-inline-size: 0;
  max-inline-size: 100%;
  color: var(--ink-3);
  font-size: var(--fs-xs);
}
.rating__stars {
  display: inline-flex;
  align-items: center;
  gap: 1px;
  color: var(--star);
  line-height: 0;
}
.rating__stars > .icon { inline-size: var(--icon-sm); block-size: var(--icon-sm); }
.rating__value { color: var(--ink); font-weight: 700; }
.rating__count { color: var(--ink-3); }
/* --sm keeps the tighter gap but not a sub-floor glyph: 13px was under the
   16px icon minimum. */
.rating--sm .rating__stars > .icon { inline-size: var(--icon-sm); block-size: var(--icon-sm); }
.rating--lg .rating__stars > .icon { inline-size: var(--icon-md); block-size: var(--icon-md); }

/* Second shape: a star placed directly in .rating, with no .rating__stars
   wrapper (product.html and reviews.html both build ratings this way). Without
   a rule it inherited the muted text colour, so 61 filled stars on the reviews
   page rendered grey — the whole page looked unrated. Filled and empty are
   distinguished by the sprite symbol (#i-star-fill vs #i-star), exactly as in
   the wrapper form, so both take the same star colour. */
.rating__star { inline-size: var(--icon-sm); block-size: var(--icon-sm); color: var(--star); }
.rating--sm .rating__star { inline-size: var(--icon-sm); block-size: var(--icon-sm); }
.rating--lg .rating__star { inline-size: var(--icon-md); block-size: var(--icon-md); }
.rating--lg { font-size: var(--fs-sm); }
.rating__empty { color: var(--line-2); }

/* input variant: radios reversed in DOM (5..1) so ~ can paint the left side */
.rating--input {
  display: inline-flex;
  flex-direction: row-reverse;
  justify-content: flex-end;
  gap: var(--sp-1);
}
.rating__radio {
  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;
}
/* NOTE the scoping. `.rating__star` is two different things in this system:
   a display star (an <svg> inside .rating) and, here, the 40px <label> a
   customer clicks to leave a rating. Unscoped, the interactive rules below
   won an unqualified specificity tie against the display rules above and
   painted every filled star on reviews.html in the empty-star grey. These
   only ever apply inside .rating--input, so they now say so. */
.rating--input .rating__star {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  color: var(--line-2);
  cursor: pointer;
  transition: color var(--dur-1) var(--ease), transform var(--dur-1) var(--ease-out-back);
}
.rating--input .rating__star > .icon { inline-size: var(--icon-xl); block-size: var(--icon-xl); }
.rating--input:hover .rating__star { color: var(--star); }
.rating--input .rating__star:hover ~ .rating__star { color: var(--line-2); }
.rating__radio:checked ~ .rating__star,
.rating__radio:checked + .rating__star { color: var(--star); }
.rating--input .rating__star:hover { transform: scale(1.12); }
.rating__radio:focus-visible + .rating__star {
  outline: none;
  border-radius: var(--r-sm);
  box-shadow: 0 0 0 3px var(--brand-ring);
}

/* --- avatar --------------------------------------------------------------- */
.avatar {
  --avatar-size: 40px;
  position: relative;
  display: inline-grid;
  place-items: center;
  flex: none;
  width: var(--avatar-size);
  height: var(--avatar-size);
  border-radius: var(--r-full);
  background: var(--brand-soft);
  color: var(--brand-text);
  /* clamped at the 12px type floor: the 24px avatar computed 9.1px */
  font-size: max(var(--fs-2xs), calc(var(--avatar-size) * .38));
  font-weight: 700;
  line-height: 1;
  overflow: hidden;
  user-select: none;
}
/* Every child shares one grid cell, so an initials label can sit on top of a
   plate or a photo instead of being pushed onto a second row. */
.avatar > * { grid-area: 1 / 1; }
.avatar__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
.avatar__initials { letter-spacing: var(--ls-wide); }
/* The gradient plates in assets/img/avatars are fixed-colour artwork: they stay
   warm and light in both themes, so the label over them uses the fixed
   decorative ink rather than a theme-following one. */
.avatar:has(> .avatar__img) .avatar__initials { color: var(--avatar-ink); }
.avatar--xs { --avatar-size: 28px; }   /* 24px could not hold 12px initials */
.avatar--sm { --avatar-size: 32px; }
.avatar--lg { --avatar-size: 56px; }
.avatar--xl { --avatar-size: 88px; }
.avatar--square { border-radius: var(--r-md); }
.avatar--ring { box-shadow: 0 0 0 2px var(--surface), 0 0 0 4px var(--brand); }
.avatar__status {
  position: absolute;
  inset-block-end: 0;
  inset-inline-end: 0;
  width: 28%;
  height: 28%;
  border: 2px solid var(--surface);
  border-radius: var(--r-full);
  background: var(--success);
}
.avatar-group {
  display: inline-flex;
  align-items: center;
}
.avatar-group > .avatar {
  margin-inline-start: calc(var(--avatar-size) * -.32);
  box-shadow: 0 0 0 2px var(--surface);
}
.avatar-group > .avatar:first-child { margin-inline-start: 0; }
.avatar-group__more {
  background: var(--surface-3);
  color: var(--ink-2);
  font-size: var(--fs-2xs);
}

/* --- tooltip (CSS-only, data-tip) ---------------------------------------- */
.tooltip {
  position: relative;
}
.tooltip::after {
  content: attr(data-tip);
  position: absolute;
  z-index: var(--z-sticky);
  max-width: 240px;
  width: max-content;
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r-sm);
  background: var(--ink);
  color: var(--ink-inv);
  font-family: var(--font-sans);
  font-size: var(--fs-2xs);
  font-weight: 600;
  line-height: var(--lh-snug);
  text-align: center;
  white-space: normal;
  box-shadow: var(--sh-3);
  opacity: 0;
  visibility: hidden;
  transform: translate3d(0, 0, 0);
  transition: opacity var(--dur-2) var(--ease), transform var(--dur-2) var(--ease), visibility var(--dur-2);
  pointer-events: none;
}
.tooltip::before {
  content: "";
  position: absolute;
  z-index: var(--z-sticky);
  width: 8px;
  height: 8px;
  background: var(--ink);
  transform: rotate(45deg);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--dur-2) var(--ease), visibility var(--dur-2);
  pointer-events: none;
}
.tooltip:hover::after,
.tooltip:hover::before,
.tooltip:focus-visible::after,
.tooltip:focus-visible::before,
.tooltip.is-open::after,
.tooltip.is-open::before { opacity: 1; visibility: visible; }

/* default = top */
.tooltip::after,
.tooltip--top::after { inset-block-end: calc(100% + 10px); inset-inline-start: 50%; transform: translateX(-50%) translateY(4px); }
.tooltip:hover::after,
.tooltip--top:hover::after,
.tooltip:focus-visible::after { transform: translateX(-50%) translateY(0); }
.tooltip::before,
.tooltip--top::before { inset-block-end: calc(100% + 6px); inset-inline-start: 50%; margin-inline-start: -4px; }

.tooltip--bottom::after { inset-block-end: auto; inset-block-start: calc(100% + 10px); inset-inline-start: 50%; transform: translateX(-50%) translateY(-4px); }
.tooltip--bottom:hover::after,
.tooltip--bottom:focus-visible::after { transform: translateX(-50%) translateY(0); }
.tooltip--bottom::before { inset-block-end: auto; inset-block-start: calc(100% + 6px); inset-inline-start: 50%; margin-inline-start: -4px; }

.tooltip--left::after { inset-block-end: auto; inset-block-start: 50%; inset-inline-start: auto; inset-inline-end: calc(100% + 10px); transform: translateY(-50%) translateX(4px); }
.tooltip--left:hover::after,
.tooltip--left:focus-visible::after { transform: translateY(-50%) translateX(0); }
.tooltip--left::before { inset-block-end: auto; inset-block-start: 50%; inset-inline-start: auto; inset-inline-end: calc(100% + 6px); margin: -4px 0 0 0; }

.tooltip--right::after { inset-block-end: auto; inset-block-start: 50%; inset-inline-start: calc(100% + 10px); transform: translateY(-50%) translateX(-4px); }
.tooltip--right:hover::after,
.tooltip--right:focus-visible::after { transform: translateY(-50%) translateX(0); }
.tooltip--right::before { inset-block-end: auto; inset-block-start: 50%; inset-inline-start: calc(100% + 6px); margin: -4px 0 0 0; }

@media (hover: none) {
  .tooltip:hover::after,
  .tooltip:hover::before { opacity: 0; visibility: hidden; }
}

/* --- target size (WCAG 2.2 AA, 2.5.8) -------------------------------------
   A pointer target must be at least 24x24 CSS px. The exception is a link that
   flows inline inside a sentence, where enlarging it would break the line box.
   A link that is the whole content of a list item, table cell or definition —
   footer columns, sitemap lists, order tables — is not a sentence, so it gets
   a real hit area. Cards are already covered: their ::after stretches over the
   whole card, so the visible text can stay tight.
   -------------------------------------------------------------------------- */
@media (max-width: 900px), (pointer: coarse) {
  /* Named link-list components: these are always the whole row, so they can
     become a box with a real minimum height. */
  .footer-legal__link,
  .footer-brand__contact-item,
  .announce__link,
  .section-head__link,
  .review-item__product,
  .ord-card__link,
  .btn--link {
    display: inline-flex;
    align-items: center;
    min-block-size: 24px;
  }

  /* Every other text link gets its 24px through vertical padding. Padding on
     an inline box counts toward the hit area but NOT toward the line box, so
     prose keeps its exact layout and only the tappable region grows — which
     is why this can be applied to mid-sentence links too.

     DERIVED, not assumed. This used to be a flat 4px "because a 16px line plus
     8px is 24px exactly" — an assumption that broke the moment the type scale
     moved, leaving 116 links at exactly 23px.

     Note it must solve against the FONT metric, not `1lh`: an inline box's rect
     is its font-derived content height plus padding — line-height does not
     enter into it — so at the 12px floor the content box is 15px and needs 5px
     a side, not the 2.7px that `(24px - 1lh) / 2` would have given.

     Specificity is (0,0,1): the container list sits inside :where(), so any
     link that is already a box — .btn, .tile, .card__link, .footer-col__link —
     keeps its own padding untouched. */
  /* A LINK THAT STANDS ALONE IS NOT "IN A SENTENCE".

     WCAG 2.5.8's exception covers a target inside a sentence or block of text.
     A link that is the only thing in its table cell or its list item is not
     that — it is a control that happens to be made of words, and it needs the
     24px. These get a box, which cannot collide with a neighbour the way
     padding on an inline box does, because a box takes its own room in flow.

     :only-child is the guard that keeps this away from prose: a link with text
     beside it in the same paragraph is never matched, which is why <p> is
     deliberately absent from this list. Adding it would re-create the 6px
     overlapping tap band described below. */
  /* Headings, addresses and figure captions join the list: a link that is the
     WHOLE of one of those is a control, never a sentence. <p> is included only
     for the :only-child case — a paragraph whose entire content is one link.

     Why a box and not padding: an inline-flex element with a min-block-size is
     an atomic inline, so it grows the LINE BOX and pushes the lines around it
     apart. Padding on a plain inline does not, which is how the old blanket
     rule produced a 6px band where two links' hit areas overlapped. Measured
     after this change: 0px. */
  /* Zero-specificity (:where) since the Sep 2026 audit: as (0,1,2) this beat
     .tile, .card__link and .blog-card__link, flattening link CARDS into inline
     boxes on phones (contact tiles broke one digit per line). A link that is
     already a styled box now keeps its own layout. */
  :where(li, td, th, dd, p, div, span, h1, h2, h3, h4, h5, h6, address, figcaption) > a:where(:only-child) {
    display: inline-flex;
    align-items: center;
    min-block-size: 24px;
  }

  /* …except a product card's title link. Its ::after already stretches over the
     whole card (the real target), and as an atomic inline-flex box the name
     could not be line-clamped, so on phones long names ran to 3–4 lines and
     pushed the price rows out of line (Sep 2026 audit). */
  .product-card__title > .product-card__link { display: inline; min-block-size: 0; }

  /* A link in an auth card's row is a control sitting beside a checkbox, so it
     is never :only-child and never in a sentence. */
  .auth-card__row > a { display: inline-flex; align-items: center; min-block-size: 24px; }

  /* A phone number or an email address is a control whatever it sits next to —
     tapping it dials or composes. These often carry a label beside them in the
     same <li>, so :only-child above cannot reach them. */
  :where(li, dd, p, td, address) > a:where([href^="tel:"], [href^="mailto:"]) {
    display: inline-flex;
    align-items: center;
    min-block-size: 24px;
  }

  /* WITHDRAWN, and here is the measurement that settled it.

     This used to pad every prose link out to a 24px hit area. It is not
     needed: WCAG 2.5.8 has an explicit exception for a target "in a sentence
     or block of text", which is precisely what these are. The named standalone
     link components listed above are NOT in a sentence, which is why they keep
     their minimum and these do not.

     It was also slightly harmful. Padding an inline box does not move the line
     it sits on, so the padded hit areas of two links on ADJACENT LINES grow
     into each other. Measured per line (getClientRects, not the union box that
     getBoundingClientRect returns for a wrapped inline) on search.html at
     390px: "shop everything" and "offers page" overlapped by 6px with the
     padding, and by 0px without it. Six pixels where a tap goes to whichever
     link wins is a worse failure than a 19px target that is exactly where it
     looks and is exempt anyway.

     If a particular prose link ever does need a larger target, give that one
     link a class and a box of its own rather than padding every link on the
     site. */
}

/* --- breadcrumb ----------------------------------------------------------- */
.breadcrumb {
  display: flex;
  align-items: center;
  flex-wrap: wrap;
  gap: var(--sp-1);
  color: var(--ink-3);
  font-size: var(--fs-xs);
}
.breadcrumb__item {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
}
.breadcrumb__link {
  /* A crumb is a control, not prose, so WCAG 2.5.8's in-a-sentence exception
     does not cover it. It sits beside a separator span rather than alone in its
     <li>, which is why the generic :only-child rule cannot reach it. */
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  min-block-size: 24px;
  color: var(--ink-3);
  text-decoration: none;
  transition: color var(--dur-1) var(--ease);
}
.breadcrumb__link:hover { color: var(--ink); text-decoration: underline; text-underline-offset: .2em; }
.breadcrumb__link:focus-visible { outline: none; border-radius: var(--r-xs); box-shadow: 0 0 0 3px var(--brand-ring); }
.breadcrumb__item[aria-current] { color: var(--ink); font-weight: 600; }
.breadcrumb__sep {
  width: 14px;
  height: 14px;
  background-color: var(--ink-3);
  -webkit-mask: var(--i-chevron-right) center / contain no-repeat;
  mask: var(--i-chevron-right) center / contain no-repeat;
  opacity: .7;
}

/* --- tag list ------------------------------------------------------------- */
.tag-list {
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-2);
  list-style: none;
  margin: 0;
  padding: 0;
}
.tag {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  padding: var(--sp-1) var(--sp-3);
  border: 1px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface-2);
  color: var(--ink-2);
  font-size: var(--fs-2xs);
  font-weight: 600;
  text-decoration: none;
  transition: border-color var(--dur-1) var(--ease), color var(--dur-1) var(--ease), background-color var(--dur-1) var(--ease);
}
.tag:hover { border-color: var(--brand); background: var(--brand-soft); color: var(--brand-text); }
.tag:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--brand-ring); }
.tag.is-active { border-color: var(--brand); background: var(--brand); color: var(--brand-ink); }

/* ==========================================================================
   06. PRODUCT
   ========================================================================== */

.product-card {
  position: relative;
  display: flex;
  flex-direction: column;
  min-width: 0;
  border-radius: var(--r-lg);
  background: var(--surface);
  transition: transform var(--dur-3) var(--ease);
  container-type: inline-size;
}
@media (hover: hover) { .product-card:hover { transform: translateY(-2px); } }   /* a tapped card must not stay lifted on touch */

.product-card__media {
  position: relative;
  display: block;
  aspect-ratio: 4 / 5;
  border-radius: var(--r-lg);
  background: var(--surface-2);
  overflow: hidden;
  /* Deliberately NOT `isolation: isolate`. Isolating the media created a
     stacking context, which trapped .product-card__wish (z-index 3) inside it;
     the card-wide .product-card__link::after (z-index 1) then painted over the
     whole media block and swallowed every click on the wishlist button. Left
     un-isolated, wish (3) > link overlay (1) and the button is reachable,
     while the image itself still falls through to the product link. */
}
.product-card__img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: opacity var(--dur-4) var(--ease), transform var(--dur-4) var(--ease);
}
.product-card__img--alt { opacity: 0; }
@media (hover: hover) {
  .product-card:hover .product-card__img { transform: scale(1.04); }
  .product-card:hover .product-card__img--alt { opacity: 1; }
}

.product-card__badges {
  position: absolute;
  inset-block-start: var(--sp-3);
  inset-inline-start: var(--sp-3);
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--sp-1);
  pointer-events: none;
}

.product-card__wish {
  position: absolute;
  inset-block-start: var(--sp-3);
  inset-inline-end: var(--sp-3);
  z-index: 3;
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border: 0;
  border-radius: var(--r-full);
  background: var(--c-veil);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  color: var(--ink-2);
  cursor: pointer;
  box-shadow: var(--sh-1);
  transition: background-color var(--dur-2) var(--ease), color var(--dur-2) var(--ease), transform var(--dur-2) var(--ease-out-back);
}
.product-card__wish:hover { background: var(--surface); color: var(--danger); transform: scale(1.06); }
.product-card__wish:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--brand-ring); }
.product-card__wish.is-active { color: var(--danger); background: var(--surface); }

/* A badge pinned over artwork cannot use the dark theme's *-soft tints: those
   are mixed to sit on a dark surface (rgb(... / .16)), and the picture behind a
   product badge is light, so the plate composited pale and the label measured
   1.24:1. Painting the tint over an opaque --surface first gives a solid plate
   in both themes — light theme is unchanged, because its *-soft tints are
   already opaque. */
/* ALL THREE overlay containers, not just the product card. `.gallery__badges`
   (PDP and home-v3) and `.hc-card__badges` (home-clothing) were missed when this
   was first fixed, so in dark mode "GI Tagged" measured 1.21:1 and "21% off"
   2.13:1 over the light product artwork. If a new container ever pins a badge
   over a picture, add it here. */
:is(.product-card__badges, .gallery__badges, .hc-card__badges) .badge {
  background-color: var(--surface);
  background-image: linear-gradient(var(--badge-bg), var(--badge-bg));
}

.product-card__actions {
  position: absolute;
  inset-inline: var(--sp-3);
  inset-block-end: var(--sp-3);
  z-index: 2;
  display: flex;
  gap: var(--sp-2);
  opacity: 1;
  transform: none;
  transition: opacity var(--dur-3) var(--ease), transform var(--dur-3) var(--ease);
}
.product-card__actions > .btn { flex: 1; box-shadow: var(--sh-2); }
.product-card__quick { flex: none; width: 44px; min-width: 44px; }

@media (hover: hover) and (pointer: fine) {
  .product-card__actions {
    opacity: 0;
    transform: translateY(10px);
  }
  .product-card:hover .product-card__actions,
  .product-card:focus-within .product-card__actions {
    opacity: 1;
    transform: none;
  }
}

.product-card__oos {
  position: absolute;
  inset: 0;
  z-index: 4;
  display: none;
  place-items: center;
  background: color-mix(in oklab, var(--surface) 66%, transparent);
  backdrop-filter: blur(2px);
  -webkit-backdrop-filter: blur(2px);
}
.product-card__oos-label {
  padding: var(--sp-2) var(--sp-4);
  border-radius: var(--r-full);
  background: var(--ink);
  color: var(--ink-inv);
  font-size: var(--fs-2xs);
  font-weight: 700;
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
}
.product-card.is-soldout .product-card__oos { display: grid; }
.product-card.is-soldout .product-card__actions { display: none; }
.product-card.is-soldout .product-card__img { filter: saturate(.35); }

.product-card__body {
  /* A FLEX column, not a grid. As a grid with align-content:start the free
     space was distributed before any item could claim it, so the auto margin
     on .product-card__swatches had nothing left to take and the swatch row
     still sat 28px higher on cards with no stock note. In a flex column the
     auto margin takes the slack directly. */
  display: flex;
  flex-direction: column;
  gap: var(--sp-1);
  padding: var(--sp-3) var(--sp-1) 0;
  flex: 1 1 auto;
}
.product-card__cat {
  color: var(--ink-3);
  font-size: var(--fs-2xs);
  font-weight: 700;
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
}
.product-card__title {
  color: var(--ink);
  font-size: var(--fs-sm);
  font-weight: 600;
  line-height: var(--lh-snug);
  display: -webkit-box;
  /* THREE lines, not two. A four-up grid gives each card about 19 characters
     to a line, and five of the eight titles here carry a variant after an
     em dash — "… — Whole Bean, 250g", "… — 70 x 200 cm" — so a two-line clamp
     truncated the half of the name that tells them apart. Three lines fit
     every title in the catalogue with room to spare. */
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
  /* TWO lines reserved, not three and not none. With no reservation a
     one-line name lifted its rating and price 20px above the neighbours in the
     same row (Sep 2026 audit, /shop and every rail). Three reserved lines left
     a blank line under the usual two-line name; two is the common case, so a
     row's prices share a baseline and only the rare three-line name drops. */
  min-block-size: calc(2em * var(--lh-snug));
  text-wrap: balance;
}
.product-card__link {
  color: inherit;
  text-decoration: none;
}
.product-card__link::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
}
.product-card__link:focus-visible { outline: none; }
.product-card:focus-within .product-card__media { box-shadow: 0 0 0 3px var(--brand-ring); }
.product-card__rating { margin-top: 2px; }
.product-card__price { margin-top: var(--sp-1); }
.product-card__swatches {
  display: flex;
  align-items: center;
  gap: var(--sp-1);
  margin-top: var(--sp-2);
  position: relative;
  z-index: 2;
}
.product-card__meta {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-2);
}
/* This line carries a stock state when there is one and nothing when there is
   not. It used to collapse when empty — see ROW ALIGNMENT below for why it
   now holds its 24px instead. */

@container (min-width: 320px) {
  .product-card__title { font-size: var(--fs-md); }
}

/* horizontal card — cart, wishlist, search results */
.product-card--list {
  flex-direction: row;
  align-items: flex-start;
  gap: var(--sp-4);
  padding: var(--sp-3);
  border: 1px solid var(--line);
  container-type: normal;
}
.product-card--list:hover { transform: none; border-color: var(--line-2); }
.product-card--list .product-card__media {
  flex: none;
  width: 112px;
  aspect-ratio: 1;
  border-radius: var(--r-md);
}
.product-card--list .product-card__body { padding: 0; flex: 1; min-width: 0; }
.product-card--list .product-card__actions,
.product-card--list .product-card__wish { position: static; opacity: 1; transform: none; }
.product-card--list .product-card__wish { box-shadow: none; background: transparent; backdrop-filter: none; }
/* The list view is a wide row with the description underneath, so a title has
   plenty of measure and two lines is still right — but it must not inherit the
   three-line reserve the grid card now sets. */
.product-card--list .product-card__title { -webkit-line-clamp: 2; min-block-size: 0; }
.product-card--list .product-card__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  margin-top: var(--sp-3);
}

/* mini card — cart drawer lines */
.product-card--mini {
  flex-direction: row;
  align-items: center;
  gap: var(--sp-3);
  padding: 0;
  container-type: normal;
}
.product-card--mini:hover { transform: none; }
.product-card--mini .product-card__media {
  flex: none;
  width: 64px;
  aspect-ratio: 1;
  border-radius: var(--r-sm);
}
.product-card--mini .product-card__body { padding: 0; gap: 2px; min-width: 0; }
.product-card--mini .product-card__title { font-size: var(--fs-xs); -webkit-line-clamp: 1; min-block-size: 0; }
.product-card--mini .product-card__actions,
.product-card--mini .product-card__wish { display: none; }

/* --- swatches ------------------------------------------------------------- */
.swatch-list {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2);
}
.swatch {
  position: relative;
  display: inline-grid;
  place-items: center;
  min-width: 44px;
  min-height: 44px;
  padding: 0 var(--sp-3);
  border: 1.5px solid var(--line);
  border-radius: var(--r-sm);
  background: var(--surface);
  color: var(--ink-2);
  font-size: var(--fs-xs);
  font-weight: 600;
  cursor: pointer;
  user-select: none;
  -webkit-tap-highlight-color: transparent;
  transition: border-color var(--dur-2) var(--ease), color var(--dur-2) var(--ease), background-color var(--dur-2) var(--ease), transform var(--dur-1) var(--ease);
}
.swatch:hover { border-color: var(--ink-3); color: var(--ink); }
.swatch:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--brand-ring); }
.swatch.is-selected {
  border-color: var(--brand);
  background: var(--brand-soft);
  color: var(--brand-text);
}
.swatch.is-disabled,
.swatch[disabled] {
  opacity: .45;
  cursor: not-allowed;
  pointer-events: none;
}
.swatch.is-soldout {
  color: var(--ink-3);
  cursor: not-allowed;
  overflow: hidden;
}
.swatch.is-soldout::after {
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(
    to top left,
    transparent calc(50% - 1px),
    var(--line-2) calc(50% - 1px),
    var(--line-2) calc(50% + 1px),
    transparent calc(50% + 1px)
  );
}

.swatch--color {
  --swatch-color: var(--surface-3);
  min-width: 0;
  width: 30px;
  height: 30px;
  min-height: 30px;
  padding: 3px;
  border-radius: var(--r-full);
  border-color: transparent;
  background: transparent;
  box-shadow: inset 0 0 0 1px var(--line);
}
.swatch--color::before {
  content: "";
  display: block;
  width: 100%;
  height: 100%;
  border-radius: var(--r-full);
  background: var(--swatch-color);
  box-shadow: inset 0 0 0 1px color-mix(in oklab, var(--ink) 12%, transparent);
}
.swatch--color:hover { box-shadow: inset 0 0 0 1px var(--ink-3); }
.swatch--color.is-selected {
  background: transparent;
  border-color: var(--brand);
  box-shadow: none;
}
.swatch--color.is-soldout::after { border-radius: var(--r-full); }

.swatch--size {
  min-width: 52px;
  border-radius: var(--r-sm);
  font-variant-numeric: tabular-nums;
}

.swatch--image {
  width: 64px;
  min-width: 64px;
  height: 78px;
  min-height: 78px;
  padding: 2px;
  border-radius: var(--r-sm);
  overflow: hidden;
}
.swatch--image > img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  border-radius: calc(var(--r-sm) - 2px);
  display: block;
}
.swatch--image.is-selected { background: transparent; }
.swatch--dot {
  min-width: 0;
  width: 14px;
  height: 14px;
  min-height: 14px;
  padding: 0;
  border-radius: var(--r-full);
  border-width: 1px;
  background: var(--swatch-color, var(--surface-3));
  cursor: default;
}

/* --- gallery -------------------------------------------------------------- */
.gallery {
  display: grid;
  gap: var(--sp-3);
}
.gallery__main { position: relative; min-width: 0; }
.gallery__frame {
  position: relative;
  aspect-ratio: 4 / 5;
  border-radius: var(--r-xl);
  background: var(--surface-2);
  overflow: hidden;
  cursor: zoom-in;
}
.gallery__img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform var(--dur-4) var(--ease);
}
.gallery__frame:hover .gallery__img { transform: scale(1.06); }
.gallery__frame.is-zoomed { cursor: zoom-out; }
.gallery__frame.is-zoomed .gallery__img { transform: scale(1.9); transition: transform var(--dur-2) var(--ease); }

.gallery__badges {
  position: absolute;
  inset-block-start: var(--sp-4);
  inset-inline-start: var(--sp-4);
  z-index: 2;
  display: flex;
  flex-direction: column;
  align-items: flex-start;
  gap: var(--sp-1);
}
.gallery__zoom {
  position: absolute;
  inset-block-end: var(--sp-4);
  inset-inline-end: var(--sp-4);
  z-index: 2;
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border: 0;
  border-radius: var(--r-full);
  background: var(--c-veil);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  color: var(--ink);
  box-shadow: var(--sh-2);
  cursor: pointer;
}
.gallery__zoom:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--brand-ring); }

.gallery__nav {
  position: absolute;
  inset-block-start: 50%;
  z-index: 2;
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  transform: translateY(-50%);
  border: 0;
  border-radius: var(--r-full);
  background: var(--c-veil);
  backdrop-filter: blur(8px);
  -webkit-backdrop-filter: blur(8px);
  color: var(--ink);
  box-shadow: var(--sh-2);
  cursor: pointer;
}
.gallery__nav--prev { inset-inline-start: var(--sp-3); }
.gallery__nav--next { inset-inline-end: var(--sp-3); }
.gallery__nav:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--brand-ring); }

.gallery__thumbs {
  display: flex;
  gap: var(--sp-2);
  overflow-x: auto;
  scroll-snap-type: x mandatory;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  padding-bottom: var(--sp-1);
}
.gallery__thumbs::-webkit-scrollbar { display: none; }
.gallery__thumb {
  flex: none;
  width: 72px;
  aspect-ratio: 4 / 5;
  padding: 0;
  border: 1.5px solid transparent;
  border-radius: var(--r-md);
  background: var(--surface-2);
  overflow: hidden;
  cursor: pointer;
  scroll-snap-align: start;
  transition: border-color var(--dur-2) var(--ease), opacity var(--dur-2) var(--ease);
  opacity: .72;
}
.gallery__thumb > img { width: 100%; height: 100%; object-fit: cover; display: block; }
.gallery__thumb:hover { opacity: 1; }
.gallery__thumb:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--brand-ring); opacity: 1; }
.gallery__thumb.is-selected { border-color: var(--brand); opacity: 1; }

.gallery__dots {
  display: flex;
  justify-content: center;
  gap: 0;   /* each 24px button already carries 8px of clear space per side */
}
/* The dot LOOKS like an 8px pill (22px when selected) but the button itself is
   a 24px square, because an 8px target fails WCAG 2.5.8 and is genuinely hard
   to hit on a phone. The pill is drawn by ::after so the visual rhythm of the
   row is unchanged; the container drops its gap to keep the same pitch. */
.gallery__dot {
  position: relative;
  width: 24px;
  height: 24px;
  padding: 0;
  border: 0;
  background: none;
  cursor: pointer;
}
.gallery__dot::after {
  content: "";
  position: absolute;
  inset-block-start: 50%;
  inset-inline-start: 50%;
  width: 8px;
  height: 8px;
  transform: translate(-50%, -50%);
  border-radius: var(--r-full);
  background: var(--line-2);
  transition: width var(--dur-2) var(--ease), background-color var(--dur-2) var(--ease);
}
.gallery__dot.is-selected::after { width: 22px; background: var(--brand); }
.gallery__dot:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--brand-ring); }

@media (min-width: 900px) {
  .gallery--rail {
    grid-template-columns: 84px 1fr;
    align-items: start;
    gap: var(--sp-4);
  }
  .gallery--rail .gallery__thumbs {
    flex-direction: column;
    max-height: 640px;
    overflow-y: auto;
    overflow-x: hidden;
    scroll-snap-type: y mandatory;
  }
  .gallery--rail .gallery__thumb { width: 100%; }
  .gallery--rail .gallery__dots { display: none; }
  .gallery--rail .gallery__main { order: 2; }
  .gallery--rail .gallery__thumbs { order: 1; }
}

/* --- review item ---------------------------------------------------------- */
/* A column, not a grid. The grid version had align-content: start so that a
   stretched card would not spread its rows apart — but that also meant the
   footer stopped wherever the body happened to end, so eight cards in a row
   had their "Helpful" buttons at eight different heights, one of them with
   118px of dead space under it. A flex column keeps the rows tight AND lets
   the footer take the slack with margin-block-start: auto. */
.review-item {
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  padding: var(--sp-5) 0;
  border-bottom: 1px solid var(--line);
}
/* As a bare list item (the product page) it is separated by a rule and sits on
   the page's own measure. As a .card it has a border of its own — and then it
   needs inset, or the avatar and the body text sit flush against that border. */
.card.review-item {
  padding: var(--sp-5);
  border-bottom: 0;
}

/* THE HEADER CANNOT BE ALLOWED TO REFLOW.
   This was a wrapping flex row: avatar, then a block holding the name (with the
   "Verified purchase" badge inline) and the date. At a 285px card the name plus
   that badge measures about 228px against 229px of room, so whether it fitted
   came down to the length of the reviewer's name — "Aarti Nair" fitted and the
   header was 45px tall, "Rohan Mehta" did not and the whole name block wrapped
   BELOW the avatar, making the header 97px. Everything under it — stars, title,
   body, footer — was then 52px out of step with the card beside it.

   Two columns fix it for good: the avatar owns column one whatever happens, and
   column two stacks name, badge and date. Same three lines on every card. */
.review-item__head {
  display: grid;
  grid-template-columns: auto minmax(0, 1fr);
  align-items: start;
  gap: var(--sp-3);
}
.review-item__meta { display: grid; gap: 2px; min-width: 0; }
.review-item__name {
  display: grid;
  justify-items: start;
  gap: var(--sp-1);
  min-inline-size: 0;
  color: var(--ink);
  font-size: var(--fs-sm);
  font-weight: 700;
}
.review-item__verified {
  display: inline-flex;
  align-items: center;
  gap: 3px;
  color: var(--success);
  font-size: var(--fs-2xs);
  font-weight: 700;
}
.review-item__date { color: var(--ink-3); font-size: var(--fs-2xs); }
/* Two lines reserved, so the body copy of every card in a row starts on the
   same line whether the headline ran to one line or two. */
.review-item__title {
  color: var(--ink);
  font-size: var(--fs-md);
  font-weight: 700;
  line-height: var(--lh-snug);
  min-block-size: calc(2 * var(--lh-snug) * 1em);
}
.review-item__body { color: var(--ink-2); font-size: var(--fs-sm); line-height: var(--lh-relaxed); }
.review-item__media {
  display: flex;
  gap: var(--sp-2);
  flex-wrap: wrap;
}
.review-item__media img {
  width: 72px;
  height: 72px;
  object-fit: cover;
  border-radius: var(--r-sm);
  display: block;
  cursor: zoom-in;
}
/* The product tag reserves two lines. "Pure Pashmina Solid Stole" fits on one
   and "Sozni Hand-Embroidered Pashmina Shawl" needs two, which made the footer
   of that one card 19px taller than its neighbours — and since the footer is
   pinned to the bottom edge, a taller footer starts higher and the "Helpful"
   buttons stopped agreeing across the row. */
.review-item__product {
  display: flex;
  align-items: flex-start;
  gap: var(--sp-1);
  min-block-size: calc(2 * var(--lh-snug) * 1em);
  line-height: var(--lh-snug);
}
.review-item__product > .icon { margin-block-start: 1px; }

.review-item__foot {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  flex-wrap: wrap;
  gap: var(--sp-2) var(--sp-4);
  /* Takes the slack in the column, so it sits on the bottom edge of the card
     rather than wherever the review copy happened to stop. */
  margin-block-start: auto;
  padding-block-start: var(--sp-1);
  color: var(--ink-3);
  font-size: var(--fs-xs);
}
.review-item__helpful {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  min-height: 32px;
  padding: 0 var(--sp-3);
  border: 1px solid var(--line);
  border-radius: var(--r-full);
  background: transparent;
  color: var(--ink-2);
  font-size: var(--fs-2xs);
  font-weight: 600;
  cursor: pointer;
  transition: border-color var(--dur-1) var(--ease), color var(--dur-1) var(--ease);
}
.review-item__helpful:hover { border-color: var(--brand); color: var(--brand-text); }
.review-item__helpful:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--brand-ring); }
.review-item__helpful.is-active { border-color: var(--brand); background: var(--brand-soft); color: var(--brand-text); }

/* --- Q&A ------------------------------------------------------------------ */
.qa-item {
  display: grid;
  gap: var(--sp-3);
  padding: var(--sp-5) 0;
  border-bottom: 1px solid var(--line);
}
.qa-item__q,
.qa-item__a {
  display: grid;
  grid-template-columns: 28px 1fr;
  gap: var(--sp-3);
  align-items: start;
}
.qa-item__q::before,
.qa-item__a::before {
  content: "Q";
  display: grid;
  place-items: center;
  width: 28px;
  height: 28px;
  border-radius: var(--r-full);
  background: var(--brand-soft);
  color: var(--brand-text);
  font-size: var(--fs-2xs);
  font-weight: 800;
}
.qa-item__a::before { content: "A"; background: var(--surface-3); color: var(--ink-2); }
.qa-item__q { color: var(--ink); font-size: var(--fs-md); font-weight: 600; line-height: var(--lh-snug); }
.qa-item__a { color: var(--ink-2); font-size: var(--fs-sm); line-height: var(--lh-relaxed); }
.qa-item__meta {
  grid-column: 2;
  color: var(--ink-3);
  font-size: var(--fs-2xs);
}

/* --- compare table -------------------------------------------------------- */
.compare-table {
  width: 100%;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
}
.compare-table table {
  width: 100%;
  min-width: 720px;
  border-collapse: collapse;
  font-size: var(--fs-sm);
}
.compare-table th,
.compare-table td {
  padding: var(--sp-4);
  border-bottom: 1px solid var(--line);
  text-align: start;
  vertical-align: top;
}
.compare-table tr:last-child th,
.compare-table tr:last-child td { border-bottom: 0; }
.compare-table__label {
  position: sticky;
  inset-inline-start: 0;
  z-index: 1;
  min-width: 160px;
  background: var(--surface-2);
  color: var(--ink-2);
  font-size: var(--fs-xs);
  font-weight: 700;
  letter-spacing: var(--ls-wide);
}
.compare-table__head {
  background: var(--surface);
  color: var(--ink);
}
.compare-table__cell { color: var(--ink-2); }
.compare-table__cell.is-best { background: var(--success-soft); color: var(--ink); font-weight: 600; }
.compare-table__product { display: grid; gap: var(--sp-2); min-width: 180px; }
.compare-table__product img {
  width: 100%;
  aspect-ratio: 4 / 5;
  object-fit: cover;
  border-radius: var(--r-md);
  background: var(--surface-2);
  display: block;
}
.compare-table__remove {
  justify-self: end;
  display: grid;
  place-items: center;
  width: 30px;
  height: 30px;
  border: 1px solid var(--line);
  border-radius: var(--r-full);
  background: var(--surface);
  color: var(--ink-3);
  cursor: pointer;
}
.compare-table__remove:hover { border-color: var(--danger); color: var(--danger); }
.compare-table__remove:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--brand-ring); }
.compare-table tbody tr:nth-child(even) td { background: color-mix(in oklab, var(--surface-2) 55%, transparent); }

/* --- cart line ------------------------------------------------------------ */
.cart-line {
  display: grid;
  grid-template-columns: 88px 1fr auto;
  gap: var(--sp-4);
  align-items: start;
  padding: var(--sp-4) 0;
  border-bottom: 1px solid var(--line);
}
.cart-line:last-child { border-bottom: 0; }
.cart-line__media {
  position: relative;
  width: 88px;
  aspect-ratio: 1;
  border-radius: var(--r-md);
  background: var(--surface-2);
  overflow: hidden;
}
.cart-line__media img { width: 100%; height: 100%; object-fit: cover; display: block; }
.cart-line__body { display: grid; gap: var(--sp-1); min-width: 0; }
.cart-line__title {
  color: var(--ink);
  font-size: var(--fs-sm);
  font-weight: 600;
  line-height: var(--lh-snug);
  text-decoration: none;
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.cart-line__title:hover { color: var(--brand-text); }
.cart-line__variant { color: var(--ink-3); font-size: var(--fs-xs); }
/* --fs-2xs is reserved for badges and uppercase labels; the stock note, the
   per-unit price and the line actions are sentence-case meta, so --fs-xs. */
.cart-line__stock { color: var(--success); font-size: var(--fs-xs); font-weight: 600; }
.cart-line__controls,
.cart-line__foot {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  margin-top: var(--sp-2);
  flex-wrap: wrap;
}
.cart-line__price {
  display: grid;
  gap: 2px;
  justify-items: end;
  text-align: end;
  font-variant-numeric: tabular-nums;
}
.cart-line__amount { color: var(--ink); font-size: var(--fs-sm); font-weight: 700; }
.cart-line__each { color: var(--ink-3); font-size: var(--fs-xs); }
.cart-line__remove {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  min-height: 32px;
  padding: 0 var(--sp-2);
  border: 0;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--ink-3);
  font-size: var(--fs-xs);
  font-weight: 600;
  cursor: pointer;
  transition: color var(--dur-1) var(--ease), background-color var(--dur-1) var(--ease);
}
.cart-line__remove:hover { background: var(--danger-soft); color: var(--danger); }
.cart-line__remove:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--brand-ring); }
.cart-line.is-loading { opacity: .5; pointer-events: none; }
.cart-line--compact { grid-template-columns: 64px 1fr auto; gap: var(--sp-3); }
.cart-line--compact .cart-line__media { width: 64px; }

/* --- order summary -------------------------------------------------------- */
.order-summary {
  display: grid;
  gap: var(--sp-3);
  padding: var(--sp-5);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
}
/* Always an <h2>, so the base heading rule handed it Instrument Serif — which
   at 16px read as a smudge next to the sans figures under it. It is a panel
   label: sans, one step up from body. */
.order-summary__title {
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--fs-lg);
  font-weight: 700;
  letter-spacing: var(--ls-tight);
}
.order-summary__row {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-4);
  font-size: var(--fs-sm);
  font-variant-numeric: tabular-nums;
}
.order-summary__label { color: var(--ink-2); }
.order-summary__value { color: var(--ink); font-weight: 600; }
.order-summary__row--discount .order-summary__value { color: var(--success); }
.order-summary__row--muted .order-summary__value { color: var(--ink-3); font-weight: 500; }
.order-summary__total {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: var(--sp-4);
  padding-top: var(--sp-3);
  border-top: 1px solid var(--line);
  font-variant-numeric: tabular-nums;
}
.order-summary__total .order-summary__label { color: var(--ink); font-size: var(--fs-md); font-weight: 700; }
.order-summary__total .order-summary__value { color: var(--ink); font-size: var(--fs-2xl); font-weight: 700; letter-spacing: var(--ls-tight); }
.order-summary__foot { display: grid; gap: var(--sp-3); margin-top: var(--sp-1); }
.order-summary__note { color: var(--ink-3); font-size: var(--fs-xs); line-height: var(--lh-snug); text-align: center; }
.order-summary--sticky { position: sticky; top: calc(var(--header-h) + var(--sp-4)); }

/* --- coupon form ---------------------------------------------------------- */
.coupon-form { display: grid; gap: var(--sp-2); }
.coupon-form__row { display: flex; gap: var(--sp-2); }
.coupon-form__input {
  flex: 1;
  min-width: 0;
  text-transform: uppercase;
  letter-spacing: var(--ls-wide);
  font-weight: 600;
}
.coupon-form__btn { flex: none; }
.coupon-form__applied {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  border: 1px dashed var(--success);
  border-radius: var(--r-sm);
  background: var(--success-soft);
  color: var(--success);
  font-size: var(--fs-xs);
  font-weight: 700;
}
.coupon-form__code { letter-spacing: var(--ls-wide); }
.coupon-form__msg { font-size: var(--fs-xs); }
.coupon-form.has-error .coupon-form__msg { color: var(--danger); }

/* ==========================================================================
   07. NAVIGATION & OVERLAY
   ========================================================================== */

/* --- tabs ----------------------------------------------------------------- */
.tabs { display: grid; gap: var(--sp-5); }
.tabs__list {
  display: flex;
  align-items: center;
  gap: var(--sp-1);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  scroll-snap-type: x proximity;
}
.tabs__list::-webkit-scrollbar { display: none; }
.tabs__tab {
  flex: none;
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  min-height: 44px;
  padding: 0 var(--sp-4);
  border: 0;
  border-radius: var(--r-full);
  background: transparent;
  color: var(--ink-3);
  font-family: var(--font-sans);
  font-size: var(--fs-sm);
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
  scroll-snap-align: start;
  transition: color var(--dur-2) var(--ease), background-color var(--dur-2) var(--ease), box-shadow var(--dur-2) var(--ease);
}
.tabs__tab:hover { color: var(--ink); background: var(--c-hover); }
.tabs__tab:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--brand-ring); }
.tabs__tab[aria-selected="true"],
.tabs__tab.is-active { color: var(--ink); }
.tabs__count {
  padding: 1px var(--sp-2);
  border-radius: var(--r-full);
  background: var(--surface-3);
  color: var(--ink-3);
  font-size: var(--fs-2xs);
  font-weight: 700;
  font-variant-numeric: tabular-nums;
}
.tabs__panel { min-width: 0; }
.tabs__panel[hidden] { display: none; }

.tabs--pill .tabs__list {
  gap: var(--sp-1);
  padding: var(--sp-1);
  border-radius: var(--r-full);
  background: var(--surface-2);
}
.tabs--pill .tabs__tab[aria-selected="true"],
.tabs--pill .tabs__tab.is-active {
  background: var(--surface);
  box-shadow: var(--sh-1);
}
.tabs--pill .tabs__tab[aria-selected="true"] .tabs__count { background: var(--brand-soft); color: var(--brand-text); }

.tabs--underline .tabs__list {
  gap: var(--sp-5);
  border-bottom: 1px solid var(--line);
}
.tabs--underline .tabs__tab {
  position: relative;
  padding: 0 0 var(--sp-3);
  border-radius: 0;
  min-height: 44px;
  align-items: flex-end;
}
.tabs--underline .tabs__tab:hover { background: transparent; }
.tabs--underline .tabs__tab::after {
  content: "";
  position: absolute;
  inset-inline: 0;
  inset-block-end: -1px;
  height: 2px;
  border-radius: var(--r-full);
  background: var(--brand);
  transform: scaleX(0);
  transform-origin: center;
  transition: transform var(--dur-2) var(--ease);
}
.tabs--underline .tabs__tab[aria-selected="true"]::after,
.tabs--underline .tabs__tab.is-active::after { transform: scaleX(1); }

.tabs--scroll .tabs__list { padding-inline-end: var(--sp-6); }
.tabs--fill .tabs__list { display: grid; grid-auto-flow: column; grid-auto-columns: 1fr; }
.tabs--fill .tabs__tab { justify-content: center; }

/* --- accordion ------------------------------------------------------------ */
.accordion { display: grid; }
.accordion__item { border-bottom: 1px solid var(--line); }
.accordion__trigger {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  width: 100%;
  min-height: 56px;
  padding: var(--sp-4) 0;
  border: 0;
  background: transparent;
  color: var(--ink);
  font-family: var(--font-sans);
  font-size: var(--fs-md);
  font-weight: 600;
  line-height: var(--lh-snug);
  text-align: start;
  cursor: pointer;
  transition: color var(--dur-2) var(--ease);
}
.accordion__trigger:hover { color: var(--brand-text); }
.accordion__trigger:focus-visible { outline: none; border-radius: var(--r-sm); box-shadow: 0 0 0 3px var(--brand-ring); }
.accordion__icon {
  flex: none;
  width: 20px;
  height: 20px;
  background-color: var(--ink-3);
  -webkit-mask: var(--i-chevron-down) center / contain no-repeat;
  mask: var(--i-chevron-down) center / contain no-repeat;
  transition: transform var(--dur-3) var(--ease), background-color var(--dur-2) var(--ease);
}
.accordion__trigger[aria-expanded="true"] .accordion__icon { transform: rotate(180deg); background-color: var(--brand); }
.accordion__panel {
  display: grid;
  grid-template-rows: 0fr;
  transition: grid-template-rows var(--dur-3) var(--ease);
}
.accordion__panel > * { overflow: hidden; min-height: 0; }
.accordion__item.is-open > .accordion__panel { grid-template-rows: 1fr; }
.accordion__body {
  padding-bottom: var(--sp-5);
  color: var(--ink-2);
  font-size: var(--fs-sm);
  line-height: var(--lh-relaxed);
}
.accordion--boxed .accordion__item {
  margin-bottom: var(--sp-2);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
}
.accordion--boxed .accordion__trigger { padding-inline: var(--sp-4); }
.accordion--boxed .accordion__body { padding-inline: var(--sp-4); }

/* --- drawer --------------------------------------------------------------- */
.drawer {
  position: fixed;
  inset: 0;
  z-index: var(--z-drawer);
  visibility: hidden;
  pointer-events: none;
}
.drawer.is-open { visibility: visible; pointer-events: auto; }
.drawer__backdrop {
  position: absolute;
  inset: 0;
  background: var(--overlay);
  opacity: 0;
  transition: opacity var(--dur-3) var(--ease);
}
.drawer.is-open .drawer__backdrop { opacity: 1; }
.drawer__panel {
  position: absolute;
  display: flex;
  flex-direction: column;
  background: var(--bg);
  box-shadow: var(--sh-4);
  transition: transform var(--dur-3) var(--ease), opacity var(--dur-3) var(--ease);
  will-change: transform;
  overscroll-behavior: contain;
}
.drawer--right .drawer__panel {
  inset-block: 0;
  inset-inline-end: 0;
  width: min(440px, 100vw);
  transform: translateX(100%);
  border-start-start-radius: var(--r-xl);
  border-end-start-radius: var(--r-xl);
}
.drawer--left .drawer__panel {
  inset-block: 0;
  inset-inline-start: 0;
  width: min(400px, 88vw);
  transform: translateX(-100%);
  border-start-end-radius: var(--r-xl);
  border-end-end-radius: var(--r-xl);
}
.drawer--bottom .drawer__panel {
  inset-inline: 0;
  inset-block-end: 0;
  max-height: 88vh;
  max-height: 88dvh;           /* the visible viewport: a phone's toolbar otherwise hides the footer */
  transform: translateY(100%);
  border-start-start-radius: var(--r-xl);
  border-start-end-radius: var(--r-xl);
  padding-bottom: var(--safe-b);
}
.drawer.is-open .drawer__panel { transform: none; }

.drawer__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  flex: none;
  min-height: var(--header-h-sm);
  padding: var(--sp-4) var(--sp-5);
  border-bottom: 1px solid var(--line);
}
.drawer__title {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  color: var(--ink);
  /* the title inherits the condensed display serif from h2, which at 16px read
     smaller than the line items under it */
  font-size: var(--fs-xl);
  font-weight: 700;
  letter-spacing: var(--ls-tight);
}
.drawer__close {
  display: grid;
  place-items: center;
  flex: none;
  width: 40px;
  height: 40px;
  border: 0;
  border-radius: var(--r-full);
  background: transparent;
  color: var(--ink-2);
  cursor: pointer;
  transition: background-color var(--dur-2) var(--ease), color var(--dur-2) var(--ease);
}
.drawer__close:hover { background: var(--c-hover); color: var(--ink); }
.drawer__close:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--brand-ring); }
.drawer__body {
  flex: 1;
  min-height: 0;
  padding: var(--sp-5);
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}
.drawer__foot {
  flex: none;
  display: grid;
  gap: var(--sp-3);
  padding: var(--sp-5);
  padding-bottom: calc(var(--sp-5) + var(--safe-b));
  border-top: 1px solid var(--line);
  background: var(--surface);
}
.drawer--wide .drawer__panel { width: min(560px, 100vw); }

/* --- sheet (mobile bottom sheet) ----------------------------------------- */
.sheet {
  position: fixed;
  inset: 0;
  z-index: var(--z-drawer);
  visibility: hidden;
  pointer-events: none;
}
.sheet.is-open { visibility: visible; pointer-events: auto; }
.sheet__backdrop {
  position: absolute;
  inset: 0;
  background: var(--overlay);
  opacity: 0;
  transition: opacity var(--dur-3) var(--ease);
}
.sheet.is-open .sheet__backdrop { opacity: 1; }
.sheet__panel {
  position: absolute;
  inset-inline: 0;
  inset-block-end: 0;
  display: flex;
  flex-direction: column;
  max-height: 90vh;
  max-height: 90dvh;
  padding-bottom: var(--safe-b);
  border-start-start-radius: var(--r-xl);
  border-start-end-radius: var(--r-xl);
  background: var(--bg);
  box-shadow: var(--sh-4);
  transform: translateY(100%);
  transition: transform var(--dur-3) var(--ease);
  will-change: transform;
  overscroll-behavior: contain;
  touch-action: pan-y;
}
.sheet.is-open .sheet__panel { transform: none; }
.sheet.is-dragging .sheet__panel { transition: none; }
.sheet__handle {
  flex: none;
  display: grid;
  place-items: center;
  padding: var(--sp-3) 0 var(--sp-2);
  cursor: grab;
  touch-action: none;
}
.sheet__handle::before {
  content: "";
  width: 44px;
  height: 5px;
  border-radius: var(--r-full);
  background: var(--line-2);
}
.sheet__handle:active { cursor: grabbing; }
.sheet__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-3);
  flex: none;
  padding: var(--sp-2) var(--sp-5) var(--sp-4);
  border-bottom: 1px solid var(--line);
}
.sheet__title { color: var(--ink); font-size: var(--fs-md); font-weight: 700; }
.sheet__close {
  display: grid;
  place-items: center;
  width: 36px;
  height: 36px;
  border: 0;
  border-radius: var(--r-full);
  background: var(--surface-2);
  color: var(--ink-2);
  cursor: pointer;
}
.sheet__close:hover { background: var(--surface-3); color: var(--ink); }
.sheet__close:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--brand-ring); }
.sheet__body {
  flex: 1;
  min-height: 0;
  padding: var(--sp-5);
  overflow-y: auto;
  overscroll-behavior: contain;
  -webkit-overflow-scrolling: touch;
}
.sheet__foot {
  flex: none;
  display: flex;
  gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-5);
  border-top: 1px solid var(--line);
  background: var(--surface);
}
.sheet__foot > .btn { flex: 1; }
.sheet--tall .sheet__panel { height: 90vh; height: 90dvh; }

/* --- modal ---------------------------------------------------------------- */
.modal {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal);
  display: grid;
  place-items: center;
  padding: var(--sp-4);
  visibility: hidden;
  pointer-events: none;
}
.modal.is-open { visibility: visible; pointer-events: auto; }
.modal__backdrop {
  position: absolute;
  inset: 0;
  background: var(--overlay);
  opacity: 0;
  transition: opacity var(--dur-3) var(--ease);
}
.modal.is-open .modal__backdrop { opacity: 1; }
.modal__dialog {
  position: relative;
  display: flex;
  flex-direction: column;
  width: min(560px, 100%);
  max-height: min(86vh, 900px);
  max-height: min(86dvh, 900px);
  border-radius: var(--r-xl);
  background: var(--bg);
  box-shadow: var(--sh-4);
  opacity: 0;
  transform: translateY(12px) scale(.98);
  transition: opacity var(--dur-3) var(--ease), transform var(--dur-3) var(--ease-out-back);
  overflow: hidden;
}
.modal.is-open .modal__dialog { opacity: 1; transform: none; }
.modal__head {
  display: flex;
  align-items: flex-start;
  justify-content: space-between;
  gap: var(--sp-4);
  flex: none;
  padding: var(--sp-6) var(--sp-6) var(--sp-4);
}
.modal__title {
  color: var(--ink);
  font-family: var(--font-display);
  font-size: var(--fs-2xl);
  font-weight: 400;
  line-height: var(--lh-tight);
}
.modal__sub { color: var(--ink-3); font-size: var(--fs-sm); margin-top: var(--sp-1); }
.modal__close {
  display: grid;
  place-items: center;
  flex: none;
  width: 40px;
  height: 40px;
  border: 0;
  border-radius: var(--r-full);
  background: var(--surface-2);
  color: var(--ink-2);
  cursor: pointer;
  transition: background-color var(--dur-2) var(--ease), color var(--dur-2) var(--ease);
}
.modal__close:hover { background: var(--surface-3); color: var(--ink); }
.modal__close:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--brand-ring); }
.modal__body {
  flex: 1;
  min-height: 0;
  padding: 0 var(--sp-6) var(--sp-6);
  overflow-y: auto;
  overscroll-behavior: contain;
  color: var(--ink-2);
  font-size: var(--fs-sm);
  line-height: var(--lh-relaxed);
}
.modal__foot {
  flex: none;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: var(--sp-3);
  padding: var(--sp-4) var(--sp-6);
  border-top: 1px solid var(--line);
  background: var(--surface);
}
.modal--sm .modal__dialog { width: min(400px, 100%); }
.modal--lg .modal__dialog { width: min(880px, 100%); }
.modal--full .modal__dialog { width: 100%; max-width: var(--container); height: 92vh; height: 92dvh; max-height: none; }
.modal--flush .modal__body { padding: 0; }

@media (max-width: 640px) {
  .modal { padding: 0; place-items: end center; }
  .modal__dialog {
    width: 100%;
    max-height: 92vh;
    max-height: 92dvh;
    border-end-start-radius: 0;
    border-end-end-radius: 0;
    padding-bottom: var(--safe-b);
    transform: translateY(24px);
  }
  .modal__foot > .btn { flex: 1; }
}

/* --- dropdown ------------------------------------------------------------- */
.dropdown { position: relative; display: inline-block; }
.dropdown__toggle { cursor: pointer; }
.dropdown__menu {
  position: absolute;
  inset-block-start: calc(100% + var(--sp-2));
  inset-inline-start: 0;
  z-index: var(--z-sticky);
  min-width: 220px;
  max-height: 60vh;
  max-height: 60dvh;
  padding: var(--sp-2);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
  box-shadow: var(--sh-3);
  overflow-y: auto;
  overscroll-behavior: contain;
  opacity: 0;
  visibility: hidden;
  transform: translateY(-6px);
  transition: opacity var(--dur-2) var(--ease), transform var(--dur-2) var(--ease), visibility var(--dur-2);
}
.dropdown.is-open > .dropdown__menu {
  opacity: 1;
  visibility: visible;
  transform: none;
}
.dropdown--right .dropdown__menu { inset-inline: auto 0; }
.dropdown--up .dropdown__menu { inset-block: auto calc(100% + var(--sp-2)); transform: translateY(6px); }
.dropdown--wide .dropdown__menu { min-width: 300px; }
.dropdown__label {
  display: block;
  padding: var(--sp-2) var(--sp-3);
  color: var(--ink-3);
  font-size: var(--fs-2xs);
  font-weight: 700;
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
}
.dropdown__item {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  width: 100%;
  min-height: 40px;
  padding: var(--sp-2) var(--sp-3);
  border: 0;
  border-radius: var(--r-sm);
  background: transparent;
  color: var(--ink-2);
  font-family: var(--font-sans);
  font-size: var(--fs-sm);
  font-weight: 500;
  text-align: start;
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--dur-1) var(--ease), color var(--dur-1) var(--ease);
}
.dropdown__item:hover { background: var(--c-hover); color: var(--ink); }
.dropdown__item:focus-visible { outline: none; background: var(--brand-soft); color: var(--brand-text); }
.dropdown__item.is-active,
.dropdown__item[aria-selected="true"] { background: var(--brand-soft); color: var(--brand-text); font-weight: 600; }
.dropdown__item.is-danger { color: var(--danger); }
.dropdown__item.is-danger:hover { background: var(--danger-soft); }
.dropdown__item.is-disabled { opacity: .45; pointer-events: none; }
.dropdown__sep {
  height: 1px;
  margin: var(--sp-2) 0;
  border: 0;
  background: var(--line);
}
.dropdown__hint { margin-inline-start: auto; color: var(--ink-3); font-size: var(--fs-2xs); }

/* checkbox menu (facet filters) */
.dropdown--check .dropdown__item { gap: var(--sp-3); }
.dropdown--check .dropdown__item:has(.checkbox__input:checked) { color: var(--ink); }
.dropdown--check .dropdown__foot {
  display: flex;
  gap: var(--sp-2);
  padding: var(--sp-2) var(--sp-1) 0;
  margin-top: var(--sp-2);
  border-top: 1px solid var(--line);
}
.dropdown--check .dropdown__foot > .btn { flex: 1; }

/* sort menu: check mark on the selected row */
.dropdown--sort .dropdown__item::after {
  content: "";
  width: 16px;
  height: 16px;
  margin-inline-start: auto;
  background-color: currentColor;
  -webkit-mask: var(--i-check) center / contain no-repeat;
  mask: var(--i-check) center / contain no-repeat;
  opacity: 0;
}
.dropdown--sort .dropdown__item[aria-selected="true"]::after,
.dropdown--sort .dropdown__item.is-active::after { opacity: 1; }

/* --- pagination ----------------------------------------------------------- */
.pagination {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-1);
  flex-wrap: wrap;
  list-style: none;
  margin: 0;
  padding: 0;
}
.pagination__item { display: inline-flex; }
.pagination__link {
  display: inline-grid;
  place-items: center;
  min-width: 44px;
  height: 44px;
  padding: 0 var(--sp-3);
  border: 1px solid transparent;
  border-radius: var(--r-md);
  background: transparent;
  color: var(--ink-2);
  font-size: var(--fs-sm);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  text-decoration: none;
  cursor: pointer;
  transition: background-color var(--dur-2) var(--ease), border-color var(--dur-2) var(--ease), color var(--dur-2) var(--ease);
}
.pagination__link:hover { background: var(--c-hover); color: var(--ink); }
.pagination__link:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--brand-ring); }
.pagination__link.is-active,
.pagination__link[aria-current="page"] {
  border-color: var(--brand);
  background: var(--brand);
  color: var(--brand-ink);
}
.pagination__link.is-disabled { opacity: .4; pointer-events: none; }
.pagination__ellipsis {
  display: inline-grid;
  place-items: center;
  min-width: 32px;
  height: 44px;
  color: var(--ink-3);
}
.pagination__info {
  width: 100%;
  margin-top: var(--sp-2);
  color: var(--ink-3);
  font-size: var(--fs-xs);
  text-align: center;
}

/* --- stepper (order status / vertical process) --------------------------- */
.stepper { display: grid; gap: 0; }
.stepper__item {
  position: relative;
  display: grid;
  grid-template-columns: 32px 1fr;
  gap: var(--sp-4);
  padding-bottom: var(--sp-6);
}
.stepper__item:last-child { padding-bottom: 0; }
.stepper__item::before {
  content: "";
  position: absolute;
  inset-block: 32px 0;
  inset-inline-start: 15px;
  width: 2px;
  border-radius: var(--r-full);
  background: var(--line);
}
.stepper__item:last-child::before { display: none; }
.stepper__dot {
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border: 2px solid var(--line-2);
  border-radius: var(--r-full);
  background: var(--surface);
  color: var(--ink-3);
  font-size: var(--fs-2xs);
  font-weight: 700;
}
.stepper__body { display: grid; gap: 2px; padding-top: var(--sp-1); }
.stepper__title { color: var(--ink-2); font-size: var(--fs-sm); font-weight: 700; }
/* a timestamp is sentence-case meta: --fs-xs (2xs is for badges / caps labels) */
.stepper__time { color: var(--ink-3); font-size: var(--fs-xs); }
.stepper__text { color: var(--ink-3); font-size: var(--fs-xs); line-height: var(--lh-snug); }
.stepper__item.is-done .stepper__dot { border-color: var(--success); background: var(--success); color: var(--ink-inv); }
.stepper__item.is-done::before { background: var(--success); }
.stepper__item.is-active .stepper__dot {
  border-color: var(--brand);
  background: var(--brand);
  color: var(--brand-ink);
  box-shadow: 0 0 0 4px var(--brand-ring);
}
.stepper__item.is-active .stepper__title { color: var(--ink); }
.stepper--horizontal {
  grid-auto-flow: column;
  grid-auto-columns: 1fr;
}
.stepper--horizontal .stepper__item {
  grid-template-columns: 1fr;
  justify-items: center;
  text-align: center;
  padding-bottom: 0;
}
.stepper--horizontal .stepper__item::before {
  inset-block: 15px auto;
  inset-inline-start: 50%;
  width: 100%;
  height: 2px;
}
.stepper--horizontal .stepper__body { justify-items: center; }

/* --- segmented control ---------------------------------------------------- */
.segmented {
  display: inline-flex;
  padding: 3px;
  border: 1px solid var(--line);
  border-radius: var(--r-full);
  background: var(--surface-2);
}
.segmented__item {
  position: relative;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-2);
  min-height: 36px;
  padding: 0 var(--sp-4);
  border-radius: var(--r-full);
  color: var(--ink-3);
  font-size: var(--fs-xs);
  font-weight: 600;
  white-space: nowrap;
  cursor: pointer;
  user-select: none;
  transition: background-color var(--dur-2) var(--ease), color var(--dur-2) var(--ease), box-shadow var(--dur-2) var(--ease);
}
.segmented__input {
  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;
}
.segmented__item:hover { color: var(--ink); }
.segmented__item:has(.segmented__input:checked),
.segmented__item.is-active {
  background: var(--surface);
  color: var(--ink);
  box-shadow: var(--sh-1);
}
.segmented__item:has(.segmented__input:focus-visible) { box-shadow: 0 0 0 3px var(--brand-ring); }
/* minmax(0, 1fr): auto-columns default to a min of `auto`, so each option
   kept its min-content width and the control had a hard ~352px floor that
   overflowed the hero on a phone. */
.segmented--block { display: grid; grid-auto-flow: column; grid-auto-columns: minmax(0, 1fr); width: 100%; }
.segmented--lg .segmented__item { min-height: 44px; font-size: var(--fs-sm); }

/* --- rail (snap carousel) ------------------------------------------------- */
.rail { position: relative; }
.rail__track {
  display: grid;
  grid-auto-flow: column;
  grid-auto-columns: var(--rail-item, 72%);
  gap: var(--sp-4);
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-padding-inline: var(--sp-1);
  -webkit-overflow-scrolling: touch;
  scrollbar-width: none;
  padding-bottom: var(--sp-2);
}
.rail__track::-webkit-scrollbar { display: none; }
.rail__item {
  scroll-snap-align: start;
  min-width: 0;
}
.rail__nav { display: none; }
.rail__btn {
  display: grid;
  place-items: center;
  width: 40px;
  height: 40px;
  border: 1px solid var(--line);
  border-radius: var(--r-full);
  background: var(--surface);
  color: var(--ink);
  box-shadow: var(--sh-1);
  cursor: pointer;
  transition: background-color var(--dur-2) var(--ease), border-color var(--dur-2) var(--ease), opacity var(--dur-2) var(--ease);
}
.rail__btn:hover { border-color: var(--ink-3); background: var(--surface-2); }
.rail__btn:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--brand-ring); }
.rail__btn[disabled] { opacity: .35; cursor: not-allowed; }
.rail__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  margin-bottom: var(--sp-4);
}

@media (min-width: 640px) {
  .rail__track { grid-auto-columns: var(--rail-item-sm, 42%); }
}
@media (min-width: 900px) {
  .rail__track { grid-auto-columns: var(--rail-item-md, 30%); }
  .rail__nav { display: flex; gap: var(--sp-2); }
}
@media (min-width: 1200px) {
  .rail__track { grid-auto-columns: var(--rail-item-lg, 23%); }
}

/* --- marquee -------------------------------------------------------------- */
.marquee {
  display: flex;
  overflow: hidden;
  user-select: none;
  mask-image: linear-gradient(90deg, transparent, var(--ink) 8%, var(--ink) 92%, transparent);
  -webkit-mask-image: linear-gradient(90deg, transparent, var(--ink) 8%, var(--ink) 92%, transparent);
}
.marquee__track {
  display: flex;
  flex: none;
  align-items: center;
  gap: var(--sp-8);
  min-width: 100%;
  padding-inline-end: var(--sp-8);
  animation: ss-marquee var(--marquee-dur, 30s) linear infinite;
  will-change: transform;
}
.marquee:hover .marquee__track { animation-play-state: paused; }
.marquee__item {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-3);
  color: var(--ink-2);
  font-size: var(--fs-sm);
  font-weight: 600;
  white-space: nowrap;
}
.marquee--reverse .marquee__track { animation-direction: reverse; }
.marquee--slow { --marquee-dur: 55s; }
.marquee--display .marquee__item {
  color: var(--ink);
  font-family: var(--font-display);
  font-size: var(--fs-4xl);
  font-weight: 400;
}

/* --- lightbox ------------------------------------------------------------- */
.lightbox {
  position: fixed;
  inset: 0;
  z-index: var(--z-modal);
  display: grid;
  grid-template-rows: auto 1fr auto;
  background: color-mix(in oklab, var(--ink) 92%, transparent);
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--dur-3) var(--ease), visibility var(--dur-3);
}
.lightbox.is-open { opacity: 1; visibility: visible; }
.lightbox__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  padding: var(--sp-4) var(--sp-5);
  color: var(--ink-inv);
}
.lightbox__counter {
  color: var(--ink-inv);
  font-size: var(--fs-xs);
  font-weight: 600;
  font-variant-numeric: tabular-nums;
  opacity: .8;
}
.lightbox__stage {
  position: relative;
  display: grid;
  place-items: center;
  min-height: 0;
  padding: var(--sp-4);
}
.lightbox__img {
  max-width: 100%;
  max-height: 100%;
  object-fit: contain;
  border-radius: var(--r-md);
  animation: ss-pop-in var(--dur-3) var(--ease) both;
}
.lightbox__close,
.lightbox__nav {
  display: grid;
  place-items: center;
  width: 44px;
  height: 44px;
  border: 1px solid color-mix(in oklab, var(--ink-inv) 22%, transparent);
  border-radius: var(--r-full);
  background: color-mix(in oklab, var(--ink-inv) 10%, transparent);
  color: var(--ink-inv);
  cursor: pointer;
  transition: background-color var(--dur-2) var(--ease);
}
.lightbox__close:hover,
.lightbox__nav:hover { background: color-mix(in oklab, var(--ink-inv) 22%, transparent); }
.lightbox__close:focus-visible,
.lightbox__nav:focus-visible { outline: none; box-shadow: 0 0 0 3px var(--brand-ring); }
.lightbox__nav {
  position: absolute;
  inset-block-start: 50%;
  transform: translateY(-50%);
}
.lightbox__nav--prev { inset-inline-start: var(--sp-4); }
.lightbox__nav--next { inset-inline-end: var(--sp-4); }
.lightbox__thumbs {
  display: flex;
  justify-content: center;
  gap: var(--sp-2);
  padding: var(--sp-4);
  padding-bottom: calc(var(--sp-4) + var(--safe-b));
  overflow-x: auto;
  scrollbar-width: none;
}
.lightbox__thumbs::-webkit-scrollbar { display: none; }
.lightbox__thumb {
  flex: none;
  width: 56px;
  height: 56px;
  padding: 0;
  border: 2px solid transparent;
  border-radius: var(--r-sm);
  overflow: hidden;
  opacity: .55;
  cursor: pointer;
  transition: opacity var(--dur-2) var(--ease), border-color var(--dur-2) var(--ease);
}
.lightbox__thumb > img { width: 100%; height: 100%; object-fit: cover; display: block; }
.lightbox__thumb.is-selected { opacity: 1; border-color: var(--brand); }

/* ==========================================================================
   08. MARKETING
   ========================================================================== */

/* --- hero -----------------------------------------------------------------
   The hero LAYOUT lives in layout.css §10, which is the file that matches the
   markup every page actually ships: .hero > .container > .hero__inner >
   (.hero__body + .hero__media).

   This block used to carry a second, older definition that assumed
   .hero > .hero__inner with no container. Its `.hero { display: grid }` stuck
   (layout.css never re-declares display), so `@media (min-width: 900px)
   .hero--split { grid-template-columns: 1fr 1fr }` split the *section* instead
   of the inner grid: the container was squeezed into one 690px column, the
   second column stayed empty, and the headline wrapped inside a 281px measure
   with half the viewport blank beside it. Only the pieces layout.css does not
   own are kept here.
   -------------------------------------------------------------------------- */
.hero__meta {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-5);
  color: var(--ink-3);
  font-size: var(--fs-xs);
}
.hero__figure {
  position: absolute;
  z-index: 2;
  display: grid;
  gap: var(--sp-1);
  padding: var(--sp-4);
  border-radius: var(--r-lg);
  background: var(--c-veil);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  box-shadow: var(--sh-3);
}
.hero--center .hero__body { justify-items: center; text-align: center; margin-inline: auto; }
.hero--center .hero__actions { justify-content: center; }
.hero--compact .hero__inner { padding-block: var(--sp-8); }

/* --- banner --------------------------------------------------------------- */
.banner {
  position: relative;
  display: grid;
  align-items: center;
  gap: var(--sp-6);
  padding: var(--sp-8);
  border-radius: var(--r-xl);
  background: var(--surface-2);
  overflow: hidden;
}
.banner__body { display: grid; gap: var(--sp-3); justify-items: start; position: relative; z-index: 2; }
.banner__eyebrow { color: var(--brand-text); font-size: var(--fs-2xs); font-weight: 700; letter-spacing: var(--ls-wide); text-transform: uppercase; }
.banner__title {
  color: var(--ink);
  font-family: var(--font-display);
  font-size: var(--fs-3xl);
  font-weight: 400;
  line-height: var(--lh-tight);
  text-wrap: balance;
}
.banner__text { color: var(--ink-2); font-size: var(--fs-sm); line-height: var(--lh-relaxed); max-width: 46ch; }
.banner__actions { display: flex; flex-wrap: wrap; gap: var(--sp-3); margin-top: var(--sp-1); }
.banner__media {
  position: relative;
  border-radius: var(--r-lg);
  overflow: hidden;
  background: var(--surface-3);
}
.banner__media > img { width: 100%; height: 100%; object-fit: cover; display: block; }
.banner--brand { background: var(--brand-soft); }
.banner--brand .banner__eyebrow { color: var(--brand-text); }
.banner--dark { background: var(--ink); }
.banner--dark .banner__title,
.banner--dark .banner__text { color: var(--ink-inv); }
/* --brand-on-inv, NOT --brand-on-ink. The two tokens look interchangeable and
   are not: --brand-on-ink is for a ground that is ALWAYS dark (home-fashion's
   scrims over artwork), while .banner--dark's ground is var(--ink), which
   INVERTS with the theme — in dark mode this panel is near-white. Using the
   always-light amber here measured 1.32:1 on the panel's own background. */
.banner--dark .banner__eyebrow { color: var(--brand-on-inv); }
.banner--dark .banner__text a,
.banner--dark .banner__body a:not(.btn) { color: var(--brand-on-inv); }
.banner--dark .banner__text { opacity: .82; }
.banner--center { justify-items: center; text-align: center; }
.banner--center .banner__body { justify-items: center; }
.banner--sm { padding: var(--sp-5); }

@media (min-width: 768px) {
  .banner--split { grid-template-columns: 1fr 1fr; }
  .banner--split .banner__media { aspect-ratio: 16 / 10; }
}

/* --- promo strip ---------------------------------------------------------- */
.promo-strip {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: center;
  gap: var(--sp-6);
  min-height: 40px;
  padding: var(--sp-2) var(--sp-10);
  background: var(--ink);
  color: var(--ink-inv);
  font-size: var(--fs-xs);
  font-weight: 600;
  text-align: center;
}
.promo-strip--brand { background: var(--brand); color: var(--brand-ink); }
.promo-strip__item {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
}
.promo-strip__link {
  color: inherit;
  text-decoration: underline;
  text-underline-offset: .2em;
}
.promo-strip__close {
  position: absolute;
  inset-inline-end: var(--sp-2);
  display: grid;
  place-items: center;
  width: 32px;
  height: 32px;
  border: 0;
  border-radius: var(--r-full);
  background: transparent;
  color: inherit;
  opacity: .7;
  cursor: pointer;
}
.promo-strip__close::before {
  content: "";
  width: 13px; height: 13px;
  background-color: currentColor;
  -webkit-mask: var(--i-close) center / contain no-repeat;
  mask: var(--i-close) center / contain no-repeat;
}
.promo-strip__close:hover { opacity: 1; }
.promo-strip__close:focus-visible { outline: none; opacity: 1; box-shadow: 0 0 0 2px var(--brand-ring); }

/* --- countdown ------------------------------------------------------------ */
.countdown {
  display: inline-flex;
  /* wrap: four 58px units plus separators have a ~300px min-content floor,
     which forced the offers hero's grid column wider than the banner itself
     and `overflow: hidden` then sheared the copy off at 320-403px */
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2);
}
.countdown__unit {
  display: grid;
  justify-items: center;
  gap: 2px;
  min-width: 52px;
  padding: var(--sp-2) var(--sp-2);
  border: 1px solid var(--line);
  border-radius: var(--r-md);
  background: var(--surface);
}
.countdown__num {
  color: var(--ink);
  font-size: var(--fs-2xl);
  font-weight: 700;
  line-height: 1;
  font-variant-numeric: tabular-nums;
  letter-spacing: var(--ls-tight);
}
.countdown__label {
  color: var(--ink-3);
  font-size: var(--fs-2xs);
  font-weight: 600;
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
}
.countdown__sep { color: var(--ink-3); font-size: var(--fs-lg); font-weight: 700; }
.countdown--sm .countdown__unit { min-width: 44px; padding: var(--sp-1) var(--sp-2); }
.countdown--sm .countdown__num { font-size: var(--fs-md); }
.countdown--brand .countdown__unit { border-color: transparent; background: var(--brand); }
.countdown--brand .countdown__num { color: var(--brand-ink); }
.countdown--brand .countdown__label { color: color-mix(in oklab, var(--brand-ink) 78%, transparent); }
.countdown--bare .countdown__unit { border-color: transparent; background: transparent; padding-inline: var(--sp-1); }

/* --- feature -------------------------------------------------------------- */
.feature {
  display: grid;
  /* Content packs to the top; without this a stretched card distributed its
     slack between the icon and the title, so titles in one row sat at
     different heights. */
  align-content: start;
  gap: var(--sp-3);
  justify-items: start;
}
.feature__icon {
  display: grid;
  place-items: center;
  width: 52px;
  height: 52px;
  border-radius: var(--r-lg);
  background: var(--brand-soft);
  color: var(--brand-text);
}
.feature__title {
  color: var(--ink);
  font-size: var(--fs-lg);
  font-weight: 700;
  line-height: var(--lh-snug);
  letter-spacing: var(--ls-tight);
}
.feature__text { color: var(--ink-2); font-size: var(--fs-sm); line-height: var(--lh-relaxed); }
.feature--center { justify-items: center; text-align: center; }
.feature--row {
  grid-template-columns: auto 1fr;
  align-items: start;
  gap: var(--sp-4);
}
.feature--row .feature__icon { grid-row: span 2; width: 44px; height: 44px; border-radius: var(--r-md); }
.feature--boxed {
  padding: var(--sp-6);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
}

/* --- testimonial ---------------------------------------------------------- */
.testimonial {
  display: grid;
  gap: var(--sp-4);
  padding: var(--sp-6);
  border: 1px solid var(--line);
  border-radius: var(--r-lg);
  background: var(--surface);
}
.testimonial__quote {
  margin: 0;
  color: var(--ink);
  font-size: var(--fs-md);
  line-height: var(--lh-relaxed);
  text-wrap: pretty;
}
.testimonial__quote::before { content: "\201C"; }
.testimonial__quote::after { content: "\201D"; }
.testimonial__author {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
}
.testimonial__meta { display: grid; gap: 1px; }
.testimonial__name { color: var(--ink); font-size: var(--fs-sm); font-weight: 700; }
.testimonial__role { color: var(--ink-3); font-size: var(--fs-2xs); }
.testimonial__rating { color: var(--star); }
.testimonial__product {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  padding-top: var(--sp-3);
  border-top: 1px solid var(--line);
  color: var(--ink-3);
  font-size: var(--fs-2xs);
}
.testimonial--display .testimonial__quote {
  font-family: var(--font-display);
  font-size: var(--fs-2xl);
  font-weight: 400;
  line-height: var(--lh-snug);
}
.testimonial--flat { border-color: transparent; background: var(--surface-2); }

/* --- logo cloud ----------------------------------------------------------- */
.logo-cloud {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  justify-content: center;
  gap: var(--sp-8) var(--sp-12);
}
.logo-cloud__item {
  display: grid;
  place-items: center;
  height: 32px;
  color: var(--ink-3);
  opacity: .7;
  filter: grayscale(1);
  transition: opacity var(--dur-2) var(--ease), filter var(--dur-2) var(--ease);
}
.logo-cloud__item > img { max-height: 100%; width: auto; display: block; }
.logo-cloud__item:hover { opacity: 1; filter: grayscale(0); }
.logo-cloud--grid {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(120px, 1fr));
  gap: var(--sp-6);
}

/* --- newsletter ----------------------------------------------------------- */
.newsletter {
  display: grid;
  gap: var(--sp-5);
  padding: var(--sp-10) var(--sp-6);
  border-radius: var(--r-xl);
  background: var(--surface-2);
  text-align: center;
  justify-items: center;
}
.newsletter__title {
  color: var(--ink);
  font-family: var(--font-display);
  font-size: var(--fs-3xl);
  font-weight: 400;
  line-height: var(--lh-tight);
  text-wrap: balance;
}
.newsletter__text { max-width: 52ch; color: var(--ink-2); font-size: var(--fs-sm); line-height: var(--lh-relaxed); }
.newsletter__form {
  display: flex;
  gap: var(--sp-2);
  width: min(520px, 100%);
}
.newsletter__form > .input { flex: 1; min-width: 0; border-radius: var(--r-full); min-height: 52px; padding-inline: var(--sp-5); }
.newsletter__note { color: var(--ink-3); font-size: var(--fs-2xs); }
.newsletter--brand { background: var(--brand-soft); }
.newsletter--inline {
  padding: 0;
  background: transparent;
  text-align: start;
  justify-items: start;
}
.newsletter--dark { background: var(--ink); }
.newsletter--dark .newsletter__title { color: var(--ink-inv); }
.newsletter--dark .newsletter__text,
.newsletter--dark .newsletter__note { color: color-mix(in oklab, var(--ink-inv) 72%, transparent); }

@media (max-width: 560px) {
  .newsletter__form { flex-direction: column; }
}

/* --- blog card ------------------------------------------------------------ */
.blog-card {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: var(--sp-3);
  min-width: 0;
}
.blog-card__media {
  position: relative;
  aspect-ratio: 3 / 2;
  border-radius: var(--r-lg);
  background: var(--surface-2);
  overflow: hidden;
}
.blog-card__media > img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
  transition: transform var(--dur-4) var(--ease);
}
.blog-card:hover .blog-card__media > img { transform: scale(1.05); }
.blog-card__body { display: grid; gap: var(--sp-2); }
.blog-card__meta {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  color: var(--ink-3);
  font-size: var(--fs-2xs);
  font-weight: 600;
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
}
.blog-card__title {
  color: var(--ink);
  font-size: var(--fs-lg);
  font-weight: 700;
  line-height: var(--lh-snug);
  letter-spacing: var(--ls-tight);
  text-wrap: balance;
}
.blog-card__link { color: inherit; text-decoration: none; }
.blog-card__link::after { content: ""; position: absolute; inset: 0; }
.blog-card:hover .blog-card__title { color: var(--brand-text); }
.blog-card__excerpt {
  color: var(--ink-2);
  font-size: var(--fs-sm);
  line-height: var(--lh-relaxed);
  display: -webkit-box;
  -webkit-line-clamp: 3;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.blog-card__more {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-1);
  color: var(--brand-text);
  font-size: var(--fs-xs);
  font-weight: 700;
}
.blog-card--row {
  flex-direction: row;
  gap: var(--sp-4);
  align-items: center;
}
.blog-card--row .blog-card__media { flex: none; width: 120px; aspect-ratio: 1; border-radius: var(--r-md); }

/* --- faq ------------------------------------------------------------------ */
.faq-item {
  border-bottom: 1px solid var(--line);
}
.faq-item__q {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--sp-4);
  min-height: 60px;
  padding: var(--sp-4) 0;
  color: var(--ink);
  font-size: var(--fs-md);
  font-weight: 600;
  line-height: var(--lh-snug);
  cursor: pointer;
  list-style: none;
  transition: color var(--dur-2) var(--ease);
}
.faq-item__q::-webkit-details-marker { display: none; }
.faq-item__q:hover { color: var(--brand-text); }
.faq-item__q:focus-visible { outline: none; border-radius: var(--r-sm); box-shadow: 0 0 0 3px var(--brand-ring); }
.faq-item__q::after {
  content: "";
  flex: none;
  width: 20px;
  height: 20px;
  background-color: var(--ink-3);
  -webkit-mask: var(--i-plus) center / contain no-repeat;
  mask: var(--i-plus) center / contain no-repeat;
  transition: transform var(--dur-3) var(--ease), background-color var(--dur-2) var(--ease);
}
.faq-item[open] > .faq-item__q { color: var(--brand-text); }
.faq-item[open] > .faq-item__q::after {
  -webkit-mask-image: var(--i-minus);
  mask-image: var(--i-minus);
  background-color: var(--brand);
  transform: rotate(180deg);
}
.faq-item__a {
  padding-bottom: var(--sp-5);
  color: var(--ink-2);
  font-size: var(--fs-sm);
  line-height: var(--lh-relaxed);
  animation: ss-fade-in var(--dur-3) var(--ease) both;
}
/* Open and close glide instead of snapping. ::details-content plus
   interpolate-size let the panel's height animate to and from auto (Chromium
   131+); content-visibility transitions with allow-discrete so a closing panel
   stays painted until it has folded away. Browsers without the pseudo keep the
   instant open + fade above. */
@supports selector(::details-content) {
  .faq-item { interpolate-size: allow-keywords; }
  .faq-item::details-content {
    block-size: 0;
    overflow: clip;
    transition:
      block-size var(--dur-3) var(--ease),
      content-visibility var(--dur-3) allow-discrete;
  }
  .faq-item[open]::details-content { block-size: auto; }
}
@media (prefers-reduced-motion: reduce) {
  .faq-item::details-content { transition: none; }
}

/* --- timeline ------------------------------------------------------------- */
.timeline {
  display: grid;
  gap: 0;
  padding-inline-start: var(--sp-2);
}
.timeline__item {
  position: relative;
  display: grid;
  gap: var(--sp-1);
  padding: 0 0 var(--sp-8) var(--sp-8);
  border-inline-start: 2px solid var(--line);
}
.timeline__item:last-child { border-color: transparent; padding-bottom: 0; }
.timeline__dot {
  position: absolute;
  inset-block-start: 2px;
  inset-inline-start: -7px;
  width: 12px;
  height: 12px;
  border: 2px solid var(--bg);
  border-radius: var(--r-full);
  background: var(--line-2);
}
.timeline__item.is-active .timeline__dot { background: var(--brand); box-shadow: 0 0 0 4px var(--brand-ring); }
.timeline__item.is-done .timeline__dot { background: var(--success); }
.timeline__time {
  color: var(--ink-3);
  font-size: var(--fs-2xs);
  font-weight: 700;
  letter-spacing: var(--ls-wide);
  text-transform: uppercase;
}
.timeline__title { color: var(--ink); font-size: var(--fs-sm); font-weight: 700; }
.timeline__text { color: var(--ink-2); font-size: var(--fs-sm); line-height: var(--lh-relaxed); }

/* --- trust row ------------------------------------------------------------ */
.trust-row {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(200px, 1fr));
  gap: var(--sp-5);
  padding: var(--sp-6);
  border-block: 1px solid var(--line);
}
.trust-row__item {
  display: flex;
  align-items: center;
  gap: var(--sp-3);
  min-width: 0;
}
.trust-row__icon {
  display: grid;
  place-items: center;
  flex: none;
  width: 40px;
  height: 40px;
  border-radius: var(--r-full);
  background: var(--surface-2);
  color: var(--brand-text);
}
.trust-row__text { display: grid; gap: 1px; min-width: 0; }
.trust-row__title { color: var(--ink); font-size: var(--fs-xs); font-weight: 700; }
/* sentence-case support line: --fs-xs, not the badge/caps-label --fs-2xs */
.trust-row__sub { color: var(--ink-3); font-size: var(--fs-xs); line-height: var(--lh-snug); }
.trust-row--center .trust-row__item { justify-content: center; }
.trust-row--bare { border: 0; padding: 0; }

/* --- usp ------------------------------------------------------------------ */
.usp {
  display: grid;
  gap: var(--sp-2);
  justify-items: center;
  padding: var(--sp-5);
  text-align: center;
}
.usp__icon {
  display: grid;
  place-items: center;
  width: 48px;
  height: 48px;
  border-radius: var(--r-full);
  background: var(--brand-soft);
  color: var(--brand-text);
}
.usp__title { color: var(--ink); font-size: var(--fs-sm); font-weight: 700; letter-spacing: var(--ls-tight); }
.usp__text { max-width: 30ch; color: var(--ink-3); font-size: var(--fs-xs); line-height: var(--lh-snug); }
.usp--start { justify-items: start; text-align: start; padding: 0; }
.usp--row {
  grid-template-columns: auto 1fr;
  align-items: center;
  justify-items: start;
  text-align: start;
  gap: var(--sp-3);
  padding: 0;
}
.usp--row .usp__icon { grid-row: span 2; width: 40px; height: 40px; }

/* ==========================================================================
   09. REDUCED MOTION
   Placed last so equal-specificity resets win by source order.
   ========================================================================== */

@media (prefers-reduced-motion: reduce) {
  .btn,
  .card,
  .card--hover:hover,
  .chip,
  .tile,
  .tile:hover,
  .product-card,
  .product-card:hover,
  .product-card__img,
  .product-card:hover .product-card__img,
  .product-card__actions,
  .product-card__wish,
  .gallery__img,
  .gallery__frame:hover .gallery__img,
  .blog-card:hover .blog-card__media > img,
  .swatch,
  .switch__thumb,
  .checkbox__mark::after,
  .radio__mark::after,
  .accordion__icon,
  .accordion__panel,
  .tabs__tab::after,
  .tabs--underline .tabs__tab::after,
  .drawer__panel,
  .drawer__backdrop,
  .sheet__panel,
  .sheet__backdrop,
  .modal__dialog,
  .modal__backdrop,
  .dropdown__menu,
  .tooltip::after,
  .tooltip::before,
  .progress__bar,
  .rating__star,
  .range__input::-webkit-slider-thumb,
  .lightbox {
    transition-duration: 1ms;
    animation-duration: 1ms;
    transform: none;
  }

  .card--hover:hover,
  .tile:hover,
  .product-card:hover { transform: none; }
  .product-card:hover .product-card__img,
  .gallery__frame:hover .gallery__img,
  .blog-card:hover .blog-card__media > img { transform: none; }
  .product-card__actions { opacity: 1; transform: none; }
  .switch__input:checked + .switch__track .switch__thumb { transform: translateX(19px); }
  .switch--sm .switch__input:checked + .switch__track .switch__thumb { transform: translateX(15px); }
  .accordion__trigger[aria-expanded="true"] .accordion__icon { transform: rotate(180deg); }
  .drawer.is-open .drawer__panel,
  .sheet.is-open .sheet__panel,
  .modal.is-open .modal__dialog { transform: none; }
  .breadcrumb__sep,
  .chip__close::before,
  .qty__btn::before { transform: none; }

  .skeleton { animation: none; background-image: none; }
  .spinner,
  .btn.is-loading::after { animation-duration: 1.6s; }
  .marquee__track { animation: none; transform: none; }
  .marquee { mask-image: none; -webkit-mask-image: none; }
  .progress--indeterminate .progress__bar { animation: none; width: 100%; }
  .lightbox__img,
  .toast,
  .faq-item__a { animation: none; }
  .rating__star:hover { transform: none; }
}


/* ── #20 · unbreakable tokens ────────────────────────────────────────────────
   Email addresses and UPI handles are single tokens with no break opportunity,
   so at 320px they ran straight out of their container. These are the blocks
   that carry them. `anywhere` breaks only when the token genuinely cannot fit. */
.tile__text,
.feature__text,
.alert__body,
.legal-lead,
.usp__text {
  overflow-wrap: anywhere;
}


/* ══ THE SIGNED-IN CHROME ═══════════════════════════════════════════════════
   partials/header-account.html and partials/footer-account.html.

   NOTE ON WHERE THIS LIVES. Every other chrome variant on this site keeps its
   CSS in the @keep:head-extra block of the one page that uses it, with a
   comment saying "because that page is its only consumer". This one has EIGHT
   consumers — all eight account-*.html pages — so page-scoping it would mean
   eight copies drifting apart. It is scoped by the variant's own class instead
   (.hac-header, .fac-footer), which only exists where those partials are.
   ═══════════════════════════════════════════════════════════════════════ */

/* ---- header: one row, four things -------------------------------------- */

.hac-header {
  position: sticky;
  inset-block-start: 0;
  z-index: var(--z-header);
  background: var(--surface);
  border-block-end: 1px solid var(--line);
}
.hac-header.is-sticky { box-shadow: var(--sh-2); }

.hac-bar {
  display: flex;
  align-items: center;
  gap: var(--sp-2);
  min-block-size: 64px;
}

.hac-brand {
  display: inline-flex;
  align-items: center;
  flex: 0 1 auto;
  min-inline-size: 0;
  margin-inline-end: auto;
  text-decoration: none;
}
.hac-brand__logo {
  inline-size: auto;
  block-size: 36px;
  max-inline-size: 100%;
  object-fit: contain;
}

.hac-tools { display: flex; align-items: center; gap: var(--sp-1); }

.hac-tool {
  position: relative;
  display: inline-grid;
  place-items: center;
  inline-size: var(--control-md);
  block-size: var(--control-md);
  flex: none;
  border: 0;
  border-radius: var(--r-full);
  background: none;
  color: var(--ink);
  cursor: pointer;
  transition: background-color var(--dur-2) var(--ease), color var(--dur-2) var(--ease);
}
.hac-tool:hover { background: var(--bg-soft); color: var(--brand-text); }

/* The way back out to the shop, said in one word instead of a rail of seven. */
.hac-shop {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  min-block-size: var(--control-md);
  margin-inline-end: var(--sp-2);
  padding-inline: var(--sp-4);
  border: 1px solid var(--line-2);
  border-radius: var(--r-full);
  color: var(--ink);
  font-size: var(--fs-sm);
  font-weight: 700;
  text-decoration: none;
  transition: background-color var(--dur-2) var(--ease), color var(--dur-2) var(--ease), border-color var(--dur-2) var(--ease);
}
.hac-shop:hover { background: var(--ink); color: var(--bg); border-color: var(--ink); }

/* The burger is the only way to reach #mobile-nav, so it is the only control
   that appears and disappears with the width. */
@media (min-width: 1024px) {
  .hac-burger { display: none; }
}
@media (max-width: 479.98px) {
  /* At 390px the row is brand + four controls; the word goes and the icon on
     the bag carries it, rather than letting the mark shrink to nothing. */
  .hac-shop span { display: none; }
  .hac-shop { padding-inline: 0; inline-size: var(--control-md); justify-content: center; }
}

/* ---- footer: one rule, one line ---------------------------------------- */

.fac-footer {
  margin-block-start: var(--sp-16);
  border-block-start: 1px solid var(--line);
  background: var(--surface);
}
.fac-inner {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-3) var(--sp-6);
  padding-block: var(--sp-6);
}
.fac-copy { margin: 0; color: var(--ink-3); font-size: var(--fs-2xs); }
.fac-copy a { color: var(--ink-2); }
.fac-links {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--sp-2) var(--sp-5);
  margin-inline-start: auto;
  /* clear of the floating back-to-top button */
  padding-inline-end: var(--sp-14);
}
.fac-links a {
  display: inline-flex;
  align-items: center;
  gap: var(--sp-2);
  min-block-size: 24px;
  color: var(--ink-3);
  font-size: var(--fs-2xs);
  text-decoration: none;
}
.fac-links a:hover { color: var(--ink); text-decoration: underline; }
.fac-links .icon { color: var(--brand-text); }
.fac-copy a { display: inline-flex; align-items: center; min-block-size: 24px; }

@media (prefers-reduced-motion: reduce) {
  .hac-tool, .hac-shop { transition: none; }
}

/* ══ SWEEP FIXES, SHARED COMPONENTS ════════════════════════════════════════ */

/* .tile — shipping / returns / privacy / terms / cookies / contact all end
   with a row of these, and a one-line title left that tile's body a line above
   its neighbours'. Same reserve as everywhere else. */
.tile__title { min-block-size: calc(2 * var(--lh-snug) * 1em); line-height: var(--lh-snug); }

/* .trust-row — minmax(200px, 1fr) can only resolve three tracks in the ~690px
   container cart.html and track-order.html give it, so a four-item row always
   orphaned its last item onto a line of its own. 150px fits four across, and
   auto-fit still collapses to three and then two as the container narrows. */
.trust-row { grid-template-columns: repeat(auto-fit, minmax(min(100%, 150px), 1fr)); }

/* .price__save — --danger-soft is an opaque tint in light but a TRANSLUCENT
   wash in dark, so on compare.html the chip composited against whatever cell
   colour happened to be behind it instead of against a plate of its own. Same
   fix the product badges already use: paint the tint over an opaque surface
   first, which leaves the light theme byte-identical. */
.price__save {
  background-color: var(--surface);
  background-image: linear-gradient(var(--danger-soft), var(--danger-soft));
}

/* .cart-line__title — a line-clamped box with overflow:hidden, which then got
   vertical padding from the WCAG 2.5.8 target-size rule below. Padding inside
   an overflow:hidden clamp eats into the visible line box, so the second line
   of a two-line product name was cropped at 390px. The hit area moves to the
   row instead, which is taller than 24px anyway. */
.cart-line__title { padding-block: 0; }

/* WCAG 2.5.8 is not a touch-screen rule. The whole target-size implementation
   sits inside `@media (max-width: 900px), (pointer: coarse)`, so on a 1440px
   desktop with a mouse these named link components measured under 24px. They
   are standalone controls, not prose links, so the inline exception does not
   cover them — give them the minimum at every width. */
.btn--link,
.section-head__link,
.review-item__product,
.ord-card__link,
.footer-legal__link,
.announce__link,
.rating[href],
a.rating {
  display: inline-flex;
  align-items: center;
  min-block-size: 24px;
}

/* ══ CASCADE COLLISIONS AND ONE MISSING COMPONENT ══════════════════════════
   Found by the site-wide sweep. Each of these is a rule that is written and
   then silently beaten, or never written at all — the kind of fault that
   leaves no trace in the markup and does not move a single box, so a geometry
   audit walks straight past it.
   ═══════════════════════════════════════════════════════════════════════ */

/* 1 · .field__msg — the inline validation message, which had NO rule at all.

   app.js builds this element for every field it validates
   (`box.className = 'field__msg'`), on every form on the site: checkout,
   contact, careers, track-order, both newsletter forms. Nothing in any of the
   five stylesheets ever matched it, so an error rendered as plain 16px body
   text in --ink — the same weight and colour as the label above it, with no
   red and no icon. `.error` right above already describes exactly what this
   should look like; the JS simply writes a different class name. */
.field__msg {
  display: flex;
  align-items: center;
  gap: var(--sp-1);
  margin-block-start: var(--sp-1);
  color: var(--danger);
  font-size: var(--fs-xs);
  font-weight: 600;
  line-height: var(--lh-snug);
}
/* It is appended once and then emptied on every successful validation, so it
   has to take no room while it is holding nothing. */
.field__msg:empty { display: none; }

/* 2 · The product card's focus ring.

   `.product-card__link:focus-visible { outline: none }` removed the real
   indicator and handed the job to a --brand-ring box-shadow on the media.
   tokens.css warns about precisely this: "Not --brand: the lime measures about
   1.7:1 on the light paper, and WCAG 2.2 SC 1.4.11 wants 3:1". Measured on the
   rendered pixels it came to 1.23:1 against the page paper. Every other
   control on the site uses --focus-ink; the product card now does too. */
.product-card__link:focus-visible { outline: none; }
.product-card:focus-within .product-card__media {
  box-shadow: none;
  outline: var(--ring-w) solid var(--focus-ink);
  outline-offset: 2px;
}

/* 3 · A selected colour swatch had no focus ring at all.
   `.swatch:focus-visible` puts the ring on box-shadow; `.swatch--color.is-selected`
   sets `box-shadow: none` for its own reasons. Same specificity, and the
   selected rule comes later, so it won — keyboard focus on the swatch you are
   already on was invisible. An outline cannot collide with a box-shadow. */
.swatch:focus-visible {
  outline: var(--ring-w) solid var(--focus-ink);
  outline-offset: 2px;
}

/* 4 · Same collision, pill tabs. `.tabs--pill .tabs__tab[aria-selected="true"]`
   is 0-3-0 and sets its own box-shadow, so it beat the 0-2-0 focus rule and
   the selected tab — the one a keyboard lands on first — showed nothing. */
.tabs__tab:focus-visible {
  outline: var(--ring-w) solid var(--focus-ink);
  outline-offset: -2px;
}

/* 5 · The 44px "quick view" button was stretching.
   `.product-card__actions > .btn { flex: 1 }` is 0-2-0 and beats
   `.product-card__quick { flex: none; width: 44px }` at 0-1-0, so flex-grow
   stayed 1 and the square icon button grew to share the row with "Add to bag". */
.product-card__actions > .product-card__quick { flex: none; }

/* ══ ROW ALIGNMENT ═════════════════════════════════════════════════════════
   One bug, found in eight components at once by the audit's row-misalign rule.

   The shape is always the same: a card holds a heading of unpredictable length
   followed by something the eye uses as a datum — a price, a hint, an excerpt,
   a button. Three of those cards sit in a row. Two headings run to one line and
   the third runs to two, so the datum in that card sits a line lower than its
   neighbours and the row reads as broken even though no single card is wrong.

   The cure is to reserve the heading's taller case so every card spends the
   same height on it. Reserving, not clamping: a heading that needs the second
   line still gets it, and a heading that does not still pays for it, which is
   the point. Where a card has a genuine bottom edge to hang from instead, the
   trailing part takes the slack with margin-block-start:auto — that is better
   still, and is what .product-card, .review-item and .cl-counter do.
   ═══════════════════════════════════════════════════════════════════════ */

/* EXACTLY two lines, not "at least two".

   A min-block-size alone only fixes the short case: a one-line heading is
   padded up to two, but a heading that renders taller than the reserve still
   pushes everything under it down. Pinning the line-height AND clamping to two
   lines makes the box deterministically 2 x line-height whatever the words do,
   so the datum under it lands on the same pixel in every card of the row.

   Applied only to short labels that have no business running past two lines,
   and never to a title that carries a stretched-link ::after — overflow:hidden
   would clip the overlay and take the card's hit area with it. */
.stat__label,
.usp__title,
.trust-row__title,
.stepper--horizontal .stepper__title {
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
  line-height: var(--lh-snug);
  min-block-size: calc(2 * var(--lh-snug) * 1em);
}

/* These two DO carry a stretched-link overlay, so they reserve without
   clamping — the ::after must not be cropped. */
.blog-card__title,
.feature__title { min-block-size: calc(2 * var(--lh-snug) * 1em); }

/* The reserves above only exist so cards SIDE BY SIDE agree. Below 640px these
   cards stack one per row, where the reserve is just a blank line under every
   one-line title (Sep 2026 audit: blog, legal tiles, reviews, features).
   Trust rows and horizontal steppers stay side by side on phones, so they keep it. */
@media (max-width: 639.98px) {
  .review-item__title,
  .tile__title,
  .blog-card__title,
  .feature__title { min-block-size: 0; }
}

/* search.html — the trust row. The icon is 44px and the label block was
   centred against it, so an item with a two-line label started 9px higher than
   an item with one. Hang them all from the top instead: the offset is then the
   same whatever the label does. */
.trust-row__item { align-items: flex-start; }
.trust-row__body,
.trust-row__item > div { padding-block-start: var(--sp-1); }

/* --- product card: the swatch row -----------------------------------------
   .product-card__meta:empty collapses (it carries a stock state only when
   there is one), which left the swatch row under it 28px higher on the cards
   with nothing to say. Reserving the empty row would fix the alignment and
   leave 24 empty boxes on the page, so the swatches hang off the bottom of the
   card instead — .product-card__body takes the card's spare height, and the
   auto margin pushes the swatches down onto the card's own bottom edge. Cards
   in a grid row are already the same height, so that edge is a shared datum. */
.product-card__meta:empty { display: none; }
.product-card__swatches { margin-block-start: auto; }

/* --- the same bug, three more times: a grid distributing its slack ----------
   .feature already carries `align-content: start` with a note explaining why.
   These three never got it, so when their card was stretched to the tallest in
   the row the spare height was shared out BETWEEN the rows — which means the
   card with the shortest body pushed its own heading and text furthest down.
   The card with less to say ended up least aligned with its neighbours. */
.usp { align-content: start; }
.stepper__body { align-content: start; }

/* about.html sets .stat__value at --fs-3xl for a number and --fs-xl for a
   phrase, so the value row was 32px tall in one card and 22px in the next and
   the hint under it never lined up. Reserve the taller of the two and both
   sizes sit in the same row. */
.stat__value { min-block-size: calc(var(--lh-tight) * var(--fs-3xl)); }

/* A rail is a grid of columns with no fixed row height, so its cards were only
   as tall as their own content — and .product-card__swatches, which hangs off
   the bottom of the card, had a different bottom to hang from in each one.
   One row, stretched, gives them a shared bottom edge. */
.rail__track { align-items: stretch; }
.rail__item { display: flex; }
.rail__item > * { inline-size: 100%; }

/* ══ UI AUDIT FIXES ═════════════════════════════════════════════════════════
   Each block below closes a numbered finding from the full UI audit. They live
   together so the reasoning stays readable; move any of them next to its
   component if that component is ever reworked.
   ═════════════════════════════════════════════════════════════════════════ */

/* #14 · NOT FIXED — reverted deliberately.
   The complaint was that the "Save ₹X" chip wraps onto a second line only when
   the price digits happen to be long, so cards in one row disagree. Forcing
   `flex-wrap: nowrap` cured the inconsistency and blew the product card past
   the viewport on every listing page at 390px (measured: card right edge 408px
   in a 390px viewport, 4 pages, 15 elements). A cosmetic inconsistency is worth
   less than a horizontal scrollbar, so the wrap stays. Fixing this properly
   means giving the chip its own row in the card grid, which is a card redesign,
   not a CSS tweak. */
.price { min-inline-size: 0; }

/* #17 · the narrow-phone rule that lets a long button label wrap also hit the
   tiny overlay CTA inside a product card, which is only 50-67px wide in a 2-up
   grid — "Add to bag" became three lines. That button is an icon-and-label
   affordance, not prose. */
@media (max-width: 479.98px) {
  .product-card__actions .btn { white-space: nowrap; text-wrap: nowrap; }
}

/* #18 · superseded. .review-item__head was a nowrap flex row that overflowed a
   245px card, and flex-wrap fixed the overflow by letting the name block drop
   below the avatar — which made the header's height depend on how long the
   reviewer's name was. It is a two-column grid now (see .review-item__head
   above), so nothing overflows and nothing reflows. */

/* #24 · .tabs__list sets overflow-x: auto, and a computed `visible` on the
   other axis is coerced to `auto`, so the focus ring of the first and last tab
   was clipped by the scroll box. Padding gives the ring somewhere to live. */
.tabs__list { padding: 3px; }

/* #40 · .usp--row .usp__icon spans two grid rows, which assumes a title AND a
   text child. Where the markup supplies only one, the icon stretched over an
   empty second row and sat visually high. */
.usp--row .usp__icon { grid-row: span 2; }
.usp--row:not(:has(.usp__title)) .usp__icon,
.usp--row:not(:has(.usp__text)) .usp__icon { grid-row: auto; align-self: center; }

/* #41 · two .panel cards side by side, one title wrapping and one not, put
   their head dividers at different heights. A minimum head height keeps the
   rule on one line across the row. */
.panel__head { min-block-size: calc(var(--sp-4) * 2 + 1.5 * var(--fs-md)); }

/* #49 · the gallery arrows and the zoom button had a :focus-visible rule but no
   :hover, so a mouse user got no feedback at all. */
.gallery__nav:hover,
.gallery__zoom:hover { background: var(--surface); color: var(--ink); border-color: var(--line-2); }


/* #26 · search.html echoes the raw query in three places. A query with no
   spaces is one token, so at 390px it ran out of its box in the page-head
   answer, the no-results title and the toolbar summary. */
.empty-state__title,
q, .search-echo { overflow-wrap: anywhere; }

/* #28 · renderRecent() emits each stored query as .chip, which is nowrap with
   no max-width, so a long recent search pushed the overlay row sideways. */
.search-recent .chip,
[data-search-recent] .chip {
  max-inline-size: 100%;
  overflow: hidden;
  text-overflow: ellipsis;
  display: inline-block;
}

/* #36 · the address modal put Save/Cancel in a .cluster inside the scrolling
   .modal__body, so on a short window the primary action sat below the fold with
   nothing to indicate it was there. Pin it like every other modal's .modal__foot. */
.modal__body > .cluster:last-child:has(> .btn--primary) {
  position: sticky;
  inset-block-end: calc(var(--sp-5) * -1);
  z-index: 1;
  margin-block-start: var(--sp-5);
  padding-block: var(--sp-4);
  background: var(--surface);
  box-shadow: 0 -1px 0 var(--line);
}

/* ==========================================================================
   MEGA MENU — the panel a bare nav grows when NavService gives an item
   children. The markup is emitted server-side (nav.service.ts) with
   data-nav-* hooks, so these rules are design-agnostic: any header whose
   <nav class="xx-nav"> holds direct anchors gets the same behaviour without
   writing its own CSS. Content is the category tree or the owner's own list,
   never anything hard-coded here.
   Developer: Shahid Malla <https://shahidmalla.com>  ·  Owner: ElySpace
   ========================================================================== */
/* element+class so it beats the header's own `.nav__item { position: static }`,
   which exists so a full-width mega can span the header. This panel is a
   compact card anchored under its own trigger, so it needs the item to be
   the containing block. */
li.nav-mega__group,
span.nav-mega__group { position: relative; }
.nav-mega__group { display: inline-flex; align-items: center; }

.nav-mega__trigger { display: inline-flex; align-items: center; gap: 6px; white-space: nowrap; }

.nav-mega__chev {
  inline-size: 11px; block-size: 11px; flex: 0 0 auto; opacity: .55;
  transition: transform var(--dur-2) var(--ease), opacity var(--dur-2) var(--ease);
}
.nav-mega__group.is-open > .nav-mega__trigger .nav-mega__chev { transform: rotate(180deg); opacity: 1; }

.nav-mega {
  position: absolute;
  inset-block-start: calc(100% + var(--sp-4));
  inset-inline-start: 50%;
  z-index: var(--z-nav);
  padding: var(--sp-3);
  border: 1px solid color-mix(in srgb, var(--ink) 8%, transparent);
  border-radius: var(--r-xl);
  background: var(--surface);
  box-shadow: 0 1px 2px rgb(var(--shadow-rgb) / .05),
              0 18px 48px -12px rgb(var(--shadow-rgb) / .22),
              0 2px 8px -2px rgb(var(--shadow-rgb) / .08);
  opacity: 0;
  transform: translate(-50%, -8px) scale(.985);
  transform-origin: top center;
  transition: opacity var(--dur-2) var(--ease), transform var(--dur-2) var(--ease);
  pointer-events: none;
}
/* the gap between trigger and panel would end the hover, so bridge it */
.nav-mega::before {
  content: ""; position: absolute; inset-inline: 0;
  inset-block-start: calc(var(--sp-4) * -1); block-size: var(--sp-4);
}
/* a hairline tick that ties the card to its trigger */
.nav-mega::after {
  content: ""; position: absolute; inset-block-start: -5px; inset-inline-start: 50%;
  inline-size: 10px; block-size: 10px; margin-inline-start: -5px; rotate: 45deg;
  border-start-start-radius: 2px;
  border-block-start: 1px solid color-mix(in srgb, var(--ink) 8%, transparent);
  border-inline-start: 1px solid color-mix(in srgb, var(--ink) 8%, transparent);
  background: var(--surface);
}
.nav-mega[hidden] { display: none; }
.nav-mega__group.is-open > .nav-mega {
  opacity: 1; transform: translate(-50%, 0) scale(1); pointer-events: auto;
}

.nav-mega__inner { display: grid; gap: var(--sp-3); }
.nav-mega--rich .nav-mega__inner { grid-template-columns: minmax(190px, 1fr) 210px; align-items: stretch; }

.nav-mega__col { display: grid; grid-template-rows: auto auto 1fr; align-content: stretch; gap: 2px; min-inline-size: 0; }

.nav-mega__title {
  margin: 0; padding: var(--sp-2) var(--sp-3) var(--sp-2);
  font-size: var(--fs-2xs); font-weight: 700;
  letter-spacing: .16em; text-transform: uppercase; color: var(--ink-3);
}

.nav-mega__list { display: grid; gap: 2px; margin: 0; padding: 0; list-style: none; }

.nav-mega__link,
.nav-mega__all {
  position: relative;
  display: flex; align-items: center; justify-content: space-between; gap: var(--sp-3);
  padding: var(--sp-2) var(--sp-3);
  border-radius: var(--r-md);
  font-size: var(--fs-sm); line-height: 1.35; color: var(--ink);
  text-decoration: none; white-space: nowrap;
  transition: background var(--dur-1) var(--ease), color var(--dur-1) var(--ease), padding-inline-start var(--dur-1) var(--ease);
}
/* a rule that grows from the left on hover, instead of a flat colour swap */
.nav-mega__link::before {
  content: ""; position: absolute; inset-inline-start: var(--sp-2); inset-block: 34%;
  inline-size: 2px; border-radius: 2px; background: var(--brand-text);
  transform: scaleY(0); transform-origin: center;
  transition: transform var(--dur-1) var(--ease);
}
.nav-mega__link:hover, .nav-mega__link:focus-visible {
  background: var(--surface-2); color: var(--brand-text); padding-inline-start: var(--sp-5);
}
.nav-mega__link:hover::before, .nav-mega__link:focus-visible::before { transform: scaleY(1); }

.nav-mega__all {
  align-self: end; margin-block-start: var(--sp-2); padding-block: var(--sp-3);
  border-block-start: 1px solid color-mix(in srgb, var(--ink) 8%, transparent);
  border-start-start-radius: 0; border-start-end-radius: 0;
  font-weight: 600; letter-spacing: .01em; color: var(--brand-text);
}
.nav-mega__all:hover, .nav-mega__all:focus-visible { background: var(--surface-2); }
.nav-mega__all svg { inline-size: 14px; block-size: 14px; flex: 0 0 auto; transition: transform var(--dur-1) var(--ease); }
.nav-mega__all:hover svg { transform: translateX(3px); }

/* ---- the feature tile: the counter's best seller, straight from the catalogue ---- */
.nav-mega__feature {
  display: grid; grid-template-rows: 1fr auto; gap: var(--sp-2);
  padding: var(--sp-2); border-radius: var(--r-lg);
  background: var(--surface-2); text-decoration: none; color: var(--ink);
  transition: background var(--dur-2) var(--ease);
}
.nav-mega__feature:hover, .nav-mega__feature:focus-visible { background: color-mix(in srgb, var(--brand-text) 7%, var(--surface-2)); }
.nav-mega__shot {
  display: block; overflow: hidden; border-radius: var(--r-md);
  background: var(--surface); aspect-ratio: 4 / 5;
}
.nav-mega__shot img { inline-size: 100%; block-size: 100%; object-fit: cover; transition: transform var(--dur-3) var(--ease); }
.nav-mega__feature:hover .nav-mega__shot img { transform: scale(1.04); }
.nav-mega__fmeta { display: grid; gap: 3px; padding: 0 var(--sp-2) var(--sp-2); }
.nav-mega__flabel {
  font-size: var(--fs-2xs); font-weight: 700; letter-spacing: .14em;
  text-transform: uppercase; color: var(--brand-text);
}
.nav-mega__fname {
  display: -webkit-box; -webkit-line-clamp: 2; -webkit-box-orient: vertical; overflow: hidden;
  font-size: var(--fs-xs); line-height: 1.4; color: var(--ink);
}
.nav-mega__fprice { font-size: var(--fs-sm); font-weight: 700; color: var(--ink); }
.nav-mega__fprice s { margin-inline-start: 6px; font-weight: 400; color: var(--ink-3); }

/* touch and narrow screens use the drawer instead; never show the hover panel */
@media (max-width: 1023px) {
  .nav-mega { display: none !important; }
  .nav-mega__chev { display: none; }
}
@media (max-width: 1279px) {
  .nav-mega--rich .nav-mega__inner { grid-template-columns: minmax(180px, 1fr); }
  .nav-mega__feature { display: none; }
}
@media (prefers-reduced-motion: reduce) {
  .nav-mega, .nav-mega__chev, .nav-mega__link, .nav-mega__all, .nav-mega__all svg,
  .nav-mega__feature, .nav-mega__shot img { transition: none; }
  .nav-mega__group.is-open > .nav-mega { transform: translate(-50%, 0); }
}


/* ==========================================================================
   THE SHIPPED LOGO IN DARK MODE
   assets/img/aimscraft-logo.svg is the client's mark and name in one ink
   (#17181a), which vanishes on a dark header or footer. Until the owner
   uploads a dark logo (store-live.js then swaps the src, and these selectors
   stop matching), the same file is drawn light: invert takes the ink to
   #e8e7e5 and hue-rotate puts the neutral back where it was. Uploaded logos
   are never touched, and neither is the admin copy, which sits on its own
   white plate in dark mode.
   ========================================================================== */
:root[data-theme="dark"] img[src$="/assets/img/aimscraft-logo.svg"]:not([src*="/admin/"]),
:root[data-theme="dark"] img[src$="/assets/img/logo.svg"] { filter: invert(1) hue-rotate(180deg); }
@media (prefers-color-scheme: dark) {
  :root:not([data-theme="light"]) img[src$="/assets/img/aimscraft-logo.svg"]:not([src*="/admin/"]),
  :root:not([data-theme="light"]) img[src$="/assets/img/logo.svg"] { filter: invert(1) hue-rotate(180deg); }
}

/* The mark on its own, inline before the name in the footers. It is drawn in
   currentColor, so it takes whatever ink the footer sets (light or dark) and
   sizes with the name's font: a little taller than the capitals, as in the
   header lockup. */
.brand-mark {
  display: inline-block; flex: none;
  block-size: 1.08em; inline-size: calc(1.08em * 262 / 292);
  margin-inline-end: .32em; vertical-align: -.14em;
}

/* ---------------------------------------------------------------------------
   Language switcher — every header's language link and the mobile-nav row.
   modules/i18n fills them in (the same page in the other language) or removes
   them at render (Settings → Languages). Developer: Shahid Malla · Owner: ElySpace
   ------------------------------------------------------------------------ */
.lang-tool { text-decoration: none; }
/* a phone header has no room to spare: there the language lives in the menu's Language row */
@media (max-width: 559.98px) { .lang-tool { display: none !important; } }
.lang-tool__txt { font-size: .8125rem; font-weight: 600; line-height: 1; letter-spacing: 0; white-space: nowrap; }
.lang-tool[lang="ar"] .lang-tool__txt { font-family: "IBM Plex Sans Arabic", "Noto Sans Arabic", "Geeza Pro", "Segoe UI", Tahoma, sans-serif; font-size: .875rem; }
.lang-switch .tag { text-decoration: none; }
.lang-switch [lang="ar"] { font-family: "IBM Plex Sans Arabic", "Noto Sans Arabic", "Geeza Pro", "Segoe UI", Tahoma, sans-serif; }
.lang-switch .dropdown__item[aria-current="true"] { font-weight: 600; }
.mobile-nav__pref.lang-switch { grid-column: 1 / -1; align-content: start; }
.lang-switch .chip-row { align-items: center; align-content: flex-start; }
