/* Shared across every page: buttons, nav, footer, scroll reveal. */

/* ============ BUTTONS ============ */
.btn{
  display:inline-flex;align-items:center;justify-content:center;gap:8px;
  height:48px;padding:0 30px;border-radius:var(--pill);
  font-family:'Outfit',sans-serif;font-size:14px;font-weight:500;letter-spacing:.06em;text-transform:uppercase;
  transition:transform .18s var(--ease),background .18s var(--ease),color .18s var(--ease),box-shadow .18s var(--ease);
  white-space:nowrap;
}
.btn:hover{transform:translateY(-2px)}
.btn:active{transform:translateY(0)}
.btn--solid{background:var(--graphite);color:#fff}
.btn--solid:hover{background:#000;box-shadow:var(--shadow-2)}
.btn--ghost{background:rgba(255,255,255,.75);color:var(--graphite);backdrop-filter:blur(8px);box-shadow:inset 0 0 0 1px rgba(23,26,32,.16)}
.btn--ghost:hover{background:#fff}
.btn--red{background:var(--red);color:#fff}
.btn--red:hover{background:#c4142f;box-shadow:0 10px 26px rgba(227,25,55,.28)}
.btn--light{background:#fff;color:var(--graphite)}
.btn--light:hover{box-shadow:var(--shadow-2)}
.btn--sm{height:40px;padding:0 22px;font-size:12px}
.btn--block{width:100%}
/* The hover glow drops the focus ring to 2.97:1 against its own halo, so a
   keyboard user with the mouse resting on the primary CTA loses the ring. */
.btn--red:focus-visible{box-shadow:none}

/* Unavailable. `opacity` composites the label toward the page behind it rather
   than fading it against its own background: .btn--red at .4 renders white on
   #F4A3AF, about 1.98:1. Swapping tokens instead gives --silver on --cloud,
   about 5.18:1. 1.4.3 does exempt inactive components, but an aria-disabled
   button stays focusable, so "inactive" is a stretch — and the swap passes
   anyway, which makes the argument moot. */
.btn[disabled],
.btn.is-off,
.btn[aria-disabled="true"]:not(.is-busy){
  opacity:1;background:var(--cloud);color:var(--silver);cursor:not-allowed;
  transform:none!important;box-shadow:none!important}
.btn[disabled]:hover,
.btn.is-off:hover,
.btn[aria-disabled="true"]:not(.is-busy):hover{background:var(--cloud);transform:none}

/* Busy is NOT unavailable. Keep the button's own colours — the user has to be
   able to read the control they just pressed — and show pending with a spinner
   rather than by dimming it. The accessible name is never rewritten. */
.btn.is-busy{cursor:progress}
.btn.is-busy::after{content:"";width:14px;height:14px;margin-left:10px;
  border:2px solid currentColor;border-top-color:transparent;border-radius:var(--pill);
  animation:btnspin .7s linear infinite}
@keyframes btnspin{to{transform:rotate(360deg)}}
@media(prefers-reduced-motion:reduce){
  .btn.is-busy::after{animation:none;border-top-color:currentColor;opacity:.6}
}
/* Colour-only affordances disappear in forced-colors mode. */
@media(forced-colors:active){
  .btn[disabled],
  .btn.is-off,
  .btn[aria-disabled="true"]:not(.is-busy){color:GrayText;border:1px solid GrayText}
  .btn.is-busy::after{border-color:CanvasText;border-top-color:transparent}
}

/* "Added" confirmation as a state class, so the button's accessible name is
   never rewritten. The tick is generated content and therefore decorative. */
.btn.is-added .btn__lbl{opacity:.35}
.btn.is-added::after{content:"\2713";margin-left:6px;font-weight:700}

/* ============ NAV ============ */
.nav{
  position:fixed;inset:0 0 auto 0;z-index:80;
  transition:background .3s var(--ease),box-shadow .3s var(--ease),backdrop-filter .3s var(--ease);
}
.nav.is-stuck{background:rgba(255,255,255,.86);backdrop-filter:blur(18px) saturate(1.4);box-shadow:0 1px 0 var(--line)}
.nav__in{
  max-width:var(--maxw);margin:0 auto;padding:0 var(--pad);
  height:70px;display:flex;align-items:center;justify-content:space-between;gap:20px;
}
.brand{display:flex;align-items:center;gap:10px;font-family:'Outfit',sans-serif;font-weight:600;font-size:19px;letter-spacing:.24em}
.brand__mark{width:30px;height:30px;flex:none}
.nav__links{display:flex;gap:6px}
.nav__links a{
  padding:9px 18px;border-radius:var(--pill);font-size:14px;font-weight:600;
  transition:background .18s var(--ease);
}
.nav__links a:hover{background:rgba(23,26,32,.07)}
.nav__side{display:flex;align-items:center;gap:6px}
.icobtn{
  width:42px;height:42px;border-radius:var(--pill);display:grid;place-items:center;position:relative;
  transition:background .18s var(--ease);
}
.icobtn:hover{background:rgba(23,26,32,.07)}
.cartdot{
  position:absolute;top:4px;right:3px;min-width:19px;height:19px;padding:0 5px;border-radius:var(--pill);
  background:var(--red);color:#fff;font-size:11px;font-weight:700;display:none;place-items:center;
  font-family:'JetBrains Mono',monospace;
}
.cartdot.on{display:grid}
.burger{display:none}

/* ============ FOOTER ============ */
/* The load-bearing half of 2.4.11: at maximum scroll there is no scroll range
   left for scroll-padding to spend, so the last screenful of links can only be
   kept clear by reserving the space. --cc-h is 0 when no banner exists. */
.foot{background:var(--graphite);color:#fff;
  padding:clamp(52px,7vw,86px) 0 calc(34px + var(--cc-h));margin-top:clamp(64px,8vw,110px)}
.foot__grid{display:grid;grid-template-columns:1.6fr repeat(3,1fr);gap:40px}
.foot__brand p{color:rgba(255,255,255,.55);font-size:14.5px;line-height:1.6;max-width:34ch;margin-top:16px}
.foot h3,.foot h4{font-family:'JetBrains Mono',monospace;font-size:11px;letter-spacing:.2em;text-transform:uppercase;color:rgba(255,255,255,.45);margin-bottom:16px;font-weight:400}
.foot ul{list-style:none;display:flex;flex-direction:column;gap:11px}
.foot ul a{color:rgba(255,255,255,.82);font-size:14.5px;transition:color .16s var(--ease)}
.foot ul a:hover{color:#fff}
.foot__bar{
  margin-top:52px;padding-top:24px;border-top:1px solid rgba(255,255,255,.13);
  display:flex;justify-content:space-between;gap:18px;flex-wrap:wrap;
  font-size:12.5px;color:rgba(255,255,255,.45);font-family:'JetBrains Mono',monospace;
}

/* ============ REVEAL ============ */
.rv{opacity:0;transform:translateY(22px);transition:opacity .7s var(--ease),transform .7s var(--ease)}
.rv.in{opacity:1;transform:none}

/* ============ REDUCED MOTION ============
   Canonical copy. home.css and product.css each carry their own from before
   base.css had one; they are identical, so they are harmless, but every NEW
   page stylesheet should rely on this rather than adding a fourth. Three copies
   drift; four is worse. */
@media(prefers-reduced-motion:reduce){
  *{animation:none!important;transition-duration:.01ms!important}
  .rv{opacity:1;transform:none}
  html{scroll-behavior:auto}
}

/* ============ SEARCH ============
   A combobox: the input owns the listbox and tracks the active row with
   aria-activedescendant, so focus never leaves the field and typing keeps
   working while the arrow keys walk the results. */
.srch__scrim{position:fixed;inset:0;background:rgba(15,17,18,.42);backdrop-filter:blur(3px);z-index:110}
.srch__scrim[hidden]{display:none}
.srch{position:fixed;inset:0;z-index:111;display:grid;place-items:start center;padding:12vh 16px 16px;
  pointer-events:none}
.srch[hidden]{display:none}
.srch__panel{pointer-events:auto;width:min(620px,100%);background:var(--paper);
  border-radius:var(--r-lg);box-shadow:var(--shadow-3);overflow:hidden}
.srch__bar{display:flex;align-items:center;gap:12px;padding:16px 18px;border-bottom:1px solid var(--line)}
.srch__bar input{flex:1;min-height:32px;border:0;background:none;font-size:17px}
.srch__bar input:focus{outline:none}
.srch__close{width:36px;height:36px;border-radius:var(--pill);display:grid;place-items:center;flex:none}
.srch__close:hover{background:var(--mist)}
.srch__hint{padding:12px 18px;font-size:13px;color:var(--silver)}
.srch__hint:empty{display:none}
.srch__list{list-style:none;max-height:52vh;overflow-y:auto;overscroll-behavior:contain}
.srch__list:empty{display:none}
.srch__item a{display:block;padding:13px 18px;border-top:1px solid var(--line)}
.srch__item b{font-family:'Outfit',sans-serif;font-size:15.5px;font-weight:600;display:block}
.srch__item span{font-family:'JetBrains Mono',monospace;font-size:11.5px;color:var(--silver)}
/* The active row is not focused, so :hover/:focus cannot style it. */
.srch__item.on a,.srch__item a:hover{background:var(--mist)}
.srch__item.on a{box-shadow:inset 3px 0 0 var(--red)}
@media(forced-colors:active){
  .srch__panel{border:1px solid CanvasText}
  .srch__item.on a{outline:2px solid Highlight;outline-offset:-2px}
}

/* Moved from home.css, where it only ever loaded on two of the sixteen pages
   while search.js called setPageInert on all of them. */
body.overlay-open{overflow:hidden}

/* ============ COOKIE CONSENT ============
   A named region, not a dialog. See consent.js for why it does not trap focus,
   why it is removed rather than hidden, and why Escape does nothing.

   z-index 82: above .nav (80), so the mobile menu — which has no scrim — cannot
   cover buttons that are still reachable by Shift+Tab; below every scrim (cart
   90, offer 100, search 110), so that when setPageInert makes this inert it is
   visibly dimmed rather than looking operable while unreachable. */
.cc{
  position:fixed;left:0;right:0;bottom:0;z-index:82;
  /* Fully opaque, and no backdrop-filter. A translucent surface makes contrast
     uncomputable: the same --silver measures 6.11:1 over the hero and 3.80:1
     over the graphite footer through the same sheet. One passes 1.4.3 and one
     fails, depending on scroll position. */
  background:var(--paper);
  /* --ctrl-line, not --line. The strict reading of 1.4.11 exempts a decorative
     container edge, but --line is 1.02:1 against the end of the hero gradient
     and this surface is 1.00:1 against a white page — the edge is the only
     thing declaring this a separate floating surface, so it is not decorative.
     The shadow is depth alongside it, never instead of it: forced-colors
     removes shadows entirely. */
  border-top:1px solid var(--ctrl-line);
  box-shadow:var(--shadow-3);
  /* A transform transition, not a keyframe animation. The global
     prefers-reduced-motion block collapses animation to none, so a keyframe
     that never runs against a translateY(100%) resting state would leave the
     consent banner permanently off screen. A transition just becomes instant. */
  transform:translateY(100%);
  transition:transform .32s var(--ease);
}
.cc.is-in{transform:none}

.cc__in{
  max-width:var(--maxw);margin:0 auto;padding:18px var(--pad);
  display:flex;align-items:center;justify-content:space-between;gap:22px 32px;flex-wrap:wrap;
  /* No fixed height: expanded text spacing has to grow the box, not be clipped
     by it (1.4.12). The cap keeps a bottom bar from eating a 320px viewport at
     400% zoom (1.4.10); consent.js adds tabindex only while it really scrolls. */
  max-height:50dvh;overflow-y:auto;
}
.cc__p{font-size:14.5px;line-height:1.6;max-width:62ch;flex:1 1 340px}
/* Underlined, because colour alone cannot mark a link (1.4.1). Inline in the
   sentence on purpose — beside the buttons its 24px target circle would
   intersect theirs and the 2.5.8 inline exception would stop applying. */
.cc__link{text-decoration:underline;text-underline-offset:3px}

/* Both buttons carry .btn--solid: same fill, same size, same weight. Reject is
   first here and first in the DOM, and must never be reordered with `order:` or
   row-reverse — that desyncs the tab order from the reading order. */
.cc__acts{display:flex;gap:12px;flex:0 0 auto;flex-wrap:wrap}
.cc__acts .btn{flex:1 1 auto;min-width:170px}

@media(max-width:560px){
  .cc__in{padding:16px var(--pad) 18px;gap:16px}
  .cc__acts{width:100%}
}

/* Guard only. The global ring is 3px --red at 3px offset, which sits entirely
   on --paper here — 4.71:1 against both the ring and the graphite fill. If a
   red fill ever lands in this bar the ring would touch it, so swap the ring
   rather than suppress it. */
.cc .btn--red:focus-visible{outline-color:var(--graphite)}

@media(forced-colors:active){
  .cc{background:Canvas;border-top:1px solid CanvasText;box-shadow:none}
}
