/* ==========================================================================
   BASE — reset, element defaults, typography classes, layout primitives.
   Depends on tokens.css.
   ========================================================================== */

/* --- Reset ---------------------------------------------------------------- */

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

* { margin: 0; }

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  font-family: var(--font-sans-serif);
  font-size: var(--text-medium-size);
  line-height: var(--text-medium-lh);
  color: var(--text-primary);
  background-color: var(--surface);
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

/* <picture> is a source-picker, not a box. The WebP layer wraps every
   attachment in one, which put an unsized element between a media frame and the
   image meant to fill it: `height: 100%` then resolved against a box that had
   already collapsed to the image's own proportions, so object-fit had nothing
   left to crop and the frame showed whatever happened to fall inside it. It was
   patched once for .swatch and again would have been for the hero; taking the
   wrapper out of the layout fixes both and everywhere else it was quietly
   doing the same thing. */
picture { display: contents; }

/* WooCommerce prints a reduced price inside <ins>, which the UA sheet
   underlines — on a black button the number came out looking like a link.
   The site never uses <ins> for its dictionary meaning outside prose, so the
   underline is cleared here rather than in each place a price can appear; it
   had already been cleared once inside .sklad-price, and the button was the
   second place to need it. Editor prose keeps the UA meaning below. */
ins { text-decoration: none; }

.richtext ins { text-decoration: underline; }

img,
video,
svg {
  display: block;
  max-width: 100%;
}

/* Deterrence against casual saving of the photography: the long-press
   callout on iOS, drag-to-desktop, and the sweep-select that grabs a picture
   along with the text around it. The right-click menu is the JS half — see
   the image guard in main.js. Every image on the site is product or set
   photography or decoration, none of it selectable content, so the element
   is the scope. This keeps an honest visitor honest and no more: the files
   are one View Source away, and that is fine. */
img {
  -webkit-touch-callout: none;
  -webkit-user-drag: none;
  user-select: none;
}

input,
button,
textarea,
select {
  font: inherit;
  color: inherit;
}

button {
  /* iOS Safari still applies its own control styling through `appearance`
     even once background and border are cleared. Neutralising it here means
     .sklad-button and .link render the same whether they are a <button> or
     an <a> — the element is chosen by behaviour, never by looks. */
  appearance: none;
  background: none;
  border: none;
  padding: 0;
  cursor: pointer;
}

/* Fieldset carries a border, padding and a min-width of `auto` by default —
   the last of which stops it shrinking inside a flex or grid parent. Every
   grouped form control (facets, checkout) needs all three gone. */
fieldset {
  border: 0;
  padding: 0;
  margin: 0;
  min-width: min-content;
}

legend { padding: 0; }

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

ul, ol {
  list-style: none;
  padding: 0;
}

h1, h2, h3, h4, h5, h6 {
  font-size: inherit;
  font-weight: inherit;
}

/* Visible focus for keyboard users only — the design is minimal, so the
   focus ring must be unmissable when it does appear. */
:focus-visible {
  outline: 2px solid var(--ui-950);
  outline-offset: 2px;
}

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

/* --- Typography --------------------------------------------------------- */

/* Display type is set in sentence case, exactly as written in the markup.
   The Atacama cuts are already dramatic at these sizes; uppercasing them
   destroys the ascender/descender rhythm they are drawn for. Uppercase in
   this system belongs only to small DM Sans UI labels — .text-eyebrow,
   .link--uppercase, buttons, and .badge. */
.title-display,
.title-h1,
.title-h2,
.title-h3,
.title-h4,
.title-h5,
.title-h6 {
  /* Tracking belongs to the face. Sklad is high-contrast with narrow
     sidebearings and sets loose at the -0.01em this carried under the face before it,
     so the scale closes up. -0.06em was tried and read as a decision rather
     than as setting; -0.045em is where three passes on the page landed. The
     two largest go a step further below, as they always have. */
  letter-spacing: var(--tracking-display);
  font-weight: var(--font-display-weight);
}

/* The two Atacama cuts are not interchangeable, and which one you get is a
   property of the level, not a choice at the call site: the high-contrast cut
   carries the large editorial sizes, the plain condensed cut takes over from
   h3 down where the thin strokes of the contrast cut would break up. */
