/* ==========================================================================
   Glenda — shell
   Shared, token-driven layout + component primitives.

   THE CONTRACT
   ------------
   Nothing in this file hard-codes a brand decision. Every value resolves to a
   custom property that a direction's tokens.css supplies. Swap tokens.css and
   the entire system re-skins with no markup or structural change — that is
   what makes a chosen direction buildable into a finished site.

   A direction's tokens.css MUST define all of the following:

   COLOUR    --c-canvas --c-surface --c-ink --c-ink-soft --c-line
             --c-accent --c-accent-2 --c-accent-3 --c-on-accent --c-focus
   TYPE      --f-display --f-body --f-ui --f-mono
             --fs-hero --fs-d1 --fs-d2 --fs-h1 --fs-h2 --fs-h3
             --fs-body --fs-small --fs-micro
             --lh-display --lh-body --ls-display --ls-ui
             --fw-display --fw-body --fw-ui
   SPACE     --u --gutter --container --container-narrow --sec-y
   SHAPE     --r-sm --r-md --r-lg --r-pill --border-w
   MOTION    --d-fast --d-base --d-slow --d-xslow --e-out --e-signature
   ========================================================================== */

/* --------------------------------------------------------------------------
   Derived spacing scale — directions set --u, the ramp follows.
   -------------------------------------------------------------------------- */
:root {
  --s1: calc(var(--u) * 0.5);
  --s2: var(--u);
  --s3: calc(var(--u) * 1.5);
  --s4: calc(var(--u) * 2);
  --s5: calc(var(--u) * 3);
  --s6: calc(var(--u) * 4);
  --s7: calc(var(--u) * 6);
  --s8: calc(var(--u) * 8);
  --s9: calc(var(--u) * 12);
  --s10: calc(var(--u) * 16);
}

body {
  background: var(--c-canvas);
  color: var(--c-ink);
  font-family: var(--f-body);
  font-size: var(--fs-body);
  font-weight: var(--fw-body);
  line-height: var(--lh-body);
}

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

.wrap--narrow { max-width: var(--container-narrow); }
.wrap--full { max-width: none; padding-inline: var(--gutter); }
.wrap--bleed { max-width: none; padding-inline: 0; }

.section { padding-block: var(--sec-y); position: relative; }

.stack > * + * { margin-top: var(--stack-gap, var(--s4)); }

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

.grid {
  display: grid;
  gap: var(--grid-gap, var(--gutter));
  grid-template-columns: repeat(var(--cols, 12), minmax(0, 1fr));
}

.grid--2 { --cols: 2; }
.grid--3 { --cols: 3; }
.grid--4 { --cols: 4; }

@media (max-width: 900px) {
  .grid--3, .grid--4 { --cols: 2; }
}
@media (max-width: 560px) {
  .grid--2, .grid--3, .grid--4 { --cols: 1; }
}

/* Editorial grids are authored for desktop with an inline --cols and inline
   `grid-column: span n`. Below the tablet breakpoint every one of them must
   become a single column — otherwise the column gaps alone exceed a phone's
   width and the whole page scrolls sideways. Inline styles are what this has
   to beat, which is why the override carries weight. Opt out with .grid--keep. */
@media (max-width: 760px) {
  .grid:not(.grid--keep) {
    grid-template-columns: minmax(0, 1fr) !important;
    gap: var(--grid-gap-sm, var(--s5)) !important;
  }
  .grid:not(.grid--keep) > * { grid-column: auto !important; }
}

/* --------------------------------------------------------------------------
   Type primitives
   -------------------------------------------------------------------------- */
.display,
.d1, .d2, .h1, .h2, .h3 {
  font-family: var(--f-display);
  font-weight: var(--fw-display);
  line-height: var(--lh-display);
  letter-spacing: var(--ls-display);
}

