@charset "UTF-8";
/* ==========================================================================
   DUIN KOENYN
   Hand-written stylesheet. No framework, no build step.

   01  Tokens
   02  Reset & base
   03  Typography
   04  Layout
   05  Brand devices  (eyebrow, rule, trail, ticks, topo, grain)
   06  Buttons
   07  Badges, tags, notices
   08  Forms
   09  Header, nav, search
   10  Cart drawer
   11  Footer
   12  Product card & grid
   13  Home
   14  Shop & filters
   15  Product page
   16  Customise
   17  Cart & checkout
   18  Editorial pages (story, FAQ, contact, legal)
   19  Utilities
   20  Motion & print
   ========================================================================== */

/* ------------------------------------------------------------ 01 tokens -- */

:root {
  /* Brand palette — the working digital palette from the client brief. */
  --olive:        #4f5b3a;
  --olive-deep:   #3d4730;
  --olive-dark:   #2b3123;
  --khaki:        #c5b078;
  --khaki-deep:   #a38d55;
  --khaki-pale:   #e0d3ac;
  --cream:        #f5f1e7;
  --cream-deep:   #ece5d5;
  --sand:         #ded3ba;
  --charcoal:     #252820;
  --charcoal-mid: #4a4c41;
  --clay:         #8a7b5e;

  /* Ink derived tints — kept as variables so dark sections can flip them. */
  --ink:          var(--charcoal);
  --ink-soft:     rgb(37 40 32 / 0.66);
  --ink-faint:    rgb(37 40 32 / 0.42);
  --hairline:     rgb(37 40 32 / 0.14);
  --hairline-strong: rgb(37 40 32 / 0.28);
  --ground:       var(--cream);

  --font-display: "Oswald", "Arial Narrow", "Helvetica Neue", sans-serif;
  --font-body:    "Archivo", system-ui, -apple-system, "Segoe UI", sans-serif;
  --font-mono:    "IBM Plex Mono", ui-monospace, "SFMono-Regular", monospace;
  --font-serif:   "Cormorant Garamond", Georgia, "Times New Roman", serif;

  --measure: 62ch;
  --container: 1280px;
  --container-wide: 1440px;
  --container-narrow: 760px;
  --gutter: clamp(1.25rem, 4vw, 2rem);

  --section-y: clamp(3.5rem, 8vw, 7rem);
  --header-h: 4.5rem;

  --ease: cubic-bezier(0.22, 0.61, 0.36, 1);
  --speed: 0.42s;

  --z-header: 50;
  --z-menu: 60;
  --z-search: 70;
  --z-drawer: 80;
  --z-marker: 90;
}

/* Sections on olive or charcoal flip the ink variables once, and every
   component inside inherits correctly. */
.on-dark {
  --ink: var(--cream);
  --ink-soft: rgb(245 241 231 / 0.72);
  --ink-faint: rgb(245 241 231 / 0.45);
  --hairline: rgb(245 241 231 / 0.18);
  --hairline-strong: rgb(197 176 120 / 0.5);
  color: var(--cream);

}

/* -------------------------------------------------------- 02 reset/base -- */

*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header-h) + 1.5rem);
}

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: 1rem;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;

  /* clip, not hidden. Both stop a bleed from scrolling the page sideways, but
     `hidden` also makes the body a scroll container, and every position:sticky
     on the site then measures its offsets against that box instead of the
     viewport. The sign-in panel showed it: pinned with top:var(--header-h), it
     sat a full header-height down its own column at rest, leaving a bare strip
     above it. `clip` clips without becoming a scroll container, so sticky
     behaves again. */
  overflow-x: clip;
}

/* Paper grain. Keeps large cream areas from reading flat and synthetic. */
body::after {
  content: "";
  position: fixed;
  inset: 0;
  /* Above section backgrounds, below the header and every overlay. */
  z-index: 3;
  pointer-events: none;
  opacity: 0.38;
  background-image: url("/assets/textures/grain.svg");
  background-size: 220px 220px;
  mix-blend-mode: multiply;
}

body.is-locked { overflow: hidden; }

h1, h2, h3, h4, h5 { margin: 0; font-weight: 600; }
p, ul, ol, figure, blockquote { margin: 0; }
ul, ol { padding: 0; list-style: none; }
img, svg, video { display: block; max-width: 100%; height: auto; }

/* Guard: a bare <svg> with no intrinsic size falls back to 300x150 and blows a
   layout apart. Every real icon carries .icon or an .art class that overrides
   this; this is the floor. */
svg { width: 1em; height: 1em; }
button { font: inherit; color: inherit; background: none; border: 0; padding: 0; cursor: pointer; }
input, select, textarea { font: inherit; color: inherit; border-radius: 0; }
table { border-collapse: collapse; width: 100%; }

a { color: inherit; text-decoration: none; }

::selection { background: var(--olive); color: var(--cream); }

:focus-visible {
  outline: 2px solid var(--olive);
  outline-offset: 3px;
}
.on-dark :focus-visible { outline-color: var(--khaki); }

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 200;
}
.skip-link:focus {
  left: 1rem;
  top: 1rem;
  background: var(--olive);
  color: var(--cream);
  padding: 0.75rem 1.25rem;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
}

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

/* ------------------------------------------------------- 03 typography -- */

.display-xl,
.display-lg,
.display-md,
.display-sm {
  font-family: var(--font-display);
  text-transform: uppercase;
  font-weight: 600;
  line-height: 0.98;
  letter-spacing: 0.01em;
}

.display-xl { font-size: clamp(2.6rem, 6.6vw, 5.25rem); line-height: 0.92; }
.display-lg { font-size: clamp(1.9rem, 4.2vw, 3.1rem); }
.display-md { font-size: clamp(1.45rem, 2.5vw, 1.95rem); line-height: 1.05; }
.display-sm { font-size: clamp(1.1rem, 1.6vw, 1.3rem); line-height: 1.12; }

.label,
.label-sm {
  font-family: var(--font-mono);
  text-transform: uppercase;
  font-variant-numeric: tabular-nums;
  line-height: 1.4;
  display: inline-block;
}
.label    { font-size: 0.6875rem; letter-spacing: 0.22em; }
.label-sm { font-size: 0.625rem;  letter-spacing: 0.24em; }

.mono {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  letter-spacing: 0.04em;
}

.serif { font-family: var(--font-serif); text-transform: none; letter-spacing: 0.01em; }

.lede {
  font-size: clamp(1.02rem, 1.5vw, 1.18rem);
  line-height: 1.6;
  color: var(--ink-soft);
  max-width: var(--measure);
}

.prose p + p { margin-top: 1.1em; }
.prose { max-width: var(--measure); color: var(--ink-soft); }
.prose strong { color: var(--ink); font-weight: 600; }

.muted { color: var(--ink-soft); }
.faint { color: var(--ink-faint); }

/* Quiet link with an underline that draws in. */
.link {
  background-image: linear-gradient(currentColor, currentColor);
  background-size: 0% 1px;
  background-repeat: no-repeat;
  background-position: 0 100%;
  transition: background-size var(--speed) var(--ease);
  padding-bottom: 2px;
}
.link:hover, .link:focus-visible { background-size: 100% 1px; }

/* ----------------------------------------------------------- 04 layout -- */

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

.section {
  position: relative;
  padding-block: var(--section-y);
  z-index: 2;
}
.section--tight { padding-block: clamp(2.5rem, 5vw, 4rem); }
.section--flush-top { padding-top: 0; }

.section--cream    { background: var(--cream); }
.section--deep     { background: var(--cream-deep); }
.section--sand     { background: var(--sand); }
.section--olive    { background: var(--olive); }
.section--charcoal { background: var(--charcoal); }
.section--olive-dark { background: var(--olive-dark); }

.section-head {
  display: flex;
  flex-direction: column;
  gap: 1.15rem;
  margin-bottom: clamp(2rem, 4vw, 3.25rem);
}
.section-head__title { max-width: 22ch; }
.section-head__intro { max-width: 58ch; color: var(--ink-soft); font-size: 0.975rem; }
.section-head--split { gap: 1.5rem; }

@media (min-width: 820px) {
  .section-head--split {
    flex-direction: row;
    align-items: flex-end;
    justify-content: space-between;
    gap: 3rem;
  }
  .section-head--split > * { min-width: 0; }
}

.section-head--center {
  align-items: center;
  text-align: center;
}
.section-head--center .section-head__intro { margin-inline: auto; }

.stack   { display: flex; flex-direction: column; }
.stack-3 { gap: 0.75rem; } .stack-4 { gap: 1rem; }
.stack-5 { gap: 1.25rem; } .stack-6 { gap: 1.5rem; }
.stack-8 { gap: 2rem; }    .stack-10 { gap: 2.5rem; }

.row { display: flex; flex-wrap: wrap; align-items: center; gap: 0.75rem; }
.row--between { justify-content: space-between; }
.row--gap-lg { gap: 1.25rem; }

/* --------------------------------------------------- 05 brand devices -- */

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.75rem;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
.eyebrow::before {
  content: "";
  width: 2rem;
  height: 1px;
  background: currentColor;
  opacity: 0.6;
}
.eyebrow--bare::before { display: none; }

.rule { border: 0; border-top: 1px solid var(--hairline); margin: 0; }

.rule-labelled {
  display: flex;
  align-items: center;
  gap: 1rem;
  color: var(--ink-faint);
}
.rule-labelled::before,
.rule-labelled::after {
  content: "";
  flex: 1;
  height: 1px;
  background: var(--hairline);
}

/* Dotted footprint trail — echoes the track in the shirt back print. */
.trail {
  height: 1px;
  width: 100%;
  background-image: radial-gradient(circle, var(--hairline-strong) 1px, transparent 1.2px);
  background-size: 9px 1px;
  background-repeat: repeat-x;
}

/* The surveyed plate: hairline panel with corner ticks. */
.plate-frame {
  position: relative;
  border: 1px solid var(--hairline);
  background: var(--ground);
}

.ticks { position: relative; }
.ticks::before,
.ticks::after {
  content: "";
  position: absolute;
  width: 9px; height: 9px;
  pointer-events: none;
  border-color: var(--hairline-strong);
  z-index: 1;
}
.ticks::before { top: 8px; left: 8px; border-top: 1px solid; border-left: 1px solid; }
.ticks::after  { bottom: 8px; right: 8px; border-bottom: 1px solid; border-right: 1px solid; }

/* Topographic contour field. Masked so it takes the section's ink colour. */
.topo {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
  opacity: var(--topo-opacity, 0.14);
  background-color: currentColor;
  -webkit-mask-image: url("/assets/art/topo-dunes.svg");
  mask-image: url("/assets/art/topo-dunes.svg");
  -webkit-mask-size: cover;
  mask-size: cover;
  -webkit-mask-position: center;
  mask-position: center;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;
}

.coords {
  font-family: var(--font-mono);
  font-size: 0.625rem;
  letter-spacing: 0.24em;
  text-transform: uppercase;
  color: var(--ink-faint);
}

/* Field-log strip: a row of small facts under a hero or header. */
/* Wrap the strip in .factbar-band > .container > ul.factbar so the rules run
   full-bleed while the cells line up with everything else on the page. */
.factbar-band { border-block: 1px solid var(--hairline); }