/* Sklad ships a contextual alternate (calt) that collapses "(4)" into a
   circled numeral ④. It is on by default, so any parenthesised digit set in
   the serif silently becomes a glyph nobody asked for — "Cart (4)" in the
   drawer title was rendering 5.7px narrower than its own characters.
   Contextual substitution is switched off for the face; common ligatures
   (fi, fl) are deliberately left alone. */
.title-display,
.title-h1,
.title-h2,
.title-h3,
.title-h4,
.title-h5,
.title-h6 { font-variant-ligatures: no-contextual; }

.title-display,
.title-h1,
.title-h2 { font-family: var(--font-serif-contrast); }

.title-h3,
.title-h4,
.title-h5,
.title-h6 { font-family: var(--font-serif-condensed); }

/* The largest two sizes tighten further once the display scale steps up. */
/* One value at both widths now: the earlier pair stepped from -0.02 to -0.03
   because the sizes step, and a ratio-based tracking does not need that. */
.title-display,
.title-h1 { letter-spacing: var(--tracking-display-large); }

.title-display { font-size: var(--title-display-size); line-height: var(--title-display-lh); }
.title-h1      { font-size: var(--title-h1-size);      line-height: var(--title-h1-lh); }
.title-h2      { font-size: var(--title-h2-size);      line-height: var(--title-h2-lh); }
.title-h3      { font-size: var(--title-h3-size);      line-height: var(--title-h3-lh); }
.title-h4      { font-size: var(--title-h4-size);      line-height: var(--title-h4-lh); }
.title-h5      { font-size: var(--title-h5-size);      line-height: var(--title-h5-lh); }
.title-h6      { font-size: var(--title-h6-size);      line-height: var(--title-h6-lh); }

.text-large        { font-size: var(--text-large-size);     line-height: var(--text-large-lh); }
.text-medium       { font-size: var(--text-medium-size);    line-height: var(--text-medium-lh); }
.text-paragraph    { font-size: var(--text-paragraph-size); line-height: var(--text-paragraph-lh); }
.text-small        { font-size: var(--text-small-size);     line-height: var(--text-small-lh); }
.text-xsmall       { font-size: var(--text-xsmall-size);    line-height: var(--text-xsmall-lh); }
/* The two `ui` sizes are uppercase letter-spaced labels, not plain text —
   that casing is part of the class, which is why they are separate from
   .text-large / .text-xsmall at the same sizes. */
.text-ui-large {
  font-size: var(--text-ui-large-size);
  line-height: var(--text-ui-large-lh);
  text-transform: uppercase;
  letter-spacing: 0.03em;
}

.text-ui-small {
  font-size: var(--text-ui-small-size);
  line-height: var(--text-ui-small-lh);
  text-transform: uppercase;
  letter-spacing: 0.07em;
}

/* Eyebrow / kicker — small uppercase label above a heading. */
.text-eyebrow {
  font-size: var(--text-xsmall-size);
  line-height: var(--text-xsmall-lh);
  text-transform: uppercase;
  letter-spacing: var(--tracking-wide);
  color: var(--text-secondary);
}

.text-muted     { color: var(--text-secondary); }
.text-on-dark   { color: var(--text-on-dark); }
.text-center    { text-align: center; }
.text-balance   { text-wrap: balance; }

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

/* --- Layout primitives ---------------------------------------------------- */

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

.container--flush { padding-inline: 0; }

/* A .grid that runs to the page edge — its children supply their own inset. */
.grid--full { --padding-inline: 0; }

/* --- Page rhythm ----------------------------------------------------------
   Sections carry NO vertical padding of their own. All the vertical spacing
   on a page lives in the gaps between sections, which means the rhythm is
   readable in one place instead of being smeared across every component,
   and two adjacent sections can never double up their padding.

   One step — 72px — with named exceptions.
   ------------------------------------------------------------------------- */

.flow > * + * { margin-block-start: var(--flow-gap, var(--space-xxxlarge)); }

/* A screen-reader-only element is out of flow and must not open a gap. It does
   under the rule above, because `* + *` counts it as a sibling: adding a
   visually-hidden <h1> to the homepage pushed the hero down by a full 72px and
   out from under the overlay header, which read as the transparent header
   having been lost. The whole point of .visually-hidden is that it costs no
   layout. */
.flow > .visually-hidden + * { margin-block-start: 0; }

