/* ==========================================================================
   Structural layout: canvas layer, header, footer, container, grids
   ========================================================================== */

.container {
  max-width: var(--container-max);
  margin-inline: auto;
  padding-inline: var(--container-pad);
}

/* ---- Persistent WebGL / fallback canvas layer ----
   Sits behind all DOM content. Fixed to the viewport by default, spanning
   the whole scroll journey, on every page except the home page — there
   scene.js's updateCanvasLayout() switches this to position:absolute and
   sizes/offsets it to exactly cover the hero section, so the floating mark
   scrolls away together with its parallel hero text instead of staying
   pinned on screen while that text scrolls past underneath it. The values
   below are the fixed-viewport default; JS overrides them inline whenever
   it switches modes. z-index below content, above body background. */
#scene-canvas {
  position: fixed;
  inset: 0;
  width: 100vw;
  height: 100vh;
  z-index: var(--z-canvas);
  pointer-events: none;
}

/* No clip-path here on purpose: cropping the canvas cut fragments off
   mid-shape whenever the scroll-driven motion carried them near the
   boundary, which read as the logo itself being broken rather than staying
   clear of text. The mark stays out of text purely by *positioning*
   (scene.js/waypoints.js's per-page camera/groupX framing and dispersal
   directions) — page layout is untouched. */

/* ---- Flowy ambient background ----
   The site's actual visible backdrop: a slow-drifting soft gradient of
   blurred colour fields (ice-blue / platinum / steel-grey), sitting behind
   the WebGL canvas and everything else. Replaces a flat fill on every page;
   Vision re-tones it dark via body[data-page="vision"] (see components.css
   for the blob colours). An optional <video> layer (see contact/vision/home
   markup) upgrades this once a Flow-generated clip is supplied — until then
   it has no playable source and stays transparent, so the CSS blobs show
   through untouched. */
#scene-fallback {
  position: fixed;
  inset: 0;
  width: 100vw;
  height: 100vh;
  z-index: var(--z-canvas-bg);
  pointer-events: none;
  overflow: hidden;
  background: var(--surface-bg);
  transition: background var(--dur-slow) var(--ease-precision);
}

/* ---- Flowy blobs ----
   Large, heavily blurred colour fields drifting on independent slow loops.
   Pure CSS transform/opacity animation — no canvas, no per-frame JS cost.
   Tones flip to the dark palette on body[data-page="vision"] below. */
.flowy-bg__blob {
  position: absolute;
  border-radius: 50%;
  filter: blur(min(9vw, 90px));
  opacity: 0.55;
  will-change: transform;
}

.flowy-bg__blob--1 {
  top: -10%; left: -8%;
  width: 46vw; height: 46vw;
  background: radial-gradient(circle, var(--c-ice-blue), transparent 70%);
  animation: flowy-drift-1 34s ease-in-out infinite;
}
.flowy-bg__blob--2 {
  bottom: -15%; right: -10%;
  width: 52vw; height: 52vw;
  background: radial-gradient(circle, var(--c-slate-blue), transparent 70%);
  opacity: 0.35;
  animation: flowy-drift-2 40s ease-in-out infinite;
}
.flowy-bg__blob--3 {
  top: 20%; right: 8%;
  width: 30vw; height: 30vw;
  background: radial-gradient(circle, var(--c-chrome-silver), transparent 70%);
  opacity: 0.4;
  animation: flowy-drift-3 28s ease-in-out infinite;
}
.flowy-bg__blob--4 {
  bottom: 5%; left: 12%;
  width: 26vw; height: 26vw;
  background: radial-gradient(circle, var(--c-steel-grey), transparent 70%);
  opacity: 0.3;
  animation: flowy-drift-1 46s ease-in-out infinite reverse;
}

body[data-page="vision"] .flowy-bg__blob--1 { background: radial-gradient(circle, var(--c-deep-blue-grey), transparent 70%); opacity: 0.6; }
body[data-page="vision"] .flowy-bg__blob--2 { background: radial-gradient(circle, var(--c-slate-blue), transparent 70%); opacity: 0.28; }
body[data-page="vision"] .flowy-bg__blob--3 { background: radial-gradient(circle, var(--c-ice-blue), transparent 70%); opacity: 0.16; }
body[data-page="vision"] .flowy-bg__blob--4 { background: radial-gradient(circle, var(--c-steel-grey), transparent 70%); opacity: 0.14; }

@keyframes flowy-drift-1 {
  0%, 100% { transform: translate(0, 0) scale(1); }
  50% { transform: translate(6vw, 8vh) scale(1.12); }
}
@keyframes flowy-drift-2 {
  0%, 100% { transform: translate(0, 0) scale(1); }
  50% { transform: translate(-7vw, -6vh) scale(1.08); }
}
@keyframes flowy-drift-3 {
  0%, 100% { transform: translate(0, 0) scale(1); }
  50% { transform: translate(-4vw, 5vh) scale(0.92); }
}

