/* =========================================================
   GLD Alloys — shared base
   Loaded by all three directions before their own tokens.
   Nothing here is direction-specific.
   ========================================================= */

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

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

body {
  margin: 0;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

body.is-loading { overflow: hidden; }

h1, h2, h3, h4, p, figure, blockquote { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }
img, svg, canvas, video { display: block; max-width: 100%; }
button, input, select, textarea { font: inherit; color: inherit; }
a { color: inherit; text-decoration: none; }

.u-vh {
  position: absolute; width: 1px; height: 1px;
  padding: 0; margin: -1px; overflow: hidden;
  clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}

.wrap { width: 100%; max-width: var(--maxw); margin: 0 auto; padding: 0 var(--gutter); }

/* ---------------------------------------------------------
   Photo slots
   Real photo layered over a fallback gradient, so a missing
   file degrades to a handsome surface instead of a broken
   image. Dropping the JPGs into assets/photos/ is a straight
   swap with no markup change — see IMAGE-BRIEF.md.
   --------------------------------------------------------- */

.ph {
  background-color: #cfd8e2;
  background-size: cover;
  background-position: center;
  background-repeat: no-repeat;
  overflow: hidden;
  position: relative;
  /* Contain ::after's mix-blend-mode. Without this the overlay
     blends against whatever sits behind the slot, not against
     the photo — invisible today only because .ph's own
     background is opaque. Each direction's grading wash
     depends on it. */
  isolation: isolate;
}

/* ⚠ These rules must stay in THIS file. A relative url() inside a custom
   property is resolved against the stylesheet that substitutes the var(),
   not against the document. The markup writes `../assets/photos/…`, which
   is only correct because this file sits in assets/. Move these rules into
   a direction's css/ folder and every photo silently 404s. */
.ph-1 { background-image: var(--photo, none), linear-gradient(142deg, #eef3f8 0%, #c3d0de 38%, #90a3b7 72%, #b9c7d5 100%); }
.ph-2 { background-image: var(--photo, none), linear-gradient(142deg, #dfe7ef 0%, #a9b9ca 45%, #7e91a6 100%); }
.ph-3 { background-image: var(--photo, none), linear-gradient(142deg, #4a5d73 0%, #2b3d52 48%, #16283c 100%); }
.ph-4 { background-image: var(--photo, none), linear-gradient(142deg, #f6f9fc 0%, #dbe4ed 40%, #aebccb 100%); }

/* Faint billet-end texture so placeholders read as *aluminium*
   rather than as generic gradient blocks. */
.ph::after {
  content: '';
  position: absolute; inset: 0;
  background-image: radial-gradient(circle at 30% 28%, rgba(255,255,255,.5) 0 14%, transparent 15%);
  background-size: 88px 76px;
  opacity: .28;
  mix-blend-mode: overlay;
}
/* Only once the real file is confirmed loaded — see photos.js.
   This was previously .ph[style*="assets/photos"], which matches
   the markup's own --photo url and so was true on every slot
   from the start, hiding the texture above unconditionally. */
.ph--live::after { display: none; }

/* ---------------------------------------------------------
   Reduced motion — global safety net.
   Each direction's JS also branches on this.
   --------------------------------------------------------- */

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