/* ==========================================================================
   Reset, typography roles, grounds, pre-animation states.
   ========================================================================== */

/* ==========================================================================
   Metric-matched fallbacks. Measured in-browser against the real web fonts,
   not estimated: Source Sans 3 is 7.5% narrower than Arial, so an unadjusted
   fallback reflows every line when the web font swaps in.
   ========================================================================== */

@font-face {
  font-family: "Bodoni Fallback";
  src: local("Georgia"), local("Times New Roman"), local("Times");
  size-adjust: 102.12%;
  ascent-override: 110.7%;
  descent-override: 39.2%;
  line-gap-override: 0%;
}

@font-face {
  font-family: "Source Fallback";
  src: local("Arial"), local("Helvetica Neue"), local("Helvetica");
  size-adjust: 93.82%;
  ascent-override: 108.7%;
  descent-override: 42.6%;
  line-gap-override: 0%;
}

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

* {
  margin: 0;
}

html {
  -webkit-text-size-adjust: 100%;
  /* Only anchor jumps (the skip link, the persistent inquire mark) use this;
     nothing else on the page navigates by hash. Reduced-motion below forces
     it back to auto. */
  scroll-behavior: smooth;
}

/* The canvas colour lives on <html> so the fixed wash layers can sit above it.
   A background on <body> would propagate to the canvas and bury them. */
html {
  background: var(--gallery-ink);
}

body {
  background: transparent;
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--fs-body);
  font-weight: var(--fw-text-dark);
  line-height: var(--lh-body-dark);
  letter-spacing: var(--tr-body-dark);
  font-synthesis-weight: none;
  font-optical-sizing: auto;
  font-kerning: normal;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-x: hidden;
}

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

/* --- Ground washes -------------------------------------------------------- */
/* Gallery Ink is the base. Deep Aubergine and Patina Blue cross-fade over it
   on scroll, so the environment changes temperature without any section
   owning a coloured ground. Opacity on a fixed layer stays composited. */

.backdrop {
  position: fixed;
  inset: 0;
  z-index: -1;
  background: var(--gallery-ink);
  pointer-events: none;
}

.backdrop__wash {
  position: absolute;
  inset: 0;
  opacity: 0;
}

.backdrop__wash--aubergine {
  background: var(--deep-aubergine);
}

.backdrop__wash--patina {
  background: var(--patina-blue);
}

/* --- Type roles ----------------------------------------------------------- */
/* Every display heading routes through .u-display, so leading and tracking
   can never drift apart between two elements of the same rank. */

.u-display {
  font-family: var(--font-display);
  font-weight: 400;
  font-optical-sizing: auto;
  text-wrap: balance;
}

.u-display--1 { font-size: var(--fs-display); line-height: var(--lh-display); letter-spacing: var(--tr-display); }
.u-display--2 { font-size: var(--fs-title-1); line-height: var(--lh-title-1); letter-spacing: var(--tr-title-1); }
.u-display--3 { font-size: var(--fs-title-2); line-height: var(--lh-title-2); letter-spacing: var(--tr-title-2); }
.u-display--4 { font-size: var(--fs-title-3); line-height: var(--lh-title-3); letter-spacing: var(--tr-title-3); }

/* Uppercase labels. Permitted only where they carry real catalogue data or
   navigation, never stacked above a heading as a kicker. */
.u-label {
  font-family: var(--font-body);
  font-size: var(--fs-label);
  font-weight: var(--fw-label);
  line-height: var(--lh-label);
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  color: var(--ink-secondary);
}

.u-meta {
  font-family: var(--font-body);
  font-size: var(--fs-meta);
  line-height: var(--lh-meta);
  letter-spacing: 0.01em;
  color: var(--ink-secondary);
}

.u-nums {
  font-variant-numeric: tabular-nums lining-nums;
  letter-spacing: var(--tr-num);
}

h1, h2, h3 {
  text-wrap: balance;
}

p {
  text-wrap: pretty;
}

/* --- Grounds -------------------------------------------------------------- */
/* Ink and Porcelain alternate as the environment. Metadata and rule colours
   invert with the ground, because Mirror Silver is 9.5:1 on Ink but only
   1.7:1 on Porcelain. */

/* Dark type on a light ground needs none of the compensation light-on-dark
   does, so all three axes drop back at once: weight, leading and tracking. */