.factbar {
  display: grid;
  gap: 1px;
  background: var(--hairline);
  border-block: 1px solid var(--hairline);
}
.factbar-band .factbar { border-block: 0; }
.factbar-band .factbar > li { padding-inline: 0; }
.factbar-band .factbar > li + li { padding-inline-start: 1.25rem; }
.factbar > li {
  background: var(--ground);
  padding: 0.9rem var(--gutter);
  font-family: var(--font-mono);
  font-size: 0.625rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
@media (min-width: 720px) {
  .factbar { grid-auto-flow: column; grid-auto-columns: 1fr; }
}

/* Below that the four cells were four stacked rows: 186px of small-caps
   directly under the hero, before a single product. It read as a wall to get
   past rather than a set of marks. One swipeable row instead -- same cells,
   same order, about 46px, and the strip reads as a strip. */
@media (max-width: 719px) {
  .factbar {
    grid-auto-flow: column;
    grid-auto-columns: max-content;
    overflow-x: auto;
    overscroll-behavior-x: contain;
    scroll-snap-type: x proximity;
    scrollbar-width: none;
  }
  .factbar::-webkit-scrollbar { display: none; }
  .factbar > li {
    white-space: nowrap;
    scroll-snap-align: start;
    padding-inline: 1rem;
  }
  .factbar-band .factbar > li:first-child { padding-inline-start: var(--gutter); }
  .factbar-band .factbar > li:last-child  { padding-inline-end: var(--gutter); }
  .factbar-band .factbar > li + li { padding-inline-start: 1rem; }
}

/* SVG art defaults */
.art { width: 100%; height: auto; color: inherit; }
.art-compass__letters {
  font-family: var(--font-display);
  font-size: 9px;
  letter-spacing: 0.06em;
}

/* ---------------------------------------------------------- 06 buttons -- */

.btn {
  --btn-bg: var(--olive);
  --btn-fg: var(--cream);
  --btn-border: var(--olive);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.75rem;
  min-height: 48px;
  padding: 0.85rem 1.75rem;
  background: var(--btn-bg);
  color: var(--btn-fg);
  border: 1px solid var(--btn-border);
  font-family: var(--font-display);
  font-size: 0.8125rem;
  font-weight: 500;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  text-align: center;
  transition: background-color var(--speed) var(--ease), color var(--speed) var(--ease),
              border-color var(--speed) var(--ease);
}
.btn:hover { --btn-bg: var(--olive-deep); --btn-border: var(--olive-deep); }

.btn--khaki   { --btn-bg: var(--khaki); --btn-fg: var(--charcoal); --btn-border: var(--khaki); }
.btn--khaki:hover { --btn-bg: var(--khaki-deep); --btn-border: var(--khaki-deep); }

.btn--outline { --btn-bg: transparent; --btn-fg: var(--ink); --btn-border: var(--hairline-strong); }
.btn--outline:hover { --btn-bg: var(--charcoal); --btn-fg: var(--cream); --btn-border: var(--charcoal); }

.on-dark .btn--outline { --btn-fg: var(--cream); }
.on-dark .btn--outline:hover { --btn-bg: var(--cream); --btn-fg: var(--charcoal); --btn-border: var(--cream); }

.btn--quiet { --btn-bg: transparent; --btn-fg: var(--ink); --btn-border: transparent; }
.btn--quiet:hover { --btn-bg: transparent; --btn-border: var(--hairline-strong); }

.btn--block { width: 100%; }
.btn--lg { padding: 1.05rem 2.25rem; font-size: 0.875rem; }
.btn--sm { min-height: 44px; padding: 0.55rem 1.1rem; font-size: 0.6875rem; letter-spacing: 0.16em; }

.btn[disabled], .btn[aria-disabled="true"] {
  opacity: 0.45;
  cursor: not-allowed;
  pointer-events: none;
}

.btn__arrow {
  width: 22px; height: 8px;
  flex: none;
  transition: transform var(--speed) var(--ease);
}
.btn__icon { width: 18px; height: 18px; flex: none; }
.btn:hover .btn__arrow { transform: translateX(4px); }

/* Icon-only control in the header. */
.icon-btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  width: 44px; height: 44px;
  color: var(--ink);
  opacity: 0.78;
  transition: opacity var(--speed) var(--ease);
  position: relative;
}
.icon-btn:hover { opacity: 1; }
.icon-btn svg { width: 18px; height: 18px; }

/* Emitted by icon() in includes/icons.php. */
.icon { width: 18px; height: 18px; flex: none; }
.icon--lg { width: 22px; height: 22px; }

/* ------------------------------------------- 07 badges, tags, notices -- */

.badge {
  display: inline-block;
  padding: 0.3rem 0.6rem;
  background: var(--khaki);
  color: var(--charcoal);
  font-family: var(--font-mono);
  font-size: 0.625rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  line-height: 1.2;
}
.badge--olive { background: var(--olive); color: var(--cream); }
.badge--muted { background: rgb(37 40 32 / 0.08); color: var(--ink-soft); }
.badge--ghost { background: transparent; border: 1px solid var(--hairline-strong); color: var(--ink-soft); }

/* The only way to mark something waiting on the client. */
.pending {
  display: inline-flex;
  align-items: center;
  gap: 0.45rem;
  font-family: var(--font-mono);
  font-size: 0.625rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--clay);
}
.pending::before {
  content: "";
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--khaki-deep);
  flex: none;
}
.on-dark .pending { color: var(--khaki); }

.notice {
  border: 1px solid var(--hairline);
  border-left: 2px solid var(--khaki-deep);
  background: rgb(197 176 120 / 0.14);
  padding: 0.9rem 1.1rem;
  font-size: 0.85rem;
  line-height: 1.55;
  color: var(--ink-soft);
}
.notice--olive { border-left-color: var(--olive); background: rgb(79 91 58 / 0.08); }
.notice--error {
  border-left-color: #8c3a2b;
  background: rgb(140 58 43 / 0.08);
  color: #6f2e22;
}
.notice--success { border-left-color: var(--olive); background: rgb(79 91 58 / 0.1); color: var(--olive-dark); }
.notice__title { display: block; font-family: var(--font-mono); font-size: 0.625rem; letter-spacing: 0.2em;
  text-transform: uppercase; margin-bottom: 0.35rem; color: var(--ink); }

/* ------------------------------------------------------------ 08 forms -- */

.field { display: flex; flex-direction: column; gap: 0.4rem; }

.field__label {
  font-family: var(--font-mono);
  font-size: 0.625rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-soft);
}
.field__hint { font-size: 0.78rem; color: var(--ink-faint); line-height: 1.45; }
.field__error { font-size: 0.78rem; color: #8c3a2b; }

.input, .select, .textarea {
  width: 100%;
  min-height: 48px;
  padding: 0.75rem 0.9rem;
  background: var(--cream);
  border: 1px solid var(--hairline-strong);
  color: var(--ink);
  font-size: 0.95rem;
  transition: border-color var(--speed) var(--ease), background-color var(--speed) var(--ease);
}
.input::placeholder, .textarea::placeholder { color: var(--ink-faint); }
.input:hover, .select:hover, .textarea:hover { border-color: var(--olive); }
.input:focus, .select:focus, .textarea:focus { border-color: var(--olive); outline-offset: 2px; }
.textarea { min-height: 7rem; resize: vertical; line-height: 1.55; }

.select {
  appearance: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 16 16' fill='none'%3E%3Cpath d='m4 6 4 4 4-4' stroke='%23252820' stroke-width='1.4' stroke-linecap='round'/%3E%3C/svg%3E");
  background-repeat: no-repeat;
  background-position: right 0.85rem center;
  background-size: 14px;
  padding-right: 2.4rem;
}

.field--invalid .input,
.field--invalid .select,
.field--invalid .textarea { border-color: #8c3a2b; }

/* Option chips: sizes, fits, vehicle types. Real radios underneath. */
.options { display: flex; flex-wrap: wrap; gap: 0.5rem; }

/* inline-flex, not the default inline. The face inside is already a 48x44
   target, but the wrapper being inline collapsed its box to the line box, and
   the input is absolutely positioned to the WRAPPER -- so the real hit area
   came out 40x41 while the chip looked the right size. On a phone that is the
   difference between selecting a size and missing it. */
.option { position: relative; display: inline-flex; min-height: 44px; }
.option input {
  position: absolute;
  inset: 0;
  opacity: 0;
  cursor: pointer;
}
.option__face {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  min-width: 48px;
  min-height: 44px;
  padding: 0.5rem 0.9rem;
  border: 1px solid var(--hairline-strong);
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  transition: all var(--speed) var(--ease);
}
/* A chip that carries an example under its label — the plate styles on
   /customise, where the customer needs to see the line that actually prints.
   currentColor keeps the example legible in the checked state too. */
.option__face--stacked { flex-direction: column; gap: 0.3rem; padding-block: 0.6rem; }
.option__example { font-size: 0.58rem; letter-spacing: 0.22em; opacity: 0.7; }

.option input:hover + .option__face { border-color: var(--olive); }
.option input:focus-visible + .option__face { outline: 2px solid var(--olive); outline-offset: 2px; }
.option input:checked + .option__face { background: var(--charcoal); color: var(--cream); border-color: var(--charcoal); }
.option input:disabled + .option__face { opacity: 0.35; cursor: not-allowed; }

/* Colour swatch options. */
.swatches { display: flex; flex-wrap: wrap; gap: 0.6rem; }
/* Same collapsed-inline-box problem as .option: the face is 44x44, the hit
   area was 36x41. */
.swatch { position: relative; display: inline-flex; min-height: 44px; }
.swatch input { position: absolute; inset: 0; opacity: 0; cursor: pointer; }
.swatch__face {
  display: block;
  width: 44px; height: 44px;
  border: 1px solid var(--hairline-strong);
  box-shadow: inset 0 0 0 3px var(--ground);
  transition: box-shadow var(--speed) var(--ease), border-color var(--speed) var(--ease);
}
.swatch input:checked + .swatch__face { border-color: var(--charcoal); box-shadow: inset 0 0 0 3px var(--ground), 0 0 0 1px var(--charcoal); }
.swatch input:focus-visible + .swatch__face { outline: 2px solid var(--olive); outline-offset: 2px; }
/* Link variant: colourways that are separate products, so the swatch navigates
   rather than selects. Same selected state as the radio above. */
.swatch[aria-current="true"] .swatch__face { border-color: var(--charcoal); box-shadow: inset 0 0 0 3px var(--ground), 0 0 0 1px var(--charcoal); }

.checkbox {
  display: flex;
  gap: 0.75rem;
  align-items: flex-start;
  font-size: 0.875rem;
  line-height: 1.5;
  color: var(--ink-soft);
  cursor: pointer;
}
.checkbox input {
  appearance: none;
  width: 20px; height: 20px;
  margin: 0.15rem 0 0;
  flex: none;
  border: 1px solid var(--hairline-strong);
  background: var(--cream);
  display: grid;
  place-content: center;
  cursor: pointer;
}
.checkbox input::before {
  content: "";
  width: 10px; height: 10px;
  transform: scale(0);
  background: var(--olive);
  transition: transform 0.15s var(--ease);
}
.checkbox input:checked::before { transform: scale(1); }
.checkbox input:focus-visible { outline: 2px solid var(--olive); outline-offset: 2px; }

/* Quantity stepper */
.qty { display: inline-flex; align-items: center; border: 1px solid var(--hairline-strong); }
.qty button {
  width: 44px; height: 44px;
  display: grid; place-content: center;
  font-size: 1rem;
  transition: background-color var(--speed) var(--ease), color var(--speed) var(--ease);
}
.qty button:hover { background: var(--charcoal); color: var(--cream); }
.qty__value {
  min-width: 2.5rem;
  text-align: center;
  font-family: var(--font-mono);
  font-size: 0.85rem;
}
.qty input {
  width: 3rem;
  height: 44px;
  border: 0;
  background: transparent;
  text-align: center;
  font-family: var(--font-mono);
  font-size: 0.85rem;
}

/* Honeypot — never visible, never announced. */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

/* --------------------------------------------- 09 header, nav, search -- */

.announce {
  position: relative;
  background: var(--olive);
  color: var(--cream);
  z-index: calc(var(--z-header) + 1);
}
.announce__inner {
  max-width: var(--container-wide);
  margin-inline: auto;
  padding: 0.55rem 3rem;
  text-align: center;
}
.announce__sep { margin-inline: 0.5rem; opacity: 0.5; }

/* On a phone the long form wraps to three lines and eats the top of the page.
   The short form carries the fact that matters. */
.announce__long { display: none; }
@media (min-width: 620px) {
  .announce__long { display: inline; }
  .announce__short { display: none; }
}
.announce__close {
  position: absolute;
  right: 0.25rem; top: 50%;
  transform: translateY(-50%);
  width: 44px; height: 44px;
  display: grid; place-content: center;
  opacity: 0.65;
}
.announce__close:hover { opacity: 1; }
.announce__close svg { width: 10px; height: 10px; }

.site-header {
  position: sticky;
  top: 0;
  z-index: var(--z-header);
  background: var(--cream);
  border-bottom: 1px solid transparent;
  transition: border-color var(--speed) var(--ease), box-shadow var(--speed) var(--ease),
              background-color var(--speed) var(--ease);
}
.site-header.is-scrolled {
  border-bottom-color: var(--hairline);
  background: rgb(245 241 231 / 0.96);
  backdrop-filter: blur(6px);
  box-shadow: 0 1px 20px -12px rgb(37 40 32 / 0.6);
}
.site-header__inner {
  max-width: var(--container-wide);
  margin-inline: auto;
  padding-inline: var(--gutter);
  height: var(--header-h);
  display: flex;
  align-items: center;
  gap: 1rem;
}
.site-header__logo { display: flex; align-items: center; margin-inline: auto; }
.site-header__logo img { height: 2.1rem; width: auto; }
.site-header__actions { display: flex; align-items: center; }

.nav { display: none; }
.nav__link {
  position: relative;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-soft);
  padding-block: 0.35rem;
  transition: color var(--speed) var(--ease);
}
.nav__link:hover, .nav__link.is-current { color: var(--ink); }
.nav__link::after {
  content: "";
  position: absolute;
  left: 0; bottom: 0;
  height: 1px;
  width: 0;
  background: currentColor;
  transition: width var(--speed) var(--ease);
}
.nav__link.is-current::after, .nav__link:hover::after { width: 100%; }
.nav__link--emphasis::before {
  content: "";
  position: absolute;
  left: -0.7rem; top: 50%;
  transform: translateY(-50%);
  width: 4px; height: 4px;
  border-radius: 50%;
  background: var(--khaki-deep);
}

@media (min-width: 1060px) {
  .nav { display: flex; align-items: center; gap: 2rem; flex: 1; }
  .site-header__logo { margin-inline: 0; margin-right: 2.5rem; }
  .site-header__logo img { height: 2.35rem; }
  .menu-btn { display: none; }
}

.cart-count {
  position: absolute;
  top: 4px; right: 4px;
  min-width: 17px; height: 17px;
  padding-inline: 4px;
  display: grid; place-content: center;
  border-radius: 999px;
  background: var(--olive);
  color: var(--cream);
  font-family: var(--font-mono);
  font-size: 0.6rem;
  line-height: 1;
}

/* Mobile menu */
.menu {
  position: fixed;
  inset: 0;
  z-index: var(--z-menu);
  visibility: hidden;
}
.menu.is-open { visibility: visible; }
.menu__scrim {
  position: absolute;
  inset: 0;
  background: rgb(37 40 32 / 0.45);
  opacity: 0;
  transition: opacity var(--speed) var(--ease);
}
.menu.is-open .menu__scrim { opacity: 1; }
.menu__panel {
  position: relative;
  height: 100%;
  width: min(86%, 380px);
  background: var(--cream);
  transform: translateX(-100%);
  transition: transform var(--speed) var(--ease);
  display: flex;
  flex-direction: column;
  overflow-y: auto;
}
.menu.is-open .menu__panel { transform: none; }
.menu__head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 0.75rem var(--gutter);
  border-bottom: 1px solid var(--hairline);
}
.menu__head img { height: 2rem; width: auto; }
.menu__nav { padding: 1.5rem var(--gutter); display: flex; flex-direction: column; }
.menu__link {
  font-family: var(--font-display);
  font-size: 1.6rem;
  text-transform: uppercase;
  letter-spacing: 0.03em;
  padding-block: 0.85rem;
  border-bottom: 1px solid var(--hairline);
}
.menu__nav .menu__link:last-child { border-bottom: 0; }
.menu__foot {
  padding: 0 var(--gutter) 2rem;
  padding-bottom: max(2rem, env(safe-area-inset-bottom));
}
.menu__foot--push { margin-top: auto; }