/* Optional Flow-generated video upgrade — see js/core/flowy-video.js.
   No source until the file is supplied, so it stays transparent and the
   blobs above show through untouched. */
.flowy-bg__video {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  opacity: 0;
  transition: opacity var(--dur-slow) var(--ease-precision);
}
.flowy-bg__video.is-ready { opacity: 0.9; }

@media (prefers-reduced-motion: reduce) {
  .flowy-bg__blob { animation: none; }
}

/* ---- Diagonal linework layer (mid parallax depth) ---- */
.linework {
  position: fixed;
  inset: 0;
  z-index: var(--z-linework);
  pointer-events: none;
  opacity: 0.6;
  background-image: repeating-linear-gradient(
    -34deg,
    rgba(46, 58, 70, 0.06) 0px,
    rgba(46, 58, 70, 0.06) 1px,
    transparent 1px,
    transparent 88px
  );
  will-change: transform;
}

body[data-page="vision"] .linework {
  background-image: repeating-linear-gradient(
    -34deg,
    rgba(184, 188, 194, 0.05) 0px,
    rgba(184, 188, 194, 0.05) 1px,
    transparent 1px,
    transparent 88px
  );
}

main {
  position: relative;
  z-index: var(--z-content);
}

/* ---- Header ----
   At rest, the header has no panel of its own — its backdrop is a soft
   gradient (::before) that fades into the flowy background/video underneath
   rather than a flat translucent bar with a hard bottom edge, so the page
   reads as one continuous immersive surface instead of a chrome strip sitting
   on top of it. Once scrolled (.is-condensed) it solidifies into a normal
   blurred nav bar for legibility over passing content. Always light/
   translucent even on the dark Vision page, where it reads as a calm fixed
   frame around the one deep chapter rather than blending into it. */
.site-header {
  position: fixed;
  top: 0;
  left: 0;
  right: 0;
  z-index: var(--z-header);
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: var(--sp-5) var(--container-pad);
  color: var(--c-charcoal);
  transition: padding var(--dur-base) var(--ease-precision);
}

.site-header::before {
  content: '';
  position: absolute;
  inset: 0 0 -56px 0; /* extends the fade well past the header's own box so it never ends in a visible line */
  z-index: -1;
  pointer-events: none;
  background: linear-gradient(to bottom, rgba(243, 244, 246, 0.75) 0%, rgba(243, 244, 246, 0.4) 45%, rgba(243, 244, 246, 0) 100%);
  transition: background var(--dur-base) var(--ease-precision), inset var(--dur-base) var(--ease-precision);
}

.site-header.is-condensed {
  padding-block: var(--sp-3);
}

.site-header.is-condensed::before {
  inset: 0;
  background: rgba(243, 244, 246, 0.88);
  backdrop-filter: blur(14px) saturate(140%);
  -webkit-backdrop-filter: blur(14px) saturate(140%);
  border-bottom: 1px solid var(--border-on-light);
}

.site-footer {
  position: relative;
  z-index: var(--z-content);
  background: var(--surface-bg-dark);
  color: var(--surface-text-dark);
  padding: var(--sp-9) var(--container-pad) var(--sp-6);
  border-top: 1px solid var(--surface-border-dark);
}

.footer-grid {
  display: grid;
  grid-template-columns: 1.4fr 1fr 1fr;
  gap: var(--sp-7);
  max-width: var(--container-max);
  margin-inline: auto;
  padding-bottom: var(--sp-8);
}

.footer-bottom {
  max-width: var(--container-max);
  margin-inline: auto;
  padding-top: var(--sp-5);
  border-top: 1px solid var(--surface-border-dark);
  display: flex;
  flex-wrap: wrap;
  gap: var(--sp-4);
  justify-content: space-between;
  font-size: var(--fs-xs);
  color: var(--c-steel-grey);
}

@media (max-width: 860px) {
  .footer-grid { grid-template-columns: 1fr; gap: var(--sp-6); }
}

/* ---- Section rhythm ---- */
.chapter {
  position: relative;
  z-index: var(--z-content);
  padding-block: var(--sp-10);
}

.chapter--tight { padding-block: var(--sp-8); }

.section-head {
  max-width: 46rem;
  margin-bottom: var(--sp-7);
}

.grid-2 { display: grid; grid-template-columns: repeat(2, 1fr); gap: var(--sp-7); }
.grid-3 { display: grid; grid-template-columns: repeat(3, 1fr); gap: var(--sp-6); }
.grid-4 { display: grid; grid-template-columns: repeat(4, 1fr); gap: var(--sp-5); }

@media (max-width: 1024px) {
  .grid-3 { grid-template-columns: repeat(2, 1fr); }
  .grid-4 { grid-template-columns: repeat(2, 1fr); }
}

@media (max-width: 640px) {
  .grid-2, .grid-3, .grid-4 { grid-template-columns: 1fr; }
  .chapter { padding-block: var(--sp-8); }
}
