/* Ambient motion layer (references/ambient.md). From the Driftless Part 3 site, 2026-10-28: his words "Dude, I love it!",
   the gradients "the coolest thing". His rule after it: when the page is still, ONLY the background gradients move
   (slowly circling); everything else moves on arrival or with the scroll. js/ambient.js adds the classes.
   Colours: --amb-1 (the accent) and --amb-2 (a cool partner); set them in brand.css to change the pair.
   Readability: every moving layer sits behind the content; boxes keep a solid fill; body text stops once it has arrived;
   forms never move. Only bands near the screen animate. Reduced motion: gradients still, nothing moves. */

@property --amb-a { syntax: "<angle>"; inherits: false; initial-value: 0deg; }
:where(:root) {
  --amb-1: var(--accent);
  --amb-2: oklch(from var(--accent) 0.56 0.09 calc(h + 190));
  --amb-glint: oklch(from var(--accent) 0.9 0.06 h);
  --amb-edge-w: 2px;
}

/* 1. The gradient field behind each band: three soft blobs that circle slowly, forever (the one idle motion) */
.amb .amb-band { position: relative; isolation: isolate; overflow: clip; }
.amb-bg { position: absolute; inset: 0; z-index: -1; pointer-events: none; overflow: clip;
  /* the glows fade out toward the band's top and bottom, so no glow is ever cut by a hard line (his rule, seams) */
  -webkit-mask-image: linear-gradient(transparent, #000 140px, #000 calc(100% - 140px), transparent); mask-image: linear-gradient(transparent, #000 140px, #000 calc(100% - 140px), transparent); }
.amb-stage { position: sticky; top: 0; height: 100vh; max-height: 100%; overflow: clip; }   /* tall bands never scroll it away */
.amb-o { position: absolute; width: 0; height: 0; animation: amb-turn var(--t, 46s) linear infinite; }
.amb-o1 { left: 18%; top: 22%; --t: 46s; }
.amb-o2 { left: 82%; top: 48%; --t: 58s; animation-direction: reverse; }
.amb-o3 { left: 48%; top: 96%; --t: 39s; }
.amb-b {
  position: absolute; width: min(78vmax, 1150px); aspect-ratio: 1; border-radius: 50%; translate: -50% -50%;
  background: radial-gradient(closest-side, var(--c), color-mix(in srgb, var(--c) 45%, transparent) 50%, transparent 100%);
  transform: translateX(var(--r, 16vmax));
}
.amb-o1 .amb-b { --c: color-mix(in srgb, var(--amb-1) 30%, transparent); }
.amb-o2 .amb-b { --c: color-mix(in srgb, var(--amb-2) 36%, transparent); --r: 12vmax; }
.amb-o3 .amb-b { --c: color-mix(in srgb, var(--amb-1) 18%, transparent); --r: 10vmax; }
.amb-bg[data-k="1"] .amb-o1 { left: 78%; top: 18%; } .amb-bg[data-k="1"] .amb-o2 { left: 12%; top: 64%; }
.amb-bg[data-k="2"] .amb-o1 { left: 30%; top: 70%; } .amb-bg[data-k="2"] .amb-o3 { left: 84%; top: 90%; }
.amb-bg[data-k="3"] .amb-o2 { left: 70%; top: 4%; }
/* one animation per blob: it circles and breathes in the same keyframes (half the running animations of two) */
@keyframes amb-turn { 0% { rotate: 0deg; scale: 1; } 50% { scale: 1.16; } 100% { rotate: 360deg; scale: 1; } }
/* dot grain and a band of light: they move with the scroll only (--amb-p is 0..1 as the band passes) */
.amb-dots {
  position: absolute; inset: -64px;
  background-image: radial-gradient(rgb(255 255 255 / 0.075) 1.3px, transparent 1.8px); background-size: 32px 32px;
  -webkit-mask-image: linear-gradient(115deg, transparent 5%, #000 45%, transparent 95%); mask-image: linear-gradient(115deg, transparent 5%, #000 45%, transparent 95%);
  transform: translate(calc(var(--amb-p, 0) * 64px), calc(var(--amb-p, 0) * 64px));
}
.amb-ray {
  position: absolute; top: -20%; bottom: -20%; left: 0; width: 38%;
  background: linear-gradient(100deg, transparent, rgb(255 255 255 / 0.07) 45%, color-mix(in srgb, var(--amb-1) 10%, transparent) 55%, transparent);
  transform: translateX(calc(-120% + var(--amb-p, 0) * 500%)) skewX(-14deg);
}
/* light bands: softer blobs, darker dots */
.amb-bg[data-tone="light"] .amb-o1 .amb-b { --c: color-mix(in srgb, var(--amb-1) 20%, transparent); }
.amb-bg[data-tone="light"] .amb-o2 .amb-b { --c: color-mix(in srgb, var(--amb-2) 24%, transparent); }
.amb-bg[data-tone="light"] .amb-dots { background-image: radial-gradient(rgb(0 0 0 / 0.08) 1.3px, transparent 1.8px); }
.amb-bg[data-tone="light"] .amb-ray { background: linear-gradient(100deg, transparent, rgb(255 255 255 / 0.55) 50%, transparent); }
/* the accent band: stays the accent, the blobs shift it lighter and deeper; the darkening one is kept weak so the
   text on it stays above 4.5:1 (measure its darkest point) */
.amb-bg[data-tone="brand"] .amb-o1 .amb-b { --c: oklch(from var(--amb-1) calc(l + 0.12) c h / 0.7); }
.amb-bg[data-tone="brand"] .amb-o2 .amb-b { --c: oklch(from var(--amb-1) calc(l - 0.12) c h / 0.2); }
.amb-bg[data-tone="brand"] .amb-o3 .amb-b { --c: oklch(from var(--amb-1) calc(l + 0.2) c h / 0.45); }
.amb-bg[data-tone="brand"] .amb-dots { background-image: radial-gradient(rgb(0 0 0 / 0.09) 1.3px, transparent 1.8px); }
.amb-bg[data-tone="brand"] .amb-ray { background: linear-gradient(100deg, transparent, rgb(255 255 255 / 0.22) 50%, transparent); }
/* the shared field behind every band on the page's own ground (ambient.js, body.js .edge-flat; his pick 2026-10-29):
   fixed to the screen, five large overlapping glows that drift as well as circle, so it reads as one moving gradient */
.amb-field { position: fixed; inset: 0; z-index: -1; pointer-events: none; overflow: clip; }
.amb-field .amb-stage { position: absolute; inset: 0; height: auto; max-height: none; }
.amb-field .amb-b { width: min(90vmax, 1300px);   /* 1800 px gave 46 fps on Jeske but 60 on Driftless: page-dependent; check speed before going bigger */ animation: amb-drift 23s ease-in-out infinite alternate; }
.amb-o4 { left: 88%; top: 14%; --t: 39s; animation-direction: reverse; }
.amb-o5 { left: 8%; top: 84%; --t: 52s; }
.amb-o4 .amb-b { --c: color-mix(in srgb, var(--amb-1) 22%, transparent); }
.amb-o5 .amb-b { --c: color-mix(in srgb, var(--amb-2) 30%, transparent); }
.amb-field .amb-o2 .amb-b { animation-duration: 29s; animation-delay: -7s; }
.amb-field .amb-o3 .amb-b { animation-duration: 19s; animation-delay: -11s; }
.amb-field .amb-o4 .amb-b { animation-duration: 31s; animation-delay: -3s; }
.amb-field .amb-o5 .amb-b { animation-duration: 26s; animation-delay: -15s; }
/* the drift moves the glow around its centre (translate already holds the -50% that centres it) */
@keyframes amb-drift { 0% { translate: -64% -42%; scale: 0.9; } 50% { translate: -40% -62%; scale: 1.12; } 100% { translate: -34% -36%; scale: 0.96; } }
/* bands away from the screen: nothing runs */
.amb-band:not(.amb-live) .amb-o { animation-play-state: paused; }

/* 2. Headings: words rise one by one on arrival, then one glint of light passes along them, once */
.amb-w { display: inline-block; overflow: clip; padding: 0.06em 0.04em 0.12em; margin: -0.06em -0.04em -0.12em; vertical-align: top; }
.amb-wi {
  display: inline-block; transform: translateY(105%) rotate(4deg); opacity: 0;
  transition: transform 0.85s cubic-bezier(0.2, 0.75, 0.15, 1), opacity 0.5s ease-out; transition-delay: calc(var(--i) * 55ms);
  color: transparent; -webkit-text-fill-color: transparent;
  background-image: linear-gradient(100deg, var(--wc) 0 42%, color-mix(in oklch, var(--wc) 45%, var(--amb-glint)) 50%, var(--wc) 58% 100%);   /* half strength: a letter never vanishes */
  background-size: 360% 100%; background-position: 100% 0; -webkit-background-clip: text; background-clip: text;
}
.amb-now .amb-wi { transition: none; }
.amb-in .amb-wi { transform: none; opacity: 1; animation: amb-glint 2s ease-in-out 1 both; animation-delay: calc(0.9s + var(--i) * 90ms); }
@keyframes amb-glint { from { background-position: 100% 0; } to { background-position: 0% 0; } }
/* the glint must never wash a letter out: a light glint on dark bands, a deep accent on light ones, white on the accent */
.amb-on-light .amb-wi { --amb-glint: var(--accent-on-light, oklch(from var(--amb-1) 0.45 c h)); }
.amb-on-brand .amb-wi, [data-tone="brand"] .amb-wi { --amb-glint: #fff; }

/* body text: a soft arrival, then still for good */
.amb-tx { opacity: 0; transform: translateY(18px); filter: blur(6px); transition: opacity 0.7s ease-out, transform 0.8s cubic-bezier(0.2, 0.75, 0.15, 1), filter 0.7s ease-out; transition-delay: var(--d, 0ms); }
.amb-tx.amb-in { opacity: 1; transform: none; filter: none; }

/* 3. Boxes: they tip up into place; on arrival a light runs once round the edge ON the border line (it never
   widens the box or eats the gap: the spacing lesson of 2026-10-28); on a computer they lean toward the pointer */
.amb-box { position: relative; }
.amb-box.amb-pre:not(.amb-in), html.fx .amb-box.rv:not(.in) { opacity: 0; transform: perspective(1000px) rotateX(18deg) translateY(56px) scale(0.94); }
.amb-box.amb-pre.amb-in, html.fx .amb-box.rv.in { transition: opacity 0.7s ease-out, transform 0.95s cubic-bezier(0.2, 0.75, 0.15, 1), box-shadow 0.2s; transition-delay: var(--d, 0s); }
.amb-edge {
  position: absolute; inset: var(--ei, 0); border-radius: inherit; padding: var(--ew, var(--amb-edge-w)); pointer-events: none; z-index: 2; opacity: 0;
  background: conic-gradient(from var(--amb-a), transparent 0 64%, color-mix(in srgb, var(--amb-1) 0%, transparent) 66%, var(--amb-1) 78%, var(--amb-glint) 82%, transparent 90% 100%);
  -webkit-mask: linear-gradient(#000 0 0) content-box, linear-gradient(#000 0 0); -webkit-mask-composite: xor;
  mask: linear-gradient(#000 0 0) content-box exclude, linear-gradient(#000 0 0);
}
.amb-in > .amb-edge, .in > .amb-edge, .amb-box:hover > .amb-edge { animation: amb-lap 2.6s cubic-bezier(0.45, 0, 0.25, 1) 1 both; animation-delay: var(--ed, 0.5s); }
.amb-box:hover > .amb-edge { animation-delay: 0s; }
@keyframes amb-lap { 0% { --amb-a: 0deg; opacity: 0; } 10% { opacity: 1; } 85% { opacity: 1; } 100% { --amb-a: 360deg; opacity: 0; } }
.amb-glare { position: absolute; inset: 0; border-radius: inherit; pointer-events: none; z-index: 1; opacity: 0; transition: opacity 0.3s;
  background: radial-gradient(240px circle at var(--gx, 50%) var(--gy, 50%), rgb(255 255 255 / 0.13), transparent 70%); }
.amb-tilt > .amb-glare { opacity: 1; }
.amb-box.amb-tilt { transform: perspective(900px) rotateX(var(--ry, 0deg)) rotateY(var(--rx, 0deg)) translate(-3px, -3px) !important; transition: transform 0.12s linear, box-shadow 0.2s !important; }

/* the gap between a section heading and the box grid under it, so a lit edge never crowds the heading (Driftless 3b:
   40 px, 28 px on a phone; only when a grid follows directly) */
.amb .section-head:has(+ :is(.cards, .reviews, .bento, .reasons, .gallery, .ramp)) { margin-bottom: 40px; }
@media (max-width: 760px) { .amb .section-head:has(+ :is(.cards, .reviews, .bento, .reasons, .gallery, .ramp)) { margin-bottom: 28px; } }

/* buttons: one sheen across the main button when it arrives, and again on hover */
.amb .btn:not(.alt) { background-image: linear-gradient(110deg, transparent 35%, rgb(255 255 255 / 0.42) 50%, transparent 65%); background-size: 260% 100%; background-position: 130% 0; background-repeat: no-repeat; }
.amb .btn.amb-in:not(.alt), .amb .btn:not(.alt):hover { animation: amb-sheen 1.4s ease-in-out 1 both; }
.amb .btn.amb-in:not(.alt):not(:hover) { animation-delay: 0.8s; }
@keyframes amb-sheen { from { background-position: 130% 0; } to { background-position: -30% 0; } }

/* 4. Photos: they open from a wipe, then pan inside their frame with the scroll (--amb-q is 0..1 as it passes) */
.amb-ph { clip-path: inset(0 0 100% 0 round var(--r-m, 12px)); transform: scale(1.06); transition: clip-path 1.1s cubic-bezier(0.65, 0, 0.2, 1), transform 1.4s cubic-bezier(0.2, 0.75, 0.15, 1); }
.amb-ph.amb-in { clip-path: inset(0 0 0 0 round var(--r-m, 12px)); transform: none;
  object-position: calc(var(--ox) - 12% + var(--amb-q, 0.5) * 24%) calc(var(--oy) + 10% - var(--amb-q, 0.5) * 20%); }

@media (prefers-reduced-motion: reduce) {
  .amb-o, .amb-field .amb-b { animation: none !important; }
  .amb-dots, .amb-ray, .amb-edge, .amb-glare { display: none; }
  .amb-wi, .amb-tx, .amb-ph, .amb-box, .amb .btn { opacity: 1 !important; transform: none !important; filter: none !important; clip-path: none !important; animation: none !important; }
}