/* Predictive search */
.search {
  position: fixed;
  inset: 0;
  z-index: var(--z-search);
  visibility: hidden;
}
.search.is-open { visibility: visible; }
.search__scrim {
  position: absolute;
  inset: 0;
  background: rgb(37 40 32 / 0.45);
  opacity: 0;
  transition: opacity var(--speed) var(--ease);
}
.search.is-open .search__scrim { opacity: 1; }
.search__panel {
  position: relative;
  width: min(100%, 820px);
  margin-inline: auto;
  background: var(--cream);
  box-shadow: 0 30px 80px -40px rgb(37 40 32 / 0.7);
  transform: translateY(-1rem);
  opacity: 0;
  transition: transform var(--speed) var(--ease), opacity var(--speed) var(--ease);
  max-height: 100dvh;
  display: flex;
  flex-direction: column;
}
.search.is-open .search__panel { transform: none; opacity: 1; }
.search__bar {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding: 0.75rem var(--gutter);
  border-bottom: 1px solid var(--hairline);
}
.search__input {
  flex: 1;
  border: 0;
  background: transparent;
  padding-block: 0.5rem;
  font-family: var(--font-display);
  font-size: 1.15rem;
  text-transform: uppercase;
  letter-spacing: 0.08em;
}
.search__input:focus { outline: none; }               /* the ring sits on the bar */
.search__bar:focus-within { outline: 2px solid var(--olive); outline-offset: -2px; }
.search__input::-webkit-search-cancel-button { display: none; }
.search__body { padding: 1.25rem var(--gutter) 1.75rem; overflow-y: auto; }

.search-result {
  display: flex;
  align-items: center;
  gap: 1rem;
  padding-block: 0.75rem;
  border-bottom: 1px solid var(--hairline);
}
.search-result:last-child { border-bottom: 0; }
.search-result__img {
  width: 64px; height: 64px;
  flex: none;
  border: 1px solid var(--hairline);
  background: var(--cream-deep);
  object-fit: contain;
  padding: 4px;
}
.search-result > span { min-width: 0; flex: 1; }
.search-result__name {
  display: block;
  font-family: var(--font-display);
  text-transform: uppercase;
  font-size: 0.95rem;
  letter-spacing: 0.03em;
}
.search-result__meta {
  display: block;
  font-size: 0.78rem;
  color: var(--ink-soft);
  overflow: hidden;
  text-overflow: ellipsis;
  white-space: nowrap;
}
.search-result__price {
  /* flex:none, or the rule above makes it share the row with the text block. */
  flex: none;
  margin-left: auto;
  font-family: var(--font-mono);
  font-size: 0.75rem;
  color: var(--ink-soft);
}

/* ------------------------------------------------------ 10 cart drawer -- */

.drawer {
  position: fixed;
  inset: 0;
  z-index: var(--z-drawer);
  visibility: hidden;
}
.drawer.is-open { visibility: visible; }
.drawer__scrim {
  position: absolute; inset: 0;
  background: rgb(37 40 32 / 0.45);
  opacity: 0;
  transition: opacity var(--speed) var(--ease);
}
.drawer.is-open .drawer__scrim { opacity: 1; }
.drawer__panel {
  position: absolute;
  right: 0; top: 0;
  height: 100%;
  width: min(100%, 430px);
  background: var(--cream);
  display: flex;
  flex-direction: column;
  transform: translateX(100%);
  transition: transform var(--speed) var(--ease);
  box-shadow: 0 0 70px -20px rgb(37 40 32 / 0.6);
}
.drawer.is-open .drawer__panel { transform: none; }
.drawer__head {
  display: flex; align-items: center; justify-content: space-between;
  padding: 0.75rem 1rem 0.75rem 1.25rem;
  border-bottom: 1px solid var(--hairline);
}
.drawer__body { flex: 1; overflow-y: auto; padding-inline: 1.25rem; }
.drawer__foot {
  border-top: 1px solid var(--hairline);
  padding: 1.25rem;
  padding-bottom: max(1.25rem, env(safe-area-inset-bottom));
}
.drawer__empty { flex: 1; display: grid; place-content: center; text-align: center; gap: 1.25rem; padding: 2rem; }

/* A small khaki dot on the account icon when someone is signed in. */
.icon-btn__dot {
  position: absolute;
  top: 8px; right: 8px;
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--khaki-deep);
}

/* ------------------------------------------------- 10b account area -- */

.account-layout { display: grid; gap: 2.5rem; align-items: start; }
@media (min-width: 900px) {
  .account-layout { grid-template-columns: 220px minmax(0, 1fr); gap: 4rem; }
}

.account-nav { display: flex; flex-direction: column; gap: 0.25rem; }
@media (min-width: 900px) {
  .account-nav { position: sticky; top: calc(var(--header-h) + 1.5rem); }
}
.account-nav__link {
  display: flex;
  align-items: center;
  min-height: 44px;
  padding: 0.5rem 0.75rem;
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-soft);
  border-left: 2px solid transparent;
  transition: color var(--speed) var(--ease), border-color var(--speed) var(--ease),
              background-color var(--speed) var(--ease);
}
.account-nav__link:hover { color: var(--ink); background: rgb(37 40 32 / 0.04); }
.account-nav__link.is-current {
  color: var(--ink);
  border-left-color: var(--khaki-deep);
  background: rgb(37 40 32 / 0.05);
}

.account-card {
  border: 1px solid var(--hairline);
  background: var(--cream);
  padding: clamp(1.1rem, 3vw, 1.75rem);
}
.account-card + .account-card { margin-top: 1.25rem; }
.account-card__head {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  flex-wrap: wrap;
  margin-bottom: 1.1rem;
}
.account-card__title {
  font-family: var(--font-display);
  font-size: 1.05rem;
  text-transform: uppercase;
  letter-spacing: 0.05em;
}

/* One order in a list. */
.order-row {
  display: grid;
  gap: 0.5rem 1.25rem;
  padding-block: 1.1rem;
  border-bottom: 1px solid var(--hairline);
  align-items: center;
}
@media (min-width: 720px) {
  .order-row { grid-template-columns: 8.5rem 1fr auto auto; }
}
.order-row:last-child { border-bottom: 0; }
.order-row__ref { font-family: var(--font-mono); font-size: 0.8rem; letter-spacing: 0.06em; }
.order-row__meta { font-size: 0.82rem; color: var(--ink-soft); }

/* Payment / fulfilment state. */
.state {
  display: inline-block;
  padding: 0.25rem 0.6rem;
  font-family: var(--font-mono);
  font-size: 0.6rem;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  border: 1px solid var(--hairline-strong);
  color: var(--ink-soft);
  white-space: nowrap;
}
.state--paid    { background: var(--olive); border-color: var(--olive); color: var(--cream); }
.state--pending { background: var(--khaki); border-color: var(--khaki); color: var(--charcoal); }
.state--failed,
.state--cancelled { border-color: #8c3a2b; color: #8c3a2b; }

/* Address cards, and the radio-selectable version used at checkout. */
.address-grid { display: grid; gap: 1rem; }
@media (min-width: 720px) { .address-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); } }

.address-card { position: relative; border: 1px solid var(--hairline); padding: 1.1rem; background: var(--cream); }
.address-card__label {
  font-family: var(--font-mono);
  font-size: 0.625rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-faint);
  display: block;
  margin-bottom: 0.5rem;
}
.address-card address { font-style: normal; font-size: 0.875rem; line-height: 1.55; color: var(--ink-soft); }
.address-card__actions { display: flex; gap: 0.5rem; margin-top: 0.9rem; flex-wrap: wrap; }

.address-choice { position: relative; }
.address-choice input { position: absolute; inset: 0; opacity: 0; cursor: pointer; }
.address-choice input:checked + .address-card { border-color: var(--olive); box-shadow: inset 0 0 0 1px var(--olive); }
.address-choice input:focus-visible + .address-card { outline: 2px solid var(--olive); outline-offset: 2px; }

/* The "off to PayFast" hand-off panel. */
.handoff {
  border: 1px solid var(--hairline);
  background: var(--cream);
  padding: clamp(1.5rem, 4vw, 2.5rem);
  text-align: center;
  max-width: 34rem;
  margin-inline: auto;
}
.handoff__mark { font-family: var(--font-mono); font-size: 0.7rem; letter-spacing: 0.2em; text-transform: uppercase; color: var(--ink-faint); }
.handoff__ref { font-family: var(--font-mono); font-size: 1.35rem; letter-spacing: 0.08em; margin-block: 0.5rem 1rem; }

/* ---------------------------------------------------------- 11 footer -- */

.site-footer {
  position: relative;
  overflow: hidden;
  background: var(--olive-dark);
  color: var(--cream);
  z-index: 2;
}
/* The link columns pair up on a phone. One per row with a 3rem gap made the
   footer 1.6 phone screens tall -- longer than the hero -- for four short
   lists. The brand block keeps the full width above them. */
