/*
 * Shared foundation only.
 *
 * Deliberately thin. Each demo carries its own palette, type scale and layout, because five sites
 * built off one stylesheet look like five sites built off one stylesheet — which is the opposite of
 * what a portfolio needs to prove. What lives here is the part that should be identical everywhere:
 * the reset, the reveal mechanics, the accessibility floor.
 */

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

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

body {
  margin: 0;
  min-height: 100vh;
  text-rendering: optimizeLegibility;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

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

h1, h2, h3, h4, p, figure, blockquote { margin: 0; }
ul, ol { margin: 0; padding: 0; list-style: none; }

a { color: inherit; text-decoration: none; }
button { font: inherit; color: inherit; background: none; border: 0; cursor: pointer; }

/* Visible focus is not optional, and a design that cannot survive a focus ring is not finished. */
:focus-visible {
  outline: 2px solid currentColor;
  outline-offset: 3px;
  border-radius: 2px;
}

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

/* --- Reveals ------------------------------------------------------------------------------- */

[data-reveal] {
  opacity: 0;
  transform: translate3d(0, 22px, 0);
  transition: opacity .9s cubic-bezier(.16, 1, .3, 1), transform .9s cubic-bezier(.16, 1, .3, 1);
  will-change: opacity, transform;
}
[data-reveal="fade"] { transform: none; }
[data-reveal="left"] { transform: translate3d(-26px, 0, 0); }
[data-reveal="scale"] { transform: scale(1.04); }

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

/* Word-by-word headline rise, set up by splitWords(). */
.word { display: inline-block; overflow: hidden; vertical-align: bottom; }
.word > span {
  display: inline-block;
  transform: translateY(105%);
  transition: transform 1s cubic-bezier(.16, 1, .3, 1);
  transition-delay: calc(var(--i) * 55ms);
}
.is-revealed .word > span,
[data-reveal].is-revealed .word > span { transform: none; }

/* --- The WebGL layer ----------------------------------------------------------------------- */

/*
 * Every effect canvas sits on top of a CSS background-image of the same photograph. If WebGL is
 * unavailable, the shader fails to compile, or the context is lost, the canvas simply never becomes
 * visible and the visitor sees the photograph. No blank hero, ever.
 */
.gl-canvas {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  opacity: 0;
  transition: opacity 1.2s ease;
}
.gl-canvas[data-ready="true"] { opacity: 1; }

.gl-fallback {
  position: absolute;
  inset: 0;
  background-size: cover;
  background-position: center;
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *, *::before, *::after {
    animation-duration: .001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: .001ms !important;
  }

  [data-reveal] { opacity: 1; transform: none; }
  .word > span { transform: none; }
}