.display { font-size: var(--fs-hero); }
.d1 { font-size: var(--fs-d1); }
.d2 { font-size: var(--fs-d2); }
.h1 { font-size: var(--fs-h1); }
.h2 { font-size: var(--fs-h2); }
.h3 { font-size: var(--fs-h3); }

.lede {
  font-size: var(--fs-h3);
  line-height: 1.4;
  color: var(--c-ink-soft);
  max-width: 34ch;
}

.eyebrow {
  font-family: var(--f-ui);
  font-size: var(--fs-micro);
  font-weight: var(--fw-ui);
  letter-spacing: var(--ls-ui);
  text-transform: uppercase;
  color: var(--c-ink-soft);
  display: inline-flex;
  align-items: center;
  gap: 0.7em;
}

.small { font-size: var(--fs-small); }
.micro {
  font-family: var(--f-ui);
  font-size: var(--fs-micro);
  letter-spacing: var(--ls-ui);
  text-transform: uppercase;
}
.soft { color: var(--c-ink-soft); }
.measure { max-width: 62ch; }
.measure--tight { max-width: 46ch; }

/* --------------------------------------------------------------------------
   Rules — a hairline that can be drawn open by GSAP (scaleX 0 -> 1)
   -------------------------------------------------------------------------- */
.rule {
  height: var(--border-w);
  background: var(--c-line);
  border: 0;
  transform-origin: left center;
  width: 100%;
}

/* --------------------------------------------------------------------------
   Buttons
   -------------------------------------------------------------------------- */
.btn {
  --btn-bg: var(--c-accent);
  --btn-fg: var(--c-on-accent);
  --btn-bd: var(--c-accent);
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.75em;
  padding: var(--btn-py, 1.05em) var(--btn-px, 2em);
  background: var(--btn-bg);
  color: var(--btn-fg);
  border: var(--border-w) solid var(--btn-bd);
  border-radius: var(--r-pill);
  font-family: var(--f-ui);
  font-size: var(--fs-micro);
  font-weight: var(--fw-ui);
  letter-spacing: var(--ls-ui);
  text-transform: uppercase;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  transition: background var(--d-base) var(--e-out),
              color var(--d-base) var(--e-out),
              border-color var(--d-base) var(--e-out);
}

.btn:hover {
  --btn-bg: transparent;
  --btn-fg: var(--c-accent);
}

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

.btn--text {
  --btn-bg: transparent;
  --btn-fg: var(--c-ink);
  --btn-bd: transparent;
  padding-inline: 0;
  border-radius: 0;
}
.btn--text::after {
  content: "";
  position: absolute;
  left: 0; bottom: 0.35em;
  width: 100%; height: var(--border-w);
  background: currentColor;
  transform-origin: right center;
  scale: 1 1;
  transition: scale var(--d-base) var(--e-out);
}
.btn--text:hover::after { transform-origin: left center; scale: 0 1; }
.btn--text:hover { --btn-fg: var(--c-accent); }

/* --------------------------------------------------------------------------
   Media frames — every image slot on the site is one of these.
   aspect-ratio is set inline via --ar so the frame reserves exact space
   before any image exists. Generated art drops in with zero reflow.
   -------------------------------------------------------------------------- */
.media {
  position: relative;
  aspect-ratio: var(--ar, 3 / 4);
  overflow: hidden;
  background: var(--c-surface);
  border-radius: var(--media-radius, 0);
  isolation: isolate;
}

.media > img,
.media > .media__ph {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}

.media--reveal > img,
.media--reveal > .media__ph { will-change: transform; }

/* --------------------------------------------------------------------------
   Image-slot placeholders (see assets/js/placeholders.js)
   Styled as a deliberate unshot frame, not a broken image — these get shown
   to a client, so they carry the direction's palette and read as intentional.
   -------------------------------------------------------------------------- */
.media__grain {
  position: absolute;
  inset: 0;
  opacity: var(--ph-grain, 0.5);
  mix-blend-mode: overlay;
  pointer-events: none;
  background-image: url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='140' height='140'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3'/%3E%3C/filter%3E%3Crect width='140' height='140' filter='url(%23n)' opacity='0.42'/%3E%3C/svg%3E");
}