.site-footer__grid {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: 1.5rem;
  row-gap: 2.25rem;
  padding-block: clamp(2.5rem, 6vw, 5rem);
  position: relative;
}
.site-footer__grid > :first-child { grid-column: 1 / -1; }

@media (min-width: 880px) {
  .site-footer__grid { grid-template-columns: 1.15fr repeat(3, minmax(0, 0.8fr)); gap: 2.5rem; }
  .site-footer__grid > :first-child { grid-column: auto; }
}
.site-footer__logo { height: 5rem; width: auto; }
.site-footer__col-title {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-faint);
  margin-bottom: 1rem;
  display: block;
}
.site-footer__links { display: flex; flex-direction: column; gap: 0.65rem; }
.site-footer__links a {
  font-size: 0.875rem;
  color: var(--ink-soft);
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}
.site-footer__links a:hover { color: var(--cream); }

/* Two columns of links on a phone, so the footer does not run to a screen and
   a half. */
@media (max-width: 719px) {
  .site-footer__links { display: grid; grid-template-columns: 1fr 1fr; gap: 0.25rem 1rem; }
}
.site-footer__bottom {
  position: relative;
  display: flex;
  flex-direction: column;
  gap: 1.5rem;
  padding-block: 2rem;
  border-top: 1px solid var(--hairline);
}
@media (min-width: 980px) {
  .site-footer__bottom { flex-direction: row; align-items: center; justify-content: space-between; }
}
.site-footer__badge { height: 3.5rem; width: auto; opacity: 0.9; }
.site-footer__motto { white-space: nowrap; }

.site-footer__bottom ul a {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
}

/* ------------------------------------------------ 12 product card/grid -- */

.grid { display: grid; gap: 1.25rem 1.25rem; }
.grid--2 { grid-template-columns: repeat(2, minmax(0, 1fr)); }
.grid--3 { grid-template-columns: repeat(auto-fill, minmax(min(100%, 240px), 1fr)); }
.grid--products {
  grid-template-columns: repeat(2, minmax(0, 1fr));
  column-gap: 1.25rem;
  row-gap: 2.5rem;
}
@media (min-width: 900px)  { .grid--products { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 1100px) { .grid--products { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

.card { display: flex; flex-direction: column; height: 100%; }
.card__link { display: flex; flex-direction: column; flex: 1; }
.card__media {
  position: relative;
  aspect-ratio: 1;
  overflow: hidden;
  border: 1px solid var(--hairline);
  /* Matches the ground baked into the product artwork, so no lighter rectangle
     floats inside the tile. The hairline and ticks do the framing. */
  background: var(--cream);
}
/* A product saved before its photographs arrive. Drawn sand rather than a
   broken-image icon, so a half-filled catalogue still reads as a catalogue. */
.card__img--none {
  display: block;
  width: 100%;
  height: 100%;
  background:
    linear-gradient(180deg, var(--dune-far) 0%, var(--dune-mid) 62%, var(--dune-near) 100%);
}

.card__img {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  object-fit: contain;
  padding: 0.75rem;
  transition: opacity 0.7s var(--ease), transform 0.7s var(--ease);
}
.card__img--hover { opacity: 0; }
.card:hover .card__img--primary { opacity: 0; }
.card:hover .card__img--hover { opacity: 1; }
.card:hover .card__img--solo { transform: scale(1.03); }
/* The index number. One treatment: mono, khaki-deep. Also used by .step__num
   and the numbered blocks on Our Story. */
.num-mark,
.card__index,
.step__num {
  font-family: var(--font-mono);
  font-size: 0.6875rem;
  letter-spacing: 0.18em;
  color: var(--khaki-deep);
}
.card__index {
  position: absolute;
  top: 0.85rem; left: 0.85rem;
  font-size: 0.625rem;
}
.card__badges {
  position: absolute;
  top: 0.7rem; right: 0.7rem;
  display: flex; flex-direction: column; align-items: flex-end; gap: 0.4rem;
}
.card__body { display: flex; flex-direction: column; gap: 0.45rem; padding-top: 1rem; flex: 1; }
.card__name {
  font-family: var(--font-display);
  font-size: 1.0625rem;
  text-transform: uppercase;
  letter-spacing: 0.02em;
  line-height: 1.15;
}
.card__summary {
  font-size: 0.8125rem;
  line-height: 1.4;
  color: var(--ink-soft);
  display: -webkit-box;
  -webkit-line-clamp: 2;
  -webkit-box-orient: vertical;
  overflow: hidden;
}
.card__price-row { margin-top: auto; padding-top: 0.5rem; display: flex; align-items: baseline; flex-wrap: wrap; gap: 0.75rem; }
.card__price { font-family: var(--font-mono); font-size: 0.8125rem; letter-spacing: 0.06em; }
.card__price--muted { color: var(--ink-soft); }
.card__swatches { display: flex; flex-wrap: wrap; gap: 0.4rem; padding-top: 0.35rem; }
.card__swatch {
  width: 14px; height: 14px;
  border-radius: 50%;
  border: 1px solid var(--hairline-strong);
  display: block;
}
/* The colourway this card is showing. Turns six repeated rows into "this one
   of six". */
.card__swatch--current {
  box-shadow: 0 0 0 1.5px var(--cream), 0 0 0 2.5px var(--charcoal);
}

/* The "range is growing" tile — a deliberate design element, not a product. */
@media (max-width: 640px) {
  .card--placeholder { grid-column: 1 / -1; aspect-ratio: auto; }
}

.card--placeholder {
  border: 1px dashed var(--hairline-strong);
  display: flex;
  flex-direction: column;
  justify-content: center;
  align-items: flex-start;
  gap: 0.9rem;
  padding: 1.75rem;
  aspect-ratio: 1;
  text-align: left;
  color: var(--ink-soft);
}
.range-note { font-size: 0.8125rem; line-height: 1.45; color: var(--ink-soft); }

/* ------------------------------------------------------------- 13 home -- */

/* --------------------------------------------------------- the dune -- */

/*
 * Four receding ridges. Each is coloured by a variable so the whole horizon can
 * be re-tinted for the band it sits in, and each moves at its own rate on
 * scroll, which is what reads as distance rather than as a decoration.
 */
:root {
  --dune-far:   #eee7d7;
  --dune-mid:   #e2d7bd;
  --dune-near:  #ccbc99;
  --dune-fore:  #b09b72;
  --dune-face:  rgb(37 40 32 / 0.055);
  --dune-track: rgb(37 40 32 / 0.30);
  --dune-grass: rgb(37 40 32 / 0.45);
}

.dune {
  position: absolute;
  inset: 0;
  overflow: hidden;
  pointer-events: none;
}
.dune__svg { display: block; width: 100%; height: 100%; }
.dune--flip .dune__svg { transform: scaleY(-1); }

.dune__layer { will-change: transform; }
.dune__ridge { fill: var(--dune-layer-colour, var(--dune-near)); }
.dune__face  { fill: var(--dune-face); }

.dune__layer--far  { --dune-layer-colour: var(--dune-far); }
.dune__layer--mid  { --dune-layer-colour: var(--dune-mid); }
.dune__layer--near { --dune-layer-colour: var(--dune-near); }
.dune__layer--fore { --dune-layer-colour: var(--dune-fore); }

.dune__tracks ellipse { fill: var(--dune-track); }
.dune__grass path {
  fill: none;
  stroke: var(--dune-grass);
  stroke-width: 1.6;
  stroke-linecap: round;
}

/* On olive and charcoal bands the sand becomes the light, not the ground.
   These alphas stay low on purpose. The foreground ridge is a big unbroken
   mass, so at 0.22 it composited to about rgb(94 100 82) over the dusk
   gradient -- bright enough to stop reading as a ridge and start reading as a
   pale slab laid across the band, which is what it looked like on /customise.
   Halving it keeps the horizon legible as a lift out of the dark without the
   sand ever becoming the loudest thing in a section whose job is the type. */
.on-dark {
  --dune-far:   rgb(245 241 231 / 0.04);
  --dune-mid:   rgb(245 241 231 / 0.065);
  --dune-near:  rgb(245 241 231 / 0.10);
  --dune-fore:  rgb(245 241 231 / 0.14);
  --dune-face:  rgb(0 0 0 / 0.07);
  --dune-track: rgb(197 176 120 / 0.55);
  --dune-grass: rgb(197 176 120 / 0.5);
}

/* ------------------------------------------------------- the hero -- */

.hero-dune {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  min-height: min(94svh, 940px);
  display: flex;
  flex-direction: column;
  justify-content: center;
  /* Sky. Warm at the horizon, cooler overhead — the light an hour after dawn. */
  background:
    linear-gradient(
      to bottom,
      #f9f6ef 0%,
      #f6f1e6 26%,
      #f3ecdc 48%,
      #f0e5cd 66%,
      #eddfc2 100%
    );
}

.hero-dune__inner {
  position: relative;
  z-index: 2;
  /* Sits in the sky. The bottom padding is what keeps the copy clear of the
     ridge line instead of sinking into it. */
  padding-block: clamp(1.5rem, 4vh, 3rem) clamp(19rem, 48vh, 32rem);
  display: flex;
  flex-direction: column;
  align-items: center;
  text-align: center;
  gap: clamp(1rem, 2.2vh, 1.6rem);
}

.hero-dune__logo {
  width: min(100%, 42rem);
  height: auto;
}

.hero-dune__lede {
  max-width: 40ch;
  font-size: clamp(1rem, 1.5vw, 1.2rem);
  line-height: 1.55;
  color: var(--ink-soft);
}

.hero-dune__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem;
  justify-content: center;
  margin-top: 0.5rem;
}
@media (max-width: 560px) {
  .hero-dune__actions { flex-direction: column; align-self: stretch; }
  .hero-dune__actions .btn { width: 100%; }
}

/* The dune sits behind the copy and is anchored to the bottom of the band. */
.hero-dune .dune {
  z-index: 1;
  inset: auto 0 0 0;
  height: 66%;
}

/* A quiet cue that there is more below, sitting on the near sand. */
.hero-dune__cue {
  position: absolute;
  left: 50%;
  bottom: clamp(1.25rem, 4vh, 2.5rem);
  transform: translateX(-50%);
  z-index: 3;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.5rem;
  color: var(--ink-faint);
}
.hero-dune__cue-line {
  width: 1px;
  height: 2.5rem;
  background: linear-gradient(to bottom, transparent, var(--hairline-strong));
}

/* Coordinates pinned to the corner, the field-log device at hero scale.
   Out of the layout entirely below 46rem: the eyebrow beside them wraps to two
   lines on a phone and the two ran straight through each other. The reading is
   a flourish, the eyebrow says what the shop sells, so on a narrow screen the
   flourish is the one that goes. */
.hero-dune__coords { display: none; }

@media (min-width: 46rem) {
  .hero-dune__coords {
    display: block;
    position: absolute;
    right: var(--gutter);
    top: clamp(1.5rem, 5vh, 3rem);
    z-index: 3;
    text-align: right;
    line-height: 1.9;
  }
}

@media (prefers-reduced-motion: reduce) {
  .dune__layer { transform: none !important; }
}

/* z-index matches .section so the fixed paper grain never lands on top of the
   approved primary logo artwork. */
.hero { position: relative; overflow: hidden; z-index: 2; }
.hero__inner {
  position: relative;
  display: grid;
  gap: clamp(2rem, 5vw, 3.5rem);
  padding-block: clamp(3rem, 7vw, 5.5rem);
  align-items: center;
}
@media (min-width: 900px) {
  .hero__inner { grid-template-columns: 1fr 1fr; gap: 4rem; }
}
.hero__logo { width: min(100%, 30rem); height: auto; }
.hero__media { position: relative; border: 1px solid var(--hairline); }
.hero__media img { width: 100%; aspect-ratio: 16 / 10; object-fit: cover; }
.hero__media-caption {
  position: absolute;
  left: 0.9rem; bottom: 0.9rem;
  background: rgb(245 241 231 / 0.9);
  padding: 0.3rem 0.55rem;
}
/* --ink-faint over a 90% cream panel is too light to read. */
.hero__media-caption .coords { color: var(--ink-soft); }

/* Range tiles: the live collections as image-led entry points. Laid out on
   .grid--products, so they follow the same 2 / 3 / 4 column rhythm as the
   product grid. */
.range-tile { display: flex; flex-direction: column; height: 100%; }
.range-tile__media {
  position: relative;
  aspect-ratio: 1;
  overflow: hidden;
  border: 1px solid var(--hairline);
  background: var(--cream-deep);
}
.range-tile__media img {
  position: absolute;
  inset: 0;
  width: 100%; height: 100%;
  object-fit: cover;
  transition: transform 0.8s var(--ease);
}
.range-tile:hover .range-tile__media img { transform: scale(1.03); }
.range-tile__body { display: flex; flex-direction: column; gap: 0.4rem; padding-top: 1rem; flex: 1; }
.range-tile__tagline { font-size: 0.8125rem; line-height: 1.4; color: var(--ink-soft); }
.range-tile__foot {
  margin-top: auto;
  padding-top: 0.9rem;
  border-top: 1px solid var(--hairline);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  color: var(--ink-soft);
}
.range-tile:hover .btn__arrow { transform: translateX(4px); }

/* A framed brand device standing in for a photograph in the story block:
   the ridge drawing, captioned like a specimen plate. */
.ridge-panel {
  display: grid;
  gap: 1.5rem;
  justify-items: center;
  align-content: center;
  text-align: center;
  padding: clamp(1.5rem, 4vw, 2.75rem);
}

/* Copy inside the "range is growing" tile sits at card-summary size. */
.range-note { font-size: 0.8125rem; line-height: 1.45; }

/* Newsletter: the field takes the room, the button keeps its size. */
.subscribe { display: grid; gap: 0.75rem; }
@media (min-width: 560px) { .subscribe { grid-template-columns: minmax(0, 1fr) auto; } }

/* ------------------------------------------------- 13b the dune system -- */
/*
 * The devices that carry "the dune" through the rest of the site, so the hero
 * is not a one-off trick sitting on top of a plain shop.
 */

/* A section whose ground is sand rather than flat cream. */
.section--sand-grad {
  background: linear-gradient(to bottom, #f6f1e6 0%, #f1e8d6 55%, #ead9be 100%);
}
.section--dusk {
  background: linear-gradient(to bottom, var(--olive-dark) 0%, #333c28 55%, var(--charcoal) 100%);
}

/* A dune strip that sits between two sections, so the sand carries down the
   page instead of stopping under the hero. Height is deliberately small — it
   is a horizon, not a scene. */
.dune-divider {
  position: relative;
  height: clamp(6rem, 14vw, 13rem);
  overflow: hidden;
  z-index: 2;
}
.dune-divider .dune { inset: auto 0 -1px 0; height: 130%; }
.dune-divider--top { transform: scaleY(-1); }

/* Content that runs to the edge of the screen, breaking the container. */
.bleed { width: 100%; max-width: none; padding-inline: 0; }
.bleed-right { margin-right: calc(50% - 50vw); }
.bleed-left  { margin-left: calc(50% - 50vw); }

/* Asymmetric feature: copy on one side, image running off the page on the
   other. The thing the old layout never did. */
.feature {
  position: relative;
  display: grid;
  gap: clamp(2rem, 5vw, 4rem);
  align-items: center;
}
@media (min-width: 900px) {
  .feature { grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr); }
  .feature--reverse { grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr); }
  .feature--reverse .feature__media { order: -1; }
  .feature__media--bleed-right { margin-right: calc(50% - 50vw); }
  .feature__media--bleed-left  { margin-left: calc(50% - 50vw); }
}
.feature__media { position: relative; }
.feature__media img { width: 100%; height: auto; display: block; }

/* The lead product in "In the range now".
 *
 * This treatment was drawn against a LANDSCAPE photograph, where stretching the
 * image across the bleed column reads as a band running off the edge. The
 * catalogue holds SQUARE product shots, and two attempts at this went wrong in
 * opposite directions: at width:100% a square came out roughly as tall as the
 * viewport for one tee, and capping it with width:auto pinned a 520px image to
 * the start of a column that begins 277px off-screen -- so the garment was half
 * off the page with 800px of dead cream between it and the copy.
 *
 * What actually wants to bleed is the PLATE, not the garment. So the column is
 * a band of cream running off the edge of the screen, and the garment sits on
 * it at a sensible size, pushed to the side the copy is on so the two read as
 * one row. The drama stays; the product stops being cropped or stranded.
 */
@media (min-width: 900px) {
  #featured .feature__media--bleed-left,
  #featured .feature__media--bleed-right {
    display: flex;
    align-items: center;
    /* Deeper than the cream band it sits in, so the plate is actually visible
       as a plate. At var(--cream) on a section--cream it was the same colour as
       the page and the bleed did nothing at all -- the garment just looked
       loose on the left. A shallow sand gradient ties it to the horizon. */
    background: linear-gradient(180deg, var(--cream-deep) 0%, #e6dcc6 100%);
    padding-block: clamp(1.5rem, 3vw, 2.75rem);
  }
  #featured .feature__media--bleed-left {
    justify-content: flex-end;
    padding-right: clamp(1.5rem, 3.5vw, 3.5rem);
    padding-left: clamp(1rem, 2vw, 2rem);
  }
  #featured .feature__media--bleed-right {
    justify-content: flex-start;
    padding-left: clamp(1.5rem, 3.5vw, 3.5rem);
    padding-right: clamp(1rem, 2vw, 2rem);
  }
  #featured .feature__media--bleed-left img,
  #featured .feature__media--bleed-right img {
    width: auto;
    max-width: 100%;
    max-height: min(54vh, 470px);
  }
}