/* Exceptions are set on the element that owns the gap ABOVE it. */
.flow-gap-none  { --flow-gap: 0; }
.flow-gap-small { --flow-gap: var(--space-medium); }
.flow-gap-large { --flow-gap: var(--space-xxxlarge); }

/* Retained for pages that genuinely want padded sections (the styleguide). */
.section { padding-block: var(--section-spacing); }
.section--tight { padding-block: var(--space-xlarge); }
.section--flush-top { padding-block-start: 0; }

/* The 12/24 column grid. Children opt into spans with --span.
   Row gap is 0 — vertical rhythm is the page's job, not the grid's. */
.grid {
  display: grid;
  /* minmax(0, 1fr), not 1fr: a plain fr track has an `auto` minimum, so any
     item whose content will not shrink drags its track wider — and with a
     24-track grid the surplus multiplies. The story page was 1880px of tracks
     inside a 1440px window before this, which is why its hero drew at 1440
     rather than at the column it was given. Nothing can be narrower than its
     track now; overflow inside an item is the item's own to solve. */
  grid-template-columns: repeat(var(--grid-columns), minmax(0, 1fr));
  grid-auto-flow: row;
  grid-column: 1 / -1;
  gap: 0 var(--gutters);
  width: 100%;
  max-width: var(--max-width-container);
  margin-inline: auto;
  padding-inline: var(--padding-inline, 0);
}

.grid__item { grid-column: span var(--span, var(--grid-columns)); }

/* Simple auto-fitting product grid — used by listings. */
.stack > * + * { margin-block-start: var(--stack-space, var(--space-medium)); }

.cluster {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: var(--cluster-space, var(--space-small));
}

/* --- Utilities ------------------------------------------------------------ */

/* The UA sheet's [hidden] rule is `display: none` at zero specificity, so any
   component that sets `display` on itself silently outranks it — .wishlist-empty
   (display: flex) rendered its "Nothing saved yet" panel underneath four saved
   items, because JS setting .hidden = true had no effect. `hidden` means "not
   relevant", so nothing should be able to out-specify it. */
[hidden] { display: none !important; }

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

.is-hidden-mobile { display: none; }
@media (min-width: 1024px) {
  .is-hidden-mobile { display: revert; }
  .is-hidden-desktop { display: none; }
}

.surface-muted     { background-color: var(--surface-muted); }
.surface-highlight { background-color: var(--surface-highlight); }
.surface-lime      { background-color: var(--surface-lime); }
.surface-lavender  { background-color: var(--surface-lavender); }
.surface-peach     { background-color: var(--surface-peach); }
.surface-dark {
  background-color: var(--ui-950);
  color: var(--text-on-dark);
}

/* Media wrappers — keep images from causing layout shift. */
.media {
  position: relative;
  overflow: hidden;
  background-color: var(--ui-50);
}

/* Through the wrapper as well as without it. `display: contents` above takes
   <picture> out of the layout, but it does not take it out of the DOM, and `>`
   matches on the tree — so under the WebP layer this rule stopped matching the
   only images it exists for. The frame then sized nothing: the image rendered
   at its natural width and .media clipped it, which reads as a photo zoomed
   two or three times and anchored to its top-left corner. A JPEG got the
   wrapper and a WebP did not, so on a row of three cards the first two broke
   and the third looked fine. */
.media > img,
.media > picture > img,
.media > video {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.media--square    { aspect-ratio: 1; }
.media--portrait  { aspect-ratio: 3 / 4; }
.media--landscape { aspect-ratio: 4 / 3; }
.media--wide      { aspect-ratio: 16 / 9; }

/* Scroll reveal — JS adds .is-revealed as the element enters the viewport. */
[data-reveal] {
  opacity: 0;
  transform: translateY(24px);
  transition:
    opacity   var(--duration-x-slow) var(--ease-out-cubic),
    transform var(--duration-x-slow) var(--ease-out-cubic);
}

[data-reveal].is-revealed {
  opacity: 1;
  transform: none;
}

/* Slow zoom on hover — the signature move for editorial imagery here. */
.media--zoom > img,
.media--zoom > picture > img {
  transition: transform var(--duration-x-slow) var(--ease-out-cubic);
}
.media--zoom:hover > img,
.media--zoom:hover > picture > img,
a:hover .media--zoom > img,
a:hover .media--zoom > picture > img { transform: scale(1.04); }
