/* ============================================================
   Sandbox Engineering — shared site utilities
   Composes with the Sandbox Design System tokens; no new colors,
   type sizes or radii are introduced here — layout/utility only.
   ============================================================ */

@media (prefers-reduced-motion: no-preference) {
  html { scroll-behavior: smooth; }
}

.container {
  max-width: var(--container-max);
  margin: 0 auto;
  padding: 0 var(--space-6);
}

.section {
  padding-top: var(--space-11); /* 128px */
}
.section-tight {
  padding-top: var(--space-10); /* 96px */
}

/* Generic responsive grids */
.grid-2 { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--space-5); }
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--space-5); }
.grid-4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--space-5); }
.grid-5 { display: grid; grid-template-columns: repeat(5, 1fr); gap: var(--space-5); }

@media (max-width: 980px) {
  .grid-3, .grid-4, .grid-5 { grid-template-columns: repeat(2, 1fr) !important; }
  .section { padding-top: var(--space-10); }
}
@media (max-width: 640px) {
  .grid-2, .grid-3, .grid-4, .grid-5 { grid-template-columns: 1fr !important; }
  .container { padding: 0 20px; }
  .section { padding-top: var(--space-9); }
}

/* Every page hero (Home, About, Services, Projects, Contact) sets its H1
   via --text-display — at the source 60px size it wraps to 5+ lines on a
   phone-width screen, so scale the token down at narrow widths instead of
   touching each hero individually. */
@media (max-width: 600px) {
  :root {
    --text-display: 2.5rem;   /* 40px */
    --tracking-display: -0.5px;
  }
}

/* Ambient hero color blobs — slow, subtle, decorative drift. Purely
   cosmetic (never gates content visibility), so it degrades gracefully:
   if animation is throttled/disabled, blobs simply sit static in their
   start position and the wash still reads correctly. */
.hero-blob {
  position: absolute;
  border-radius: 50%;
  filter: blur(60px);
  pointer-events: none;
  z-index: 0;
}
@media (prefers-reduced-motion: no-preference) {
  .hero-blob-1 { animation: sandbox-blob-drift-1 20s ease-in-out infinite alternate; }
  .hero-blob-2 { animation: sandbox-blob-drift-2 24s ease-in-out infinite alternate; }
  .hero-blob-3 { animation: sandbox-blob-drift-3 28s ease-in-out infinite alternate; }
}
@keyframes sandbox-blob-drift-1 {
  from { transform: translate(0, 0) scale(1); }
  to   { transform: translate(50px, 34px) scale(1.1); }
}
@keyframes sandbox-blob-drift-2 {
  from { transform: translate(0, 0) scale(1); }
  to   { transform: translate(-40px, 46px) scale(0.92); }
}
@keyframes sandbox-blob-drift-3 {
  from { transform: translate(0, 0) scale(1); }
  to   { transform: translate(30px, -40px) scale(1.06); }
}

/* Scroll reveal — content is visible by default (.reveal alone never
   hides anything); reveal.js opts individual below-the-fold elements
   into a fade+rise by adding .reveal-pending, then swaps to
   .reveal-visible on intersection. If JS never runs, or the browser
   lacks IntersectionObserver, or prefers-reduced-motion is set,
   content simply stays in its default visible state. */
.reveal { opacity: 1; transform: none; }
.reveal.reveal-pending { opacity: 0; transform: translateY(16px); }
.reveal.reveal-visible {
  opacity: 1; transform: none;
  transition: opacity 600ms cubic-bezier(0.16, 1, 0.3, 1), transform 600ms cubic-bezier(0.16, 1, 0.3, 1);
  transition-delay: var(--stagger, 0ms);
}
@media (prefers-reduced-motion: reduce) {
  .reveal.reveal-pending { opacity: 1; transform: none; }
}

/* Shared easing vocabulary — natural deceleration, no bounce/elastic. */
:root {
  --ease-out-quart: cubic-bezier(0.25, 1, 0.5, 1);
  --ease-out-quint: cubic-bezier(0.22, 1, 0.36, 1);
  --ease-out-expo: cubic-bezier(0.16, 1, 0.3, 1);
}

a:focus-visible,
button:focus-visible,
input:focus-visible,
select:focus-visible,
textarea:focus-visible,
[tabindex]:focus-visible {
  outline: none;
  box-shadow: var(--shadow-ring);
  border-radius: var(--radius-base);
}

::view-transition-old(root), ::view-transition-new(root) { animation: none; }

/* html needs this too, not just body — Safari on iOS can still rubber-band
   scroll horizontally past clipped content (e.g. the hero blobs, which are
   wider than narrow viewports by design) if only body has it. */
html, body { overflow-x: hidden; }

.skip-link {
  position: absolute;
  left: 12px;
  top: -60px;
  background: var(--charcoal-100);
  color: var(--off-white);
  padding: 10px 16px;
  border-radius: var(--radius-base);
  z-index: 999;
  text-decoration: none;
  transition: top 140ms ease;
}
.skip-link:focus {
  top: 12px;
}