/* No bleed below 900px -- the negative margin is inside that query -- so the
   image is simply a block. Centre it and keep it off the full height. */
@media (max-width: 899px) {
  #featured .feature__media img {
    max-height: 58vh;
    width: auto;
    max-width: 100%;
    margin-inline: auto;
  }
}

/* A photograph whose lower edge dissolves into drawn sand. */
.plate-photo { position: relative; overflow: hidden; }
.plate-photo img { display: block; width: 100%; height: auto; }
.plate-photo__dune { position: absolute; inset: auto 0 -1px 0; height: 46%; }
.plate-photo__caption {
  position: absolute;
  left: 1rem; bottom: 1rem;
  z-index: 3;
  background: rgb(245 241 231 / 0.9);
  padding: 0.35rem 0.6rem;
}

/* The scale moment. One word, enormous, sitting behind or beside the copy. */
.display-mega {
  font-family: var(--font-display);
  text-transform: uppercase;
  font-weight: 600;
  font-size: clamp(3.5rem, 15vw, 13rem);
  line-height: 0.82;
  letter-spacing: -0.005em;
}
.display-mega--ghost {
  color: transparent;
  -webkit-text-stroke: 1px var(--hairline-strong);
  pointer-events: none;
  user-select: none;
}
.watermark {
  position: absolute;
  z-index: 0;
  inset: auto 0 -0.12em 0;
  text-align: center;
  opacity: 0.5;
  pointer-events: none;
}

/* Product tiles without the hard box: the garment sits on sand, and only the
   hover state draws a line. Used where the grid should feel editorial. */
.card--soft .card__media {
  border-color: transparent;
  background: transparent;
}
.card--soft .card__media::before {
  content: "";
  position: absolute;
  inset: auto 8% 6% 8%;
  height: 42%;
  background: radial-gradient(ellipse at center, rgb(37 40 32 / 0.09), transparent 70%);
  z-index: 0;
}
.card--soft .card__img { z-index: 1; }
.card--soft .card__media.ticks::before,
.card--soft .card__media.ticks::after { opacity: 0; transition: opacity var(--speed) var(--ease); }
.card--soft:hover .card__media.ticks::before,
.card--soft:hover .card__media.ticks::after { opacity: 1; }

/* Two tiles that overlap instead of sitting in a neat row. */
/* Two up from the smallest screen, matching .grid--products. Stacked one per
   row it ran four garments down 2.7 phone screens on the landing page on its
   own, which is most of a scroll for four tiles. The drop on the even column is
   the part that needs room, so that stays on desktop. */
.stagger {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  align-items: start;
  column-gap: 1.25rem;
  row-gap: clamp(1.75rem, 4vw, 2.5rem);
}
@media (min-width: 820px) {
  .stagger { gap: clamp(1.25rem, 3vw, 2.5rem); }
  .stagger > :nth-child(even) { transform: translateY(clamp(2rem, 6vw, 5rem)); }
}

/* Sand grain, heavier than the page-wide paper grain, for hero and dark bands. */
.grain-sand { position: relative; }
.grain-sand::after {
  content: "";
  position: absolute;
  inset: 0;
  pointer-events: none;
  opacity: 0.5;
  background-image: url("/assets/textures/grain.svg");
  background-size: 180px 180px;
  mix-blend-mode: overlay;
  z-index: 1;
}

/* A quiet marker line, used to caption a bleeding image. */
.field-note {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  font-family: var(--font-mono);
  font-size: 0.625rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-faint);
}
.field-note::before {
  content: "";
  width: 1.75rem;
  height: 1px;
  background: currentColor;
}

/* --------------------------------------------------- 14 shop & filters -- */

.shop { display: grid; gap: 2rem; align-items: start; }
@media (min-width: 1000px) {
  .shop { grid-template-columns: 230px minmax(0, 1fr); gap: 3rem; }
}

.filters { display: flex; flex-direction: column; gap: 1.75rem; }
@media (min-width: 1000px) {
  .filters { position: sticky; top: calc(var(--header-h) + 1.5rem); }
}
.filters__group { display: flex; flex-direction: column; gap: 0.75rem; }
.filters__title {
  font-family: var(--font-mono);
  font-size: 0.625rem;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-soft);
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 0.5rem;
}
.filters__list { display: flex; flex-direction: column; gap: 0.4rem; }
.filters__option { display: flex; align-items: center; gap: 0.6rem; font-size: 0.85rem; color: var(--ink-soft); cursor: pointer; min-height: 32px; }
.filters__option:hover { color: var(--ink); }
.filters__option input { accent-color: var(--olive); width: 16px; height: 16px; }

.shop__bar {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding-bottom: 1.25rem;
  margin-bottom: 1.75rem;
  border-bottom: 1px solid var(--hairline);
}
/* Filter button and sort select are one control row: same height, no drift. */
.shop__bar .btn,
.shop__bar .select {
  min-height: 44px;
  height: 44px;
  padding-block: 0;
}
.shop__sort { width: 13rem; max-width: 100%; }

.filter-drawer { position: fixed; inset: 0; z-index: var(--z-drawer); visibility: hidden; }
.filter-drawer.is-open { visibility: visible; }
.filter-drawer__scrim { position: absolute; inset: 0; background: rgb(37 40 32 / 0.45); opacity: 0; transition: opacity var(--speed) var(--ease); }
.filter-drawer.is-open .filter-drawer__scrim { opacity: 1; }
.filter-drawer__panel {
  position: absolute;
  left: 0; right: 0; bottom: 0;
  max-height: 85dvh;
  background: var(--cream);
  transform: translateY(100%);
  transition: transform var(--speed) var(--ease);
  display: flex; flex-direction: column;
}
.filter-drawer.is-open .filter-drawer__panel { transform: none; }
@media (min-width: 1000px) { .filter-btn { display: none; } }

.filter-drawer__head,
.filter-drawer__foot {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding: 0.6rem var(--gutter);
  flex: none;
}
.filter-drawer__head { border-bottom: 1px solid var(--hairline); }
.filter-drawer__foot { border-top: 1px solid var(--hairline); }
.filter-drawer__body { overflow-y: auto; padding: 1.5rem var(--gutter); }
/* Thumbs, not a mouse pointer: the drawer's rows get the full touch target. */
.filter-drawer .filters__option { min-height: 44px; }

/* The rail is the desktop home of the filters; below 1000px they live in the
   drawer instead — unless there is no JavaScript to open it, in which case the
   rail comes back and filtering still works. */
.shop__rail { display: none; }
@media (min-width: 1000px) { .shop__rail { display: block; } }
.no-js .shop__rail { display: block; }
.no-js .filter-btn { display: none; }

/* Honest empty state. Shared by /shop and /search. */
.empty-state {
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 1rem;
  text-align: center;
  padding: clamp(2.25rem, 6vw, 3.75rem) var(--gutter);
}