.ground-porcelain {
  background: var(--porcelain);
  --ground: var(--porcelain);
  --ink: var(--gallery-ink);
  --ink-secondary: rgb(16 17 20 / 0.6);
  --rule: rgb(62 98 106 / 0.34);
  color: var(--gallery-ink);
  font-weight: var(--fw-text);
  line-height: var(--lh-body);
  letter-spacing: var(--tr-body);
}

/* --- Focus ---------------------------------------------------------------- */

:focus-visible {
  outline: var(--hairline) solid currentColor;
  outline-offset: 4px;
}

.skip-link {
  position: absolute;
  left: var(--page-x);
  top: 0;
  z-index: 100;
  padding: var(--space-2xs) var(--space-sm);
  background: var(--porcelain);
  color: var(--gallery-ink);
  font-size: var(--fs-label);
  font-weight: var(--fw-label);
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  text-decoration: none;
  transform: translateY(-140%);
  transition: transform var(--dur-med) var(--ease-expo);
}

.skip-link:focus-visible {
  transform: translateY(0);
}

/* --- Persistent contact affordance -----------------------------------------
   The critique found the one CTA on the page unreachable without scrolling
   twelve screens with no way back. This is the answer that keeps the brand's
   restraint intact: a single hairline mark, quiet by default, that jumps to
   the real invitation rather than duplicating it. It stays out of the way
   during the opening moment and steps aside again once the real CTA is on
   screen, so there is never a second "Inquire" competing with the first. */
.way-in {
  position: fixed;
  right: var(--page-x);
  bottom: clamp(1.25rem, 4vh, 2rem);
  z-index: 40;
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding: 0 1.1em;
  background: var(--gallery-ink);
  border: var(--hairline) solid rgb(240 236 229 / 0.5);
  color: var(--porcelain);
  font-family: var(--font-body);
  font-size: var(--fs-label);
  font-weight: var(--fw-label);
  line-height: var(--lh-label);
  letter-spacing: var(--tr-label);
  text-transform: uppercase;
  text-decoration: none;
  opacity: 0;
  pointer-events: none;
  transition:
    opacity var(--dur-fast) var(--ease-quart),
    background-color var(--dur-fast) var(--ease-quart),
    color var(--dur-fast) var(--ease-quart),
    border-color var(--dur-fast) var(--ease-quart);
}

.way-in.is-visible {
  opacity: 1;
  pointer-events: auto;
}

.way-in:hover,
.way-in:focus-visible {
  background: var(--porcelain);
  color: var(--gallery-ink);
  border-color: var(--gallery-ink);
}

/* .98px, not a round number: matches the same buffer used throughout
   scenes.css, so a fractional viewport width from a non-integer
   devicePixelRatio can't fall through this boundary either. */
@media (max-width: 640.98px) {
  .way-in {
    right: var(--space-sm);
    bottom: var(--space-sm);
  }
}

/* --- Pre-animation states ------------------------------------------------- */
/* Gated behind .js, which is set before first paint. If GSAP fails to load,
   main.js removes the class and every one of these states drops away, so no
   content can be stranded invisible. */

.js .hero__logo,
.js .hero__tagline,
.js .hero__cue {
  opacity: 0;
}

.js .hero__title .line__inner {
  transform: translate3d(0, 105%, 0);
}

/* Plates open from their own horizontal centreline, outward in both
   directions. The governing motif: nothing fades in, everything opens from
   an axis, the way the work itself is made. */
.js [data-mask] .plate__media img {
  --mask-y: 50%;
  --mask-scale: 1.06;
}

/* Headings open from their vertical centreline. Same rule, other axis. */
.js [data-clip] {
  --clip-x: 50%;
}

.js .artist__field {
  transform: scaleY(0);
  transform-origin: bottom;
}

.js .artist__portrait picture {
  --portrait-mask: 50%;
}

/* --- Reduced motion ------------------------------------------------------- */

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

  .js .hero__logo,
  .js .hero__tagline,
  .js .hero__cue {
    opacity: 1;
  }

  .js .hero__title .line__inner {
    transform: none;
  }

  .js [data-mask] .plate__media img {
    --mask-y: 0%;
    --mask-scale: 1;
  }

  .js [data-clip] {
    --clip-x: 0%;
  }

  .js .artist__field {
    transform: none;
  }

  .js .artist__portrait picture {
    --portrait-mask: 0%;
  }
}