.media__mark {
  position: absolute;
  inset: 0;
  display: grid;
  place-items: center;
  font-family: var(--f-display);
  font-size: clamp(3rem, 9cqi, 7rem);
  line-height: 1;
  color: var(--c-ink);
  opacity: var(--ph-mark, 0.14);
  pointer-events: none;
  user-select: none;
}

.media { container-type: inline-size; }

.media__label {
  position: absolute;
  left: 0.7rem;
  bottom: 0.7rem;
  z-index: 2;
  padding: 0.35em 0.6em;
  background: color-mix(in oklab, var(--c-ink) 82%, transparent);
  color: var(--c-canvas);
  font-family: var(--f-mono, var(--f-ui));
  font-size: 0.5625rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  border-radius: var(--r-sm);
  pointer-events: none;
  white-space: nowrap;
  max-width: calc(100% - 1.4rem);
  overflow: hidden;
  text-overflow: ellipsis;
}

.hide-slot-labels .media__label { display: none; }

/* --------------------------------------------------------------------------
   Product card
   -------------------------------------------------------------------------- */
.prod { display: flex; flex-direction: column; gap: var(--s3); }
.prod__media { --ar: 3 / 4; }
.prod__meta { display: flex; justify-content: space-between; align-items: baseline; gap: var(--s2); }
.prod__name { font-family: var(--f-display); font-size: var(--fs-h3); line-height: 1.2; }
.prod__price { font-family: var(--f-ui); font-size: var(--fs-small); letter-spacing: 0.04em; white-space: nowrap; }
.prod__note { font-family: var(--f-ui); font-size: var(--fs-micro); letter-spacing: var(--ls-ui); text-transform: uppercase; color: var(--c-ink-soft); }

.chips { display: flex; flex-wrap: wrap; gap: var(--s1); }
.chip {
  font-family: var(--f-ui);
  font-size: var(--fs-micro);
  letter-spacing: var(--ls-ui);
  padding: 0.45em 0.85em;
  border: var(--border-w) solid var(--c-line);
  border-radius: var(--r-sm);
  color: var(--c-ink-soft);
  transition: border-color var(--d-fast) var(--e-out), color var(--d-fast) var(--e-out);
}
.chip:hover, .chip[aria-pressed="true"] { border-color: var(--c-ink); color: var(--c-ink); }
.chip[disabled] { opacity: 0.35; text-decoration: line-through; cursor: not-allowed; }

/* --------------------------------------------------------------------------
   Form field
   -------------------------------------------------------------------------- */
.field { display: flex; flex-direction: column; gap: var(--s1); }
.field__label {
  font-family: var(--f-ui);
  font-size: var(--fs-micro);
  letter-spacing: var(--ls-ui);
  text-transform: uppercase;
  color: var(--c-ink-soft);
}
.field__input {
  width: 100%;
  padding: 0.9em 1em;
  background: transparent;
  color: var(--c-ink);
  border: var(--border-w) solid var(--c-line);
  border-radius: var(--r-sm);
  font-family: var(--f-body);
  font-size: var(--fs-body);
  transition: border-color var(--d-fast) var(--e-out);
}
.field__input::placeholder { color: var(--c-ink-soft); opacity: 0.6; }
.field__input:focus { border-color: var(--c-accent); }
.field__hint { font-size: var(--fs-micro); color: var(--c-ink-soft); }

/* --------------------------------------------------------------------------
   Site nav (used by the hero mocks)
   -------------------------------------------------------------------------- */
.nav {
  position: fixed;
  top: var(--nav-top, 0px);
  right: 0;
  left: 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: var(--s4);
  padding: var(--s3) var(--gutter);
  transition: background var(--d-base) var(--e-out),
              padding var(--d-base) var(--e-out),
              border-color var(--d-base) var(--e-out);
  border-bottom: var(--border-w) solid transparent;
}