/* Collection header: the collection image set as one wide surveyed plate.
   Contained, never cropped — these are garment mock-ups, not scenery. */
.collection-plate { overflow: hidden; background: var(--cream-deep); }
.collection-plate img {
  position: relative;
  display: block;
  width: 100%;
  height: clamp(15rem, 34vw, 26rem);
  object-fit: contain;
  padding: clamp(1rem, 3vw, 2.25rem);
}

/* --------------------------------------------------- 15 product page -- */

.pdp { display: grid; gap: 2.5rem; align-items: start; }
@media (min-width: 900px) {
  .pdp { grid-template-columns: minmax(0, 1.15fr) minmax(0, 0.85fr); gap: 3.5rem; }
}
@media (min-width: 1200px) { .pdp { grid-template-columns: minmax(0, 1.25fr) minmax(0, 0.75fr); gap: 4.5rem; } }

.gallery { display: grid; gap: 1rem; }
@media (min-width: 700px) {
  .gallery { grid-template-columns: 82px minmax(0, 1fr); align-items: start; }
}
.gallery__main {
  position: relative;
  aspect-ratio: 1;
  border: 1px solid var(--hairline);
  background: var(--cream-deep);
  order: -1;
}
@media (min-width: 700px) { .gallery__main { order: 0; } }
.gallery__main img { position: absolute; inset: 0; width: 100%; height: 100%; object-fit: contain; padding: 1.25rem; }
.gallery__thumbs { display: flex; gap: 0.6rem; overflow-x: auto; scroll-snap-type: x mandatory; }
@media (min-width: 700px) { .gallery__thumbs { flex-direction: column; overflow: visible; } }
.gallery__thumb {
  flex: none;
  width: 72px; height: 72px;
  border: 1px solid var(--hairline);
  background: var(--cream-deep);
  padding: 4px;
  scroll-snap-align: start;
  transition: border-color var(--speed) var(--ease);
}
.gallery__thumb img { width: 100%; height: 100%; object-fit: contain; }
.gallery__thumb[aria-current="true"] { border-color: var(--charcoal); }

.buybox { display: flex; flex-direction: column; gap: 1.5rem; }
@media (min-width: 900px) {
  .buybox { position: sticky; top: calc(var(--header-h) + 1.5rem); }
}
.buybox__price { font-family: var(--font-mono); font-size: 1.35rem; letter-spacing: 0.04em; }

.spec-list { display: grid; grid-template-columns: auto 1fr; gap: 0.55rem 1.5rem; font-size: 0.85rem; }
.spec-list dt { font-family: var(--font-mono); font-size: 0.625rem; letter-spacing: 0.2em; text-transform: uppercase; color: var(--ink-faint); padding-top: 0.15rem; }
.spec-list dd { margin: 0; color: var(--ink-soft); }

.accordion { border-top: 1px solid var(--hairline); }
.accordion__item { border-bottom: 1px solid var(--hairline); }
.accordion__trigger {
  width: 100%;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  padding-block: 1.1rem;
  text-align: left;
  font-family: var(--font-display);
  font-size: 0.95rem;
  text-transform: uppercase;
  letter-spacing: 0.06em;
  min-height: 52px;
}
.accordion__icon { position: relative; width: 12px; height: 12px; flex: none; }
.accordion__icon::before,
.accordion__icon::after {
  content: "";
  position: absolute;
  background: currentColor;
  transition: transform var(--speed) var(--ease), opacity var(--speed) var(--ease);
}
.accordion__icon::before { top: 5px; left: 0; width: 12px; height: 1px; }
.accordion__icon::after  { left: 5px; top: 0; width: 1px; height: 12px; }
.accordion__trigger[aria-expanded="true"] .accordion__icon::after { transform: rotate(90deg); opacity: 0; }
.accordion__panel { padding-bottom: 1.35rem; }
.accordion__panel[hidden] { display: none; }

.sticky-buy {
  position: fixed;
  left: 0; right: 0; bottom: 0;
  z-index: 40;
  background: rgb(245 241 231 / 0.97);
  backdrop-filter: blur(6px);
  border-top: 1px solid var(--hairline);
  padding: 0.75rem var(--gutter);
  padding-bottom: max(0.75rem, env(safe-area-inset-bottom));
  display: flex;
  align-items: center;
  gap: 1rem;
  transform: translateY(110%);
  transition: transform var(--speed) var(--ease);
}
.sticky-buy.is-visible { transform: none; }
@media (min-width: 900px) { .sticky-buy { display: none; } }

/* ------------------------------------------------------- 16 customise -- */

.customise { display: grid; gap: 2.5rem; align-items: start; }
@media (min-width: 1000px) {
  .customise { grid-template-columns: minmax(0, 1fr) minmax(0, 420px); gap: 3.5rem; }
}

/* Source order is preview, then steps: on a phone the customer sees the
   artwork before the first field, and it never sits over one. On desktop the
   two are placed explicitly so the preview returns to the right-hand column. */
@media (min-width: 1000px) {
  .customise__preview { grid-column: 2; grid-row: 1; }
  .customise__steps   { grid-column: 1; grid-row: 1; min-width: 0; }
}

