/* ==========================================================================
   DRIP VAULT — BASE
   Reset, root typography, layout primitives and scroll-reveal utilities.
   Load order: tokens.css → base.css → components.css → pages.css
   ========================================================================== */

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

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

body {
  margin: 0;
  background: var(--bg);
  color: var(--fg);
  font-family: var(--font-body);
  font-size: var(--t-body);
  line-height: 1.65;
  font-weight: 400;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  overflow-x: hidden;
}

h1, h2, h3, h4, h5, h6, p, figure, blockquote, dl, dd { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
img, picture, video, svg { display: block; max-width: 100%; height: auto; }
button, input, select, textarea { font: inherit; color: inherit; }
button { background: none; border: 0; padding: 0; cursor: pointer; }
a { color: inherit; text-decoration: none; }
table { border-collapse: collapse; width: 100%; }

/* `hidden` must always win. The browser's own [hidden] rule loses to any
   class that sets display (the shop's empty state is display: grid), which
   would leave "Nothing matches" showing under a full grid. */
[hidden] { display: none !important; }

::selection { background: var(--ink); color: var(--bg); }

/* Slim scrollbar in the page's own greys rather than the OS default. */
::-webkit-scrollbar { width: 11px; height: 11px; }
::-webkit-scrollbar-track { background: var(--bg); }
::-webkit-scrollbar-thumb {
  background: var(--scroll-thumb);
  border-radius: var(--r-pill);
  border: 3px solid var(--bg);
}
::-webkit-scrollbar-thumb:hover { background: var(--scroll-thumb-hover); }

:focus-visible {
  outline: 2px solid var(--accent);
  outline-offset: 3px;
  border-radius: var(--r-sm);
}
:focus:not(:focus-visible) { outline: none; }

/* ---- Typography ----------------------------------------------------------
   Display type is heavy, tight and set in Archivo. The `wdth` axis is pushed
   past normal so headlines read wide and confident rather than condensed. */
.display,
h1, h2, h3 {
  font-family: var(--font-display);
  font-weight: var(--weight-display);
  line-height: 1.02;
  letter-spacing: var(--track-tight);
  font-stretch: 110%;
  color: var(--ink);
}

h1 { font-size: var(--t-display); }
h2 { font-size: var(--t-h2); }
h3 {
  font-size: var(--t-h3);
  line-height: 1.25;
  font-weight: 700;
  letter-spacing: -0.015em;
}

/* Chrome-filled display type — the brushed-metal treatment.
   Every element that takes the chrome fill is listed here rather than
   repeating the background-clip incantation in four places. Add `.chrome-text`
   to anything else that should pick it up.

   The @supports fallback matters: without it, a browser that can't clip a
   background to text renders transparent text on a transparent background,
   i.e. an invisible headline. */
.chrome-text,
.hero__title em,
.wordmark span,
.value__num {
  background: var(--chrome);
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  -webkit-text-fill-color: transparent;
}
@supports not ((-webkit-background-clip: text) or (background-clip: text)) {
  .chrome-text,
  .hero__title em,
  .wordmark span,
  .value__num {
    color: var(--chrome-mid);
    -webkit-text-fill-color: currentColor;
  }
}

/* Small-caps eyebrow above section headings. Wide tracking against the tight
   display type is the core typographic contrast of this theme. */
.eyebrow {
  font-family: var(--font-body);
  font-size: var(--t-micro);
  font-weight: 500;
  letter-spacing: var(--track-wide);
  text-transform: uppercase;
  color: var(--muted);
  margin-bottom: var(--sp-4);
  display: flex;
  align-items: center;
  gap: var(--sp-3);
}
/* Live dot — a small "vault is open" signal. */
.eyebrow::before {
  content: '';
  width: 5px;
  height: 5px;
  border-radius: 50%;
  background: var(--accent);
  box-shadow: 0 0 0 3px var(--glow-soft);
  flex: none;
}
.eyebrow--plain::before { display: none; }
.eyebrow--muted { color: var(--fg-muted); }
.eyebrow--muted::before { background: var(--fg-muted); box-shadow: none; }

.lede {
  font-size: clamp(1.0625rem, 1.6vw, 1.3125rem);
  line-height: 1.6;
  color: var(--fg-muted);
  max-width: var(--text-max);
}

.prose { max-width: var(--text-max); color: var(--fg-muted); }
.prose p + p { margin-top: var(--sp-5); }

.link-underline {
  position: relative;
  display: inline-block;
  padding-bottom: 2px;
}
.link-underline::after {
  content: '';
  position: absolute;
  left: 0; bottom: 0;
  width: 100%; height: 1px;
  background: currentColor;
  transform: scaleX(0);
  transform-origin: left;
  transition: transform var(--dur-mid) var(--ease-out);
}
.link-underline:hover::after,
.link-underline:focus-visible::after { transform: scaleX(1); }

/* ---- Layout primitives --------------------------------------------------- */
.container {
  width: 100%;
  max-width: var(--page-max);
  margin-inline: auto;
  padding-inline: var(--gutter);
}
.container--narrow { max-width: 940px; }

.section { padding-block: var(--section-y); position: relative; }
.section--tight { padding-block: calc(var(--section-y) * 0.6); }

/* Lifted section — a warm off-white band with hairlines top and bottom so it
   has defined edges against the white page. */
.section--alt {
  background: var(--surface-1);
  border-block: 1px solid var(--line);
}

/* Dark bands are `.theme-dark` — defined in tokens.css, since a band is a
   token scope rather than a component. */

.section-head {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: var(--sp-5);
  margin-bottom: var(--sp-7);
}
.section-head__text { max-width: 46ch; }

.rule { border: 0; height: 1px; background: var(--rule); margin: 0; }

/* ---- Grid ---------------------------------------------------------------- */
.grid {
  display: grid;
  gap: var(--sp-6) var(--sp-5);
  grid-template-columns: 1fr;
}
/* Product grids are two-up even on a phone. One card per screen turns a
   22-piece collection into a very long scroll, and two-up is what people
   browsing clothes expect. minmax(0, 1fr) lets a long word wrap instead of
   pushing its column wider than half the screen. */
.grid--2up, .grid--3up, .grid--4up { grid-template-columns: repeat(2, minmax(0, 1fr)); }
@media (max-width: 559px)  { .grid--2up, .grid--3up, .grid--4up { gap: var(--sp-5) var(--sp-3); } }
@media (min-width: 900px)  { .grid--3up, .grid--4up { grid-template-columns: repeat(3, minmax(0, 1fr)); } }
@media (min-width: 1200px) { .grid--4up { grid-template-columns: repeat(4, minmax(0, 1fr)); } }

/* ---- Scroll reveal -------------------------------------------------------
   Elements marked [data-reveal] start shifted and transparent; motion.js adds
   .is-revealed via IntersectionObserver, with the stagger written per element
   as --reveal-delay.

   NOTE the `.js` guard: the hidden pre-state only applies once JS has run, so
   if the script fails the content stays visible instead of being stuck at
   opacity 0. Never hide content behind JS that might not run. */
.js [data-reveal] {
  opacity: 0;
  transform: translate3d(0, 30px, 0);
  transition:
    opacity   var(--dur-slow) var(--ease-out) var(--reveal-delay, 0ms),
    transform var(--dur-slow) var(--ease-out) var(--reveal-delay, 0ms);
  will-change: opacity, transform;
}
.js [data-reveal].is-revealed {
  opacity: 1;
  transform: translate3d(0, 0, 0);
}
.js [data-reveal='fade'] { transform: none; }

/* UNVEIL — for images. The frame wipes open from the bottom while the picture
   inside settles from a slight zoom, so it reads as a shutter lifting rather
   than a box fading in.

   Both run as one-shot ANIMATIONS rather than transitions. When they finish
   the clip is gone entirely — a lingering `clip-path: inset(0)` would still
   cut off the hover shadow, and a lingering transform on the <img> would
   override its own hover zoom. Note the observer in motion.js uses threshold
   0 for this reason too: Chrome counts an element's own clip-path when it
   measures visibility, so a fully clipped element reads as 0% visible and
   would never cross a higher threshold — it would stay hidden forever. */
.js [data-reveal='unveil'] { opacity: 1; transform: none; }
.js [data-reveal='unveil']:not(.is-revealed) { clip-path: inset(100% 0 0 0); }
.js [data-reveal='unveil'].is-revealed {
  animation: unveil-wipe var(--dur-reveal) var(--ease-expo) var(--reveal-delay, 0ms) backwards;
}
.js [data-reveal='unveil'].is-revealed img {
  animation: unveil-settle calc(var(--dur-reveal) * 1.4) var(--ease-expo) var(--reveal-delay, 0ms) backwards;
}
@keyframes unveil-wipe   { from { clip-path: inset(100% 0 0 0); } to { clip-path: inset(0 0 0 0); } }
@keyframes unveil-settle { from { transform: scale(1.14); }       to { transform: scale(1); } }

/* MASK — for headings. The text rises out of its own line box, as if from
   behind a ledge. The heading is its own mask: overflow clips the inner span
   (added by motion.js) until it slides into place. */
.js [data-reveal='mask'] { opacity: 1; transform: none; }
.mask-line { display: block; overflow: hidden; padding-block: 0.08em; margin-block: -0.08em; }
.mask-line__inner { display: block; }
.js [data-reveal='mask'] .mask-line__inner {
  transform: translate3d(0, 108%, 0);
  transition: transform var(--dur-reveal) var(--ease-expo)
              calc(var(--reveal-delay, 0ms) + var(--line-delay, 0ms));
}
.js [data-reveal='mask'].is-revealed .mask-line__inner { transform: translate3d(0, 0, 0); }

/* ---- Page transition overlay --------------------------------------------- */
.page-veil {
  position: fixed;
  inset: 0;
  z-index: var(--z-overlay);
  background: var(--bg);
  pointer-events: none;
  opacity: 0;
  visibility: hidden;
  transition: opacity var(--dur-mid) var(--ease), visibility var(--dur-mid);
}
.page-veil.is-active { opacity: 1; visibility: visible; }

/* ---- Helpers ------------------------------------------------------------- */
.visually-hidden {
  position: absolute !important;
  width: 1px; height: 1px;
  padding: 0; margin: -1px;
  overflow: hidden;
  clip: rect(0 0 0 0);
  clip-path: inset(50%);
  white-space: nowrap;
}

.skip-link {
  position: absolute;
  top: -100px;
  left: var(--sp-4);
  z-index: calc(var(--z-overlay) + 1);
  background: var(--ink);
  color: var(--bg);
  padding: var(--sp-3) var(--sp-5);
  border-radius: var(--r-pill);
  font-size: var(--t-small);
  font-weight: 600;
  letter-spacing: var(--track-mid);
  text-transform: uppercase;
  transition: top var(--dur-fast) var(--ease);
}
.skip-link:focus { top: var(--sp-4); }

.text-center { text-align: center; }
.mx-auto { margin-inline: auto; }
.no-scroll { overflow: hidden; }

/* ==========================================================================
   PAGE TRANSITIONS — native, where the browser supports them
   --------------------------------------------------------------------------
   Cross-document View Transitions (Chrome/Edge 126+, Safari 18.2+). The
   browser snapshots the old page and animates to the new one, in BOTH
   directions, and handles the edge cases — back/forward, background tabs,
   slow loads — that made the old JS curtain outgoing-only. Browsers without
   support ignore this block and motion.js falls back to that curtain.

   The header and the WhatsApp button get their own names so they hold still
   while the page content changes underneath them, instead of fading out and
   back in with everything else.
   ========================================================================== */
@view-transition { navigation: auto; }

[data-header] { view-transition-name: site-header; }
.wa-float     { view-transition-name: wa-float; }

::view-transition-old(root) {
  animation: vt-out 260ms var(--ease) both;
}
::view-transition-new(root) {
  animation: vt-in 520ms var(--ease-out) both;
}
@keyframes vt-out { to   { opacity: 0; transform: translate3d(0, -12px, 0); } }
@keyframes vt-in  { from { opacity: 0; transform: translate3d(0, 22px, 0); } }

@media (prefers-reduced-motion: reduce) {
  ::view-transition-group(*),
  ::view-transition-old(*),
  ::view-transition-new(*) { animation: none !important; }
}