.nav__mark {
  font-family: var(--f-display);
  font-size: clamp(1.1rem, 1.6vw, 1.45rem);
  letter-spacing: var(--ls-mark, 0.24em);
  text-transform: uppercase;
  white-space: nowrap;
}

.nav__links { display: flex; gap: var(--s4); }
.nav__link {
  font-family: var(--f-ui);
  font-size: var(--fs-micro);
  letter-spacing: var(--ls-ui);
  text-transform: uppercase;
  position: relative;
  padding-block: 0.4em;
}
.nav__link::after {
  content: "";
  position: absolute;
  left: 0; bottom: 0;
  width: 100%; height: var(--border-w);
  background: currentColor;
  scale: 0 1;
  transform-origin: left center;
  transition: scale var(--d-base) var(--e-out);
}
.nav__link:hover::after, .nav__link[aria-current="page"]::after { scale: 1 1; }

.nav__toggle { display: none; }

/* 880px, matching the skin's breakpoint. Two different values here meant the
   links vanished at 900 while the skin still expected them — the menu simply
   disappeared on an ordinary laptop window. */
@media (max-width: 880px) {
  .nav__links { display: none; }
  .nav__toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.6em;
    font-family: var(--f-ui);
    font-size: var(--fs-micro);
    letter-spacing: var(--ls-ui);
    text-transform: uppercase;
  }
}

/* --------------------------------------------------------------------------
   Footer
   -------------------------------------------------------------------------- */
.foot { padding-block: var(--s8) var(--s5); border-top: var(--border-w) solid var(--c-line); }
.foot__cols { display: grid; gap: var(--s6); grid-template-columns: 1.4fr repeat(3, 1fr); }
.foot__head { font-family: var(--f-ui); font-size: var(--fs-micro); letter-spacing: var(--ls-ui); text-transform: uppercase; color: var(--c-ink-soft); margin-bottom: var(--s3); }
.foot__list > li + li { margin-top: 0.7em; }
.foot__list a { font-size: var(--fs-small); }
.foot__list a:hover { color: var(--c-accent); }
.foot__base { margin-top: var(--s7); display: flex; flex-wrap: wrap; justify-content: space-between; gap: var(--s2); }

@media (max-width: 780px) {
  .foot__cols { grid-template-columns: 1fr 1fr; }
}
@media (max-width: 460px) {
  .foot__cols { grid-template-columns: 1fr; }
}

/* --------------------------------------------------------------------------
   Marquee — shared shell; velocity behaviour is added per direction in JS
   -------------------------------------------------------------------------- */
.marquee { overflow: hidden; display: flex; width: 100%; }
.marquee__track { display: flex; flex: none; gap: var(--marq-gap, var(--s6)); padding-right: var(--marq-gap, var(--s6)); will-change: transform; }
.marquee__item { flex: none; white-space: nowrap; }

/* --------------------------------------------------------------------------
   Reveal defaults — elements start hidden ONLY when JS is confirmed present.
   .js is set on <html> by gsap-boot.js before paint, so a JS failure leaves
   the page fully readable instead of blank.
   -------------------------------------------------------------------------- */
.js [data-reveal] { opacity: 0; }
.js [data-reveal-mask] > * { will-change: transform; }

/* SplitText's line masks clip exactly to the line box. High-contrast serifs
   and italics carry side bearings and overshoots beyond that box, so their
   first and last glyphs get shaved. Widening the mask and pulling it back
   with an equal negative margin keeps the vertical reveal intact and stops
   the cropping, without moving anything in layout. */
.js .ln-mask {
  padding-inline: 0.14em;
  margin-inline: -0.14em;
  padding-bottom: 0.04em;
}

@media (prefers-reduced-motion: reduce) {
  .js [data-reveal] { opacity: 1; }
}