.plate {
  position: relative;
  width: 100%;
  background: var(--plate-ground, #d1c6b3);
  color: var(--plate-ink, #252820);
  transition: background-color var(--speed) var(--ease);
}
.plate__svg { display: block; width: 100%; height: auto; }
.plate__name {
  font-family: var(--font-display);
  font-weight: 600;
  letter-spacing: 0.06em;
}
.plate__style { font-family: var(--font-display); font-size: 16px; letter-spacing: 0.34em; }
.plate__strapline { font-family: var(--font-display); font-size: 11.5px; letter-spacing: 0.3em; }
/* expedition_plate() hides the style line with [hidden], which the UA sheet
   does not reliably apply inside an <svg>. Say it here instead. */
.plate__style-group[hidden] { display: none; }
.plate__vehicle { display: none; }
.plate[data-vehicle="bakkie-double"] .plate__vehicle[data-vehicle="bakkie-double"],
.plate[data-vehicle="bakkie-single"] .plate__vehicle[data-vehicle="bakkie-single"],
.plate[data-vehicle="wagon"] .plate__vehicle[data-vehicle="wagon"],
.plate[data-vehicle="suv"] .plate__vehicle[data-vehicle="suv"],
.plate[data-vehicle="canopy"] .plate__vehicle[data-vehicle="canopy"] { display: block; }

.preview-panel { display: flex; flex-direction: column; gap: 1.25rem; }
/* The plate is 420 × 520, so it is capped here: a sticky panel taller than the
   viewport would strand its own summary and price below the fold. */
.preview-panel .plate { max-width: 360px; margin-inline: auto; }
@media (min-width: 1000px) {
  .preview-panel { position: sticky; top: calc(var(--header-h) + 1.5rem); }
  .preview-panel .plate { max-width: 300px; }
}

.step { position: relative; border: 1px solid var(--hairline); background: var(--cream); padding: clamp(1.25rem, 3vw, 2rem); }
.step + .step { margin-top: 1.25rem; }
.step__head { display: flex; align-items: baseline; gap: 1rem; margin-bottom: 1.25rem; }
.step__num { flex: none; }
.step__title { font-family: var(--font-display); font-size: 1.15rem; text-transform: uppercase; letter-spacing: 0.04em; }

.uploads { display: grid; grid-template-columns: repeat(auto-fill, minmax(96px, 1fr)); gap: 0.75rem; }
.upload-tile { position: relative; aspect-ratio: 1; border: 1px solid var(--hairline); background: var(--cream-deep); }
.upload-tile img { width: 100%; height: 100%; object-fit: cover; }
/* A photo already held against the order has no thumbnail — storage/ is never
   web-servable — so the tile carries the file name and size instead. */
.upload-tile__meta {
  position: absolute;
  inset: 0;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  gap: 0.15rem;
  padding: 0.5rem;
  overflow: hidden;
}
.upload-tile__remove {
  position: absolute;
  top: 4px; right: 4px;
  width: 26px; height: 26px;
  display: grid; place-content: center;
  background: rgb(245 241 231 / 0.92);
  border: 1px solid var(--hairline-strong);
}
.dropzone {
  border: 1px dashed var(--hairline-strong);
  background: var(--cream-deep);
  padding: 2rem 1.25rem;
  text-align: center;
  transition: border-color var(--speed) var(--ease), background-color var(--speed) var(--ease);
}
.dropzone.is-dragging { border-color: var(--olive); background: rgb(79 91 58 / 0.06); }

.process { display: grid; gap: 1.25rem; }
@media (min-width: 760px)  { .process { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 1100px) { .process { grid-template-columns: repeat(5, 1fr); gap: 1rem; } }
.process__step { border-top: 1px solid var(--hairline-strong); padding-top: 1rem; }
.process__num { font-family: var(--font-mono); font-size: 0.75rem; letter-spacing: 0.16em; color: var(--khaki-deep); }

/* ------------------------------------------------- 17 cart & checkout -- */

.cart-layout { display: grid; gap: 2.5rem; align-items: start; }
@media (min-width: 980px) { .cart-layout { grid-template-columns: minmax(0, 1fr) 360px; gap: 3.5rem; } }

.line {
  display: grid;
  grid-template-columns: 88px minmax(0, 1fr);
  gap: 1.25rem;
  padding-block: 1.5rem;
  border-bottom: 1px solid var(--hairline);
}
.line__media { border: 1px solid var(--hairline); background: var(--cream-deep); aspect-ratio: 4 / 5; }
.line__media img { width: 100%; height: 100%; object-fit: contain; padding: 4px; }
.line__name { font-family: var(--font-display); font-size: 1rem; text-transform: uppercase; letter-spacing: 0.02em; }
.line__custom { border-left: 1px solid var(--khaki); padding-left: 0.9rem; margin-top: 0.6rem; }
.line__refs { display: flex; flex-wrap: wrap; gap: 0.4rem; margin-top: 0.5rem; }
.line__refs img { width: 44px; height: 44px; object-fit: cover; border: 1px solid var(--hairline); }

/* The custom brief is a spec list in far less room than the product page gives
   it — the checkout summary rail, and a phone. In both it stacks rather than
   squeezing the value into a third of the width. */
.summary .line__custom .spec-list { grid-template-columns: 1fr; row-gap: 0.1rem; }
.summary .line__custom .spec-list dd { margin-bottom: 0.5rem; }
@media (max-width: 600px) {
  .line__custom .spec-list { grid-template-columns: 1fr; row-gap: 0.1rem; }
  .line__custom .spec-list dd { margin-bottom: 0.5rem; }
}

.summary { border: 1px solid var(--hairline); padding: 1.5rem; background: var(--cream); }
@media (min-width: 980px) { .summary { position: sticky; top: calc(var(--header-h) + 1.5rem); } }
.summary__row { display: flex; justify-content: space-between; align-items: baseline; gap: 1rem; padding-block: 0.5rem; }
.summary__total { font-family: var(--font-mono); font-size: 1.25rem; }

.checkout { display: grid; gap: 2.5rem; align-items: start; }
@media (min-width: 980px) { .checkout { grid-template-columns: minmax(0, 1fr) 380px; gap: 3.5rem; } }
.fieldset { border: 0; padding: 0; margin: 0; display: flex; flex-direction: column; gap: 1rem; }
.fieldset + .fieldset { margin-top: 2.5rem; padding-top: 2.5rem; border-top: 1px solid var(--hairline); }
.fieldset__legend {
  display: flex; align-items: baseline; gap: 0.9rem;
  font-family: var(--font-display); font-size: 1.15rem; text-transform: uppercase; letter-spacing: 0.05em;
  padding: 0; margin-bottom: 0.5rem;
}
.field-row { display: grid; gap: 1rem; }
@media (min-width: 620px) { .field-row--2 { grid-template-columns: 1fr 1fr; } }

/* The order reference on /order-confirmed. Set large in mono because it is the
   one thing on the page a customer has to read back to us over the phone. */
.order-ref {
  font-family: var(--font-mono);
  font-variant-numeric: tabular-nums;
  font-size: clamp(1.6rem, 5.5vw, 2.5rem);
  letter-spacing: 0.12em;
  line-height: 1.15;
  margin-block: 0.75rem 0.5rem;
  overflow-wrap: anywhere;
}

/* --------------------------------------------- 18 editorial pages -- */

.story-block { display: grid; gap: 2rem; align-items: center; }
@media (min-width: 900px) {
  .story-block { grid-template-columns: 1fr 1fr; gap: 4rem; }
  .story-block:nth-child(even) .story-block__media { order: -1; }
}
.story-block__media { border: 1px solid var(--hairline); background: var(--cream-deep); }
.story-block__media img { width: 100%; aspect-ratio: 4 / 3; object-fit: cover; }

.values { display: grid; gap: 1.5rem; }
@media (min-width: 760px) { .values { grid-template-columns: repeat(3, 1fr); gap: 2.5rem; } }
.values__key { font-family: var(--font-display); font-size: 1.5rem; text-transform: uppercase; letter-spacing: 0.1em; }

.faq-layout { display: grid; gap: 2.5rem; align-items: start; }
@media (min-width: 900px) { .faq-layout { grid-template-columns: 200px minmax(0, 1fr); gap: 4rem; } }
.faq-nav { display: flex; flex-direction: column; gap: 0.5rem; }
@media (min-width: 900px) { .faq-nav { position: sticky; top: calc(var(--header-h) + 1.5rem); } }

.size-table th, .size-table td {
  border-bottom: 1px solid var(--hairline);
  padding: 0.8rem 0.5rem;
  text-align: left;
  font-family: var(--font-mono);
  font-size: 0.78rem;
}
.size-table th { color: var(--ink-faint); font-size: 0.625rem; letter-spacing: 0.2em; text-transform: uppercase; }
.size-table td.is-empty { color: var(--ink-faint); }

.legal-layout { display: grid; gap: 2.5rem; align-items: start; }
@media (min-width: 900px) { .legal-layout { grid-template-columns: 220px minmax(0, 1fr); gap: 4rem; } }
.legal-body h2 { font-size: 1.1rem; letter-spacing: 0.05em; margin-top: 2.25rem; margin-bottom: 0.75rem; }
.legal-body h2:first-child { margin-top: 0; }
.legal-body p { color: var(--ink-soft); max-width: var(--measure); }
.legal-body p + p { margin-top: 0.85rem; }

/* The legal sidebar behaves like the FAQ one: a sticky column of policy links. */
.legal-nav { display: flex; flex-direction: column; gap: 0.5rem; }
@media (min-width: 900px) { .legal-nav { position: sticky; top: calc(var(--header-h) + 1.5rem); } }

/* Sidebar links on /faq and /legal. Mono labels are small, so the target is
   opened up to 44px and the current page carries a khaki tick. */
.side-link { display: inline-flex; align-items: center; gap: 0.55rem; min-height: 44px; color: var(--ink-soft); }
.side-link:hover, .side-link:focus-visible { color: var(--ink); }
.side-link[aria-current="page"] { color: var(--ink); }
.side-link[aria-current="page"]::before {
  content: "";
  width: 0.75rem; height: 1px;
  flex: none;
  background: var(--khaki-deep);
}

/* Small mono links need a real hit area on touch. */
@media (max-width: 899px) {
  .breadcrumb a,
  .faq-nav a,
  .legal-nav a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }
}

/* The switch between the two. They are separate pages, so this is two links
   styled as one segmented control rather than a real toggle -- but it reads as
   one control, which is the point: "Create account" used to be a sentence in
   small grey text below the form, and a first-time customer had no obvious way
   in. The thumb sits under whichever page is open. */
.auth-switch {
  position: relative;
  display: grid;
  grid-template-columns: 1fr 1fr;
  padding: 4px;
  margin-bottom: clamp(1.5rem, 4vw, 2.25rem);
  border: 1px solid var(--hairline-strong);
  background: var(--cream-deep);
}
.auth-switch::before {
  content: "";
  position: absolute;
  top: 4px;
  bottom: 4px;
  left: 4px;
  width: calc(50% - 4px);
  background: var(--charcoal);
  transition: transform var(--speed) var(--ease);
}
.auth-switch--join::before { transform: translateX(100%); }

.auth-switch__opt {
  position: relative;
  z-index: 1;
  min-height: 44px;
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.4rem;
  text-align: center;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-soft);
  text-decoration: none;
  transition: color var(--speed) var(--ease);
}
.auth-switch__opt:hover { color: var(--ink); }
.auth-switch__opt[aria-current="page"] { color: var(--cream); }
.auth-switch__opt[aria-current="page"]:hover { color: var(--cream); }

/* --------------------------------------------------- 18b sign in / join -- */

/*
 * The two pages a customer has to pass through before they can buy anything,
 * and the two that looked the most like every other shop: a narrow form
 * centred on flat cream.
 *
 * So they get the ridge. The page splits into a dark panel carrying the dune
 * horizon and the reason to bother, and a cream side carrying the form. The
 * panel is the same dusk band the rest of the site uses, so signing in reads
 * as part of the journey rather than a detour into someone's admin system.
 */

.auth {
  display: grid;
  min-height: calc(100svh - var(--header-h));
}

/* The art column. On a narrow screen it is a banner above the form, not a
   column beside it -- a half-height dune on a phone would push the first
   field below the fold, which is the one thing a sign-in page must not do. */
/* The panel fills its grid row; the CONTENT inside it is what pins. Making the
   panel itself sticky left it shorter than a long form's row, so the column ran
   out partway down and showed a seam where the dune-lightened gradient met bare
   charcoal. Filling the row kills the seam, and pinning the inner block keeps
   the horizon beside the fields the whole way down a long form rather than
   stranding it at the far bottom of a very tall panel. */
.auth__aside {
  position: relative;
  overflow: hidden;

  /* Flat, and the colour the pinned block fades out to. The gradient lives on
     that block instead: stretched over a tall aside it was still olive right
     where the pinned block had already reached charcoal, which drew a hard
     line across the column on the long join form. */
  background: var(--charcoal);

  /* The dark-band alphas are tuned for a full-width section, where the ridge
     has the whole viewport to read across. In a column this narrow the same
     values flatten into a wash with no horizon in it, so the sand comes back
     up locally. Still nowhere near the cream-on-cream values. */
  --dune-far:   rgb(245 241 231 / 0.06);
  --dune-mid:   rgb(245 241 231 / 0.10);
  --dune-near:  rgb(245 241 231 / 0.155);
  --dune-fore:  rgb(245 241 231 / 0.225);
}
.auth__inner {
  position: relative;
  isolation: isolate;
  overflow: hidden;
  background: linear-gradient(to bottom, var(--olive-dark) 0%, #333c28 58%, var(--charcoal) 100%);
  display: flex;
  flex-direction: column;
  justify-content: space-between;
  gap: clamp(1.5rem, 4vw, 3rem);
  padding: clamp(1.75rem, 4vw, 3.25rem) clamp(1.5rem, 4vw, 3.5rem);
  min-height: clamp(13rem, 34vw, 20rem);
}
.auth__inner > .auth__mark,
.auth__inner > .stack { position: relative; z-index: 2; }

/* The foreground ridge runs to the bottom of its box, so the block's last row
   of pixels is sand-over-charcoal rather than charcoal, and met the flat aside
   below it as a visible step. This lands the dune into the aside colour before
   the edge arrives. */
.auth__inner::after {
  content: "";
  position: absolute;
  inset: auto 0 0 0;
  height: 16%;
  z-index: 1;
  background: linear-gradient(to bottom, rgb(37 40 32 / 0), var(--charcoal));
  pointer-events: none;
}

.auth__dune {
  position: absolute;
  inset: auto 0 0 0;
  height: 60%;
  z-index: 0;
}

/* The ghost word sits behind the dune, not in front, so the horizon always
   cuts across it. Clipped by the panel, never scrollable. */
.auth__word {
  position: absolute;
  /* Held in the empty middle of the panel rather than along the bottom edge,
     where the pitch and the points were burying it and it read as a smudge. */
  inset: 38% 0 auto 0;
  z-index: 1;
  text-align: center;
  font-family: var(--font-display);
  text-transform: uppercase;
  font-weight: 600;
  letter-spacing: 0.02em;
  font-size: clamp(4rem, 15vw, 11rem);
  line-height: 0.8;
  color: rgb(245 241 231 / 0.055);
  pointer-events: none;
  user-select: none;
}

.auth__mark {
  display: block;
  width: clamp(9rem, 15vw, 13rem);
  height: auto;
}

.auth__pitch { max-width: 26ch; }
.auth__coords {
  font-family: var(--font-mono);
  font-size: 0.7rem;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: rgb(245 241 231 / 0.5);
}

/* What an account is actually for. Three lines, not a feature grid -- the
   claim is small and the page should not oversell it. */
.auth__points {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0.85rem;
}
.auth__points li {
  display: grid;
  grid-template-columns: 1.6rem 1fr;
  align-items: start;
  gap: 0.6rem;
  font-size: 0.92rem;
  line-height: 1.5;
  color: rgb(245 241 231 / 0.78);
}
.auth__points .mono {
  font-size: 0.7rem;
  letter-spacing: 0.1em;
  color: var(--khaki);
  padding-top: 0.15rem;
}

/* The form column. */
.auth__main {
  display: flex;
  align-items: center;
  padding: clamp(2.25rem, 6vw, 4.5rem) var(--gutter);
}
.auth__form {
  width: 100%;
  max-width: 30rem;
  margin-inline: auto;
}
.auth__head { margin-bottom: clamp(1.75rem, 4vw, 2.5rem); }
.auth__foot { margin-top: clamp(2rem, 4vw, 2.75rem); }

@media (min-width: 940px) {
  .auth { grid-template-columns: minmax(0, 0.9fr) minmax(0, 1.1fr); }

  /* Pinned to 0 rather than to the header height. Chromium applies a sticky
     top offset to this block even at rest, which pushed it a full header down
     its own column and left a bare strip above it. Pinning at 0 removes the
     offset; the header clearance it was buying comes from padding inside the
     block instead, where it cannot move anything. */
  .auth__inner {
    position: sticky;
    top: 0;
    min-height: 100svh;
    padding-top: calc(var(--header-h) + clamp(1.75rem, 4vw, 3.25rem));
  }
  .auth__form { margin-inline: 0 auto; max-width: 32rem; }
}

/* The business name only belongs to a business address. Asking a customer for
   their company on their home address -- with "(business addresses only)"
   beside it, so they have to work out that it does not apply to them -- is a
   question that should not have been on screen at all. */
[data-checkout-form]:has(input[name="address_type"][value="residential"]:checked) [data-business-field] {
  display: none;
}

/* ------------------------------------------------ 18c1 payment arrival -- */

.pay-arrival { position: relative; overflow: hidden; }
.pay-arrival .dune {
  z-index: 1;
  inset: auto 0 0 0;
  height: 58%;
  opacity: 0.55;
}
.pay-arrival__inner { position: relative; z-index: 2; }

.pay-arrival__ref { margin-top: 0.5rem; }
.pay-arrival__code {
  font-size: clamp(1.6rem, 5vw, 2.6rem);
  letter-spacing: 0.14em;
  line-height: 1.2;
  margin-top: 0.35rem;
}

/* ------------------------------------------------- 18c3 order cards -- */

/*
 * An order in the account list.
 *
 * These were thin baseline-aligned rows carrying a reference, a date and a
 * total -- everything a receipt already told the customer, and nothing about
 * the only thing they came back to find out. Each order is a card now: what was
 * bought, on the left, and how far it has travelled, on the right.
 */
.order-cards { list-style: none; margin: 0; padding: 0; display: grid; gap: 1.25rem; }

.order-card {
  border: 1px solid var(--hairline-strong);
  background: var(--cream);
  transition: border-color var(--speed) var(--ease), box-shadow var(--speed) var(--ease);
}
.order-card:hover { border-color: var(--khaki); box-shadow: 0 18px 40px -32px rgb(37 40 32 / 0.5); }

.order-card__head {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem 1.5rem;
  align-items: flex-start;
  justify-content: space-between;
  padding: 1.1rem clamp(1rem, 2.5vw, 1.6rem);
  border-bottom: 1px solid var(--hairline);
}
.order-card__ref {
  font-size: 1.05rem;
  letter-spacing: 0.1em;
  color: var(--ink);
  text-decoration: none;
}
.order-card__ref:hover { color: var(--olive); }
.order-card__money { display: flex; align-items: center; gap: 0.9rem; margin-left: auto; }

.order-card__body {
  display: grid;
  gap: clamp(1.25rem, 3vw, 2.5rem);
  padding: clamp(1.1rem, 2.5vw, 1.6rem);
}
@media (min-width: 40rem) {
  /* The timeline takes the narrower column: it is a glance, not a read. */
  .order-card__body { grid-template-columns: minmax(0, 1.25fr) minmax(0, 1fr); }
}

.order-card__lines { display: grid; gap: 0.9rem; align-content: start; }

.order-line { display: grid; grid-template-columns: 64px 1fr; gap: 0.9rem; align-items: center; }
.order-line__img {
  width: 64px;
  height: 64px;
  object-fit: cover;
  border: 1px solid var(--hairline);
  background: var(--ground);
}
.order-line__img--none {
  display: block;
  background: linear-gradient(180deg, var(--dune-far) 0%, var(--dune-mid) 62%, var(--dune-near) 100%);
}
.order-line__name { font-size: 0.95rem; line-height: 1.35; }

.order-card__to {
  display: grid;
  gap: 0.3rem;
  padding-top: 0.9rem;
  border-top: 1px solid var(--hairline);
  font-size: 0.9rem;
}
.order-card__open { justify-self: start; margin-top: 0.35rem; }

.order-card__track { display: grid; gap: 0.6rem; align-content: start; justify-items: start; }

/* On the overview there is only the timeline in the body, so it takes the whole
   width instead of sitting in a column with a rule down its left. */
.order-card__body--single { grid-template-columns: 1fr !important; }
.order-card__body--single .order-card__track { border-left: 0; padding-left: 0; }

/* The orders behind the newest one. History, so: quieter than a card, but still
   a row with a name for where it is. */
.order-mini { list-style: none; margin: 1rem 0 0; padding: 0; border-top: 1px solid var(--hairline); }
.order-mini li { border-bottom: 1px solid var(--hairline); }
.order-mini a {
  display: flex;
  flex-wrap: wrap;
  gap: 0.75rem 1.5rem;
  align-items: center;
  justify-content: space-between;
  padding-block: 1rem;
  min-height: 44px;
  text-decoration: none;
  color: inherit;
}
.order-mini a:hover { color: var(--olive); }
@media (min-width: 40rem) {
  .order-card__track {
    border-left: 1px solid var(--hairline);
    padding-left: clamp(1.25rem, 3vw, 2rem);
  }
}

/* -------------------------------------------------- 18c2 account tabs -- */

/*
 * The account sub-navigation. It was four text links with inline styles and a
 * hairline above and below -- functional, and it read as a placeholder someone
 * meant to come back to. These are tabs: the current one is marked by a rule
 * sitting on the shared baseline, which is the one convention every person
 * already knows.
 */
.acct-tabs {
  display: flex;
  align-items: stretch;
  gap: 1rem;
  border-bottom: 1px solid var(--hairline);
  margin-bottom: clamp(2rem, 5vw, 3rem);
}

/* The tabs scroll sideways on a narrow screen rather than wrapping into a
   second row that would break the baseline the underline sits on. */
.acct-tabs__scroll {
  display: flex;
  gap: 0.25rem;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scrollbar-width: none;
  margin-bottom: -1px;
}
.acct-tabs__scroll::-webkit-scrollbar { display: none; }

.acct-tabs__tab {
  position: relative;
  display: inline-flex;
  align-items: center;
  white-space: nowrap;
  min-height: 44px;
  padding: 0.65rem 0.9rem;
  border-bottom: 2px solid transparent;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-faint);
  text-decoration: none;
  transition: color var(--speed) var(--ease), border-color var(--speed) var(--ease);
}
.acct-tabs__tab:hover { color: var(--ink); border-bottom-color: var(--hairline-strong); }
.acct-tabs__tab.is-current { color: var(--ink); border-bottom-color: var(--olive); }

/* Sign out is a form, not a tab -- it does something rather than going
   somewhere -- so it sits apart and does not take the underline. */
.acct-tabs__out { margin-left: auto; display: flex; align-items: center; }
.acct-tabs__out button {
  min-height: 44px;
  padding: 0.65rem 0.25rem;
  background: none;
  border: 0;
  cursor: pointer;
  font-family: var(--font-mono);
  font-size: 0.72rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--ink-faint);
  transition: color var(--speed) var(--ease);
}
.acct-tabs__out button:hover { color: var(--ink); }

@media (max-width: 46rem) {
  .acct-tabs { gap: 0.5rem; }
  .acct-tabs__out button { padding-inline: 0.5rem 0; }
}

/* ------------------------------------------------- 18d the order track -- */

/*
 * Where an order has got to. Sand fills in behind it as it travels: done stages
 * are solid, the current one is ringed, the rest are hairlines waiting.
 */
.track { --track-line: var(--hairline-strong); }

.track__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  gap: 0;
}

.track__step {
  position: relative;
  display: grid;
  grid-template-columns: 1.75rem 1fr;
  gap: 0 0.9rem;
  padding-block: 0.7rem;
}
/* The line joining the dots, drawn behind them and stopped short of the last. */
.track__step::before {
  content: "";
  position: absolute;
  left: calc(0.875rem - 1px);
  top: 1.5375rem;              /* this dot's centre */
  bottom: -1.5375rem;          /* the next dot's centre, one row down */
  width: 2px;
  background: var(--track-line);
}
.track__step:last-child::before { display: none; }

.track__dot {
  position: relative;
  z-index: 1;
  width: 0.875rem;
  height: 0.875rem;
  margin-top: 0.4rem;
  margin-inline: auto;
  border-radius: 50%;
  border: 2px solid var(--track-line);
  background: var(--ground);
}

.track__body { display: grid; gap: 0.25rem; }
.track__label { font-family: var(--font-mono); font-size: 0.78rem; letter-spacing: 0.1em; text-transform: uppercase; }
.track__note  { font-size: 0.9rem; color: var(--ink-soft); line-height: 1.55; max-width: 46ch; }

/* Travelled. */
.track__step--done .track__dot { background: var(--olive); border-color: var(--olive); }
.track__step--done::before     { background: var(--olive); }
.track__step--done .track__label { color: var(--ink-soft); }

/* Here now -- the one thing on this page the customer came to read. */
.track__step--current .track__dot {
  background: var(--olive);
  border-color: var(--olive);
  box-shadow: 0 0 0 4px rgb(74 84 51 / 0.18);
}
.track__step--current .track__label { color: var(--ink); }

/* Still to come. */
.track__step--todo .track__label { color: var(--ink-faint); }

/* An order that stepped off the track. Nothing is "current", and the line
   ahead should not look like it is still moving. */
.track--stopped { --track-line: var(--hairline); }
.track__stopped-note {
  font-size: 0.9rem;
  color: var(--ink-soft);
  margin-bottom: 1rem;
  padding: 0.75rem 1rem;
  border-left: 3px solid var(--clay);
  background: var(--cream-deep);
}

/* Tightened for a card in a list: smaller dots, less air, and the labels drop
   to the size of a caption. Still the whole journey, so a customer can see what
   is behind and what is ahead without opening the order. */
.track--lean .track__step { grid-template-columns: 1.25rem 1fr; gap: 0 0.6rem; padding-block: 0.32rem; }
.track--lean .track__dot   { width: 0.6rem; height: 0.6rem; margin-top: 0.3rem; border-width: 2px; }
.track--lean .track__step::before { left: calc(0.625rem - 1px); top: 0.92rem; bottom: -0.92rem; }
.track--lean .track__label { font-size: 0.66rem; letter-spacing: 0.12em; }
.track--lean .track__note  { font-size: 0.8rem; max-width: 34ch; }
.track--lean .track__step--current .track__dot { box-shadow: 0 0 0 3px rgb(74 84 51 / 0.16); }

/* The list shape: a bar and a word. */
.track--compact { display: grid; gap: 0.4rem; min-width: 9rem; }
.track__bar {
  height: 4px;
  background: var(--hairline-strong);
  overflow: hidden;
}
.track__fill {
  display: block;
  height: 100%;
  background: var(--olive);
  transition: width var(--speed) var(--ease);
}
.track--compact .track__now { color: var(--ink-soft); }
.track--stopped .track__fill { background: var(--clay); }

/* ------------------------------------------------- 18c small screens -- */

/*
 * A floor under the smallest type on a phone.
 *
 * The tracked uppercase labels are set in mono at 0.625rem, which is 10px. On a
 * desktop at arm's length that reads as a field-log annotation; on a phone it
 * is just small, and .field__label at 10px is a form label -- the one piece of
 * micro-copy a customer has to read to fill anything in. iOS guidance puts the
 * floor at about 11pt, and these sat under it.
 *
 * Only the 10px tier moves. The 11px labels are caps and tracked, so they carry
 * at that size, and lifting them too would start pushing nav and button widths
 * around for no readability gain.
 */
@media (max-width: 46rem) {
  .field__label { font-size: 0.75rem; }

  .label-sm,
  .pending,
  .filters__title,
  .announce__long,
  .announce__short,
  .side-link,
  th { font-size: 0.72rem; }
}

/* -------------------------------------------------------- 19 utilities -- */

.u-relative { position: relative; }
.u-center { text-align: center; }
.u-right { text-align: right; }
.u-mt-auto { margin-top: auto; }
.u-full { width: 100%; }
.u-nowrap { white-space: nowrap; }
.u-hide { display: none !important; }
@media (max-width: 719px)  { .u-hide-sm { display: none !important; } }
@media (min-width: 1000px) { .u-hide-lg { display: none !important; } }

/* Fallback controls — the submit buttons that filter and sort forms need only
   when there is no JavaScript to submit them on change. */
.u-no-js-only { display: none; }
.no-js .u-no-js-only { display: inline-flex; }

.aspect-square { aspect-ratio: 1; }
.img-contain { width: 100%; height: 100%; object-fit: contain; }
.img-cover { width: 100%; height: 100%; object-fit: cover; }

/* Staging marker. Disappears the moment preview mode is switched off. */
.preview-marker {
  position: fixed;
  right: 1rem;
  /* Clear of the mobile sticky add-to-cart bar. */
  bottom: calc(1rem + env(safe-area-inset-bottom));
  z-index: var(--z-marker);
  max-width: 19rem;
  display: flex;
  flex-direction: column;
  align-items: flex-end;
  gap: 0.5rem;
}
.preview-marker__body {
  border: 1px solid var(--hairline-strong);
  background: var(--cream);
  padding: 1rem;
  font-size: 0.78rem;
  line-height: 1.5;
  color: var(--ink-soft);
  box-shadow: 0 20px 50px -30px rgb(37 40 32 / 0.8);
}
.preview-marker__toggle {
  display: inline-flex;
  align-items: center;
  gap: 0.5rem;
  border: 1px solid var(--hairline-strong);
  background: rgb(245 241 231 / 0.95);
  backdrop-filter: blur(4px);
  padding: 0.45rem 0.7rem;
  font-family: var(--font-mono);
  font-size: 0.625rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
}
.preview-marker__toggle::before {
  content: "";
  width: 6px; height: 6px;
  border-radius: 50%;
  background: var(--khaki-deep);
}

/* Get out of the way while a drawer, the menu or search is open. */
body.is-locked .preview-marker {
  opacity: 0;
  pointer-events: none;
}

@media (max-width: 899px) {
  /* Sit above the sticky buy bar rather than on top of it. */
  .preview-marker { bottom: calc(5.5rem + env(safe-area-inset-bottom)); }
}

/* --------------------------------------------------- 20 motion & print -- */

.reveal {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity 0.7s var(--ease), transform 0.7s var(--ease);
}
.reveal.is-shown { opacity: 1; transform: none; }
.no-js .reveal { opacity: 1; transform: none; }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .reveal { opacity: 1; transform: none; transition: none; }
  *, *::before, *::after {
    animation-duration: 0.01ms !important;
    transition-duration: 0.01ms !important;
  }
}

@media print {
  .no-print, .site-header, .site-footer, .announce, .drawer, .preview-marker { display: none !important; }
  body { background: #fff; }
  body::after { display: none; }
  .section { padding-block: 1rem; }
}
