/* ============================================================
   WASH MOTION — scroll reveals + ambient wash features
   Append to your stylesheet. Pair with WW-wash-motion.js
   ============================================================ */

/* Brush-stroke masks. Each is an SVG of a bristled stroke, roughened by
   feTurbulence, occupying the LEFT HALF of an 800x100 canvas. Sliding the
   mask from mask-position-x 100% to 0% drags that stroke across the element.
   Regenerate with different seeds if you want a different bristle pattern. */
:root {
  --WW-stroke-1: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20800%20100'%20preserveAspectRatio='none'%3E%3Cdefs%3E%3ClinearGradient%20id='g'%3E%3Cstop%20offset='0'%20stop-color='white'%20stop-opacity='1'/%3E%3Cstop%20offset='.78'%20stop-color='white'%20stop-opacity='1'/%3E%3Cstop%20offset='.9'%20stop-color='white'%20stop-opacity='.55'/%3E%3Cstop%20offset='1'%20stop-color='white'%20stop-opacity='0'/%3E%3C/linearGradient%3E%3Cfilter%20id='f'%20x='-4%25'%20y='-35%25'%20width='108%25'%20height='170%25'%3E%3CfeTurbulence%20type='fractalNoise'%20baseFrequency='0.02%200.45'%20numOctaves='2'%20seed='3'%20result='n'/%3E%3CfeDisplacementMap%20in='SourceGraphic'%20in2='n'%20scale='20'%20xChannelSelector='R'%20yChannelSelector='G'/%3E%3C/filter%3E%3C/defs%3E%3Cg%20filter='url(%23f)'%3E%3Crect%20x='-40'%20y='-25'%20width='300'%20height='150'%20fill='white'/%3E%3Crect%20x='-40'%20y='-8'%20width='393'%20height='12'%20fill='url(%23g)'/%3E%3Crect%20x='-40'%20y='5'%20width='377'%20height='10'%20fill='url(%23g)'/%3E%3Crect%20x='-40'%20y='16'%20width='430'%20height='14'%20fill='url(%23g)'/%3E%3Crect%20x='-40'%20y='31'%20width='343'%20height='8'%20fill='url(%23g)'/%3E%3Crect%20x='-40'%20y='40'%20width='444'%20height='11'%20fill='url(%23g)'/%3E%3Crect%20x='-40'%20y='53'%20width='450'%20height='10'%20fill='url(%23g)'/%3E%3Crect%20x='-40'%20y='64'%20width='448'%20height='13'%20fill='url(%23g)'/%3E%3Crect%20x='-40'%20y='79'%20width='410'%20height='9'%20fill='url(%23g)'/%3E%3Crect%20x='-40'%20y='88'%20width='444'%20height='9'%20fill='url(%23g)'/%3E%3Crect%20x='-40'%20y='98'%20width='452'%20height='9'%20fill='url(%23g)'/%3E%3C/g%3E%3C/svg%3E");
  --WW-stroke-2: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20800%20100'%20preserveAspectRatio='none'%3E%3Cdefs%3E%3ClinearGradient%20id='g'%3E%3Cstop%20offset='0'%20stop-color='white'%20stop-opacity='1'/%3E%3Cstop%20offset='.78'%20stop-color='white'%20stop-opacity='1'/%3E%3Cstop%20offset='.9'%20stop-color='white'%20stop-opacity='.55'/%3E%3Cstop%20offset='1'%20stop-color='white'%20stop-opacity='0'/%3E%3C/linearGradient%3E%3Cfilter%20id='f'%20x='-4%25'%20y='-35%25'%20width='108%25'%20height='170%25'%3E%3CfeTurbulence%20type='fractalNoise'%20baseFrequency='0.018%200.5'%20numOctaves='2'%20seed='11'%20result='n'/%3E%3CfeDisplacementMap%20in='SourceGraphic'%20in2='n'%20scale='24'%20xChannelSelector='R'%20yChannelSelector='G'/%3E%3C/filter%3E%3C/defs%3E%3Cg%20filter='url(%23f)'%3E%3Crect%20x='-40'%20y='-25'%20width='300'%20height='150'%20fill='white'/%3E%3Crect%20x='-40'%20y='-8'%20width='354'%20height='13'%20fill='url(%23g)'/%3E%3Crect%20x='-40'%20y='5'%20width='375'%20height='13'%20fill='url(%23g)'/%3E%3Crect%20x='-40'%20y='19'%20width='357'%20height='9'%20fill='url(%23g)'/%3E%3Crect%20x='-40'%20y='28'%20width='434'%20height='13'%20fill='url(%23g)'/%3E%3Crect%20x='-40'%20y='41'%20width='394'%20height='12'%20fill='url(%23g)'/%3E%3Crect%20x='-40'%20y='53'%20width='351'%20height='8'%20fill='url(%23g)'/%3E%3Crect%20x='-40'%20y='62'%20width='404'%20height='9'%20fill='url(%23g)'/%3E%3Crect%20x='-40'%20y='71'%20width='365'%20height='12'%20fill='url(%23g)'/%3E%3Crect%20x='-40'%20y='85'%20width='397'%20height='9'%20fill='url(%23g)'/%3E%3Crect%20x='-40'%20y='95'%20width='451'%20height='14'%20fill='url(%23g)'/%3E%3C/g%3E%3C/svg%3E");
  --WW-stroke-3: url("data:image/svg+xml,%3Csvg%20xmlns='http://www.w3.org/2000/svg'%20viewBox='0%200%20800%20100'%20preserveAspectRatio='none'%3E%3Cdefs%3E%3ClinearGradient%20id='g'%3E%3Cstop%20offset='0'%20stop-color='white'%20stop-opacity='1'/%3E%3Cstop%20offset='.78'%20stop-color='white'%20stop-opacity='1'/%3E%3Cstop%20offset='.9'%20stop-color='white'%20stop-opacity='.55'/%3E%3Cstop%20offset='1'%20stop-color='white'%20stop-opacity='0'/%3E%3C/linearGradient%3E%3Cfilter%20id='f'%20x='-4%25'%20y='-35%25'%20width='108%25'%20height='170%25'%3E%3CfeTurbulence%20type='fractalNoise'%20baseFrequency='0.024%200.4'%20numOctaves='2'%20seed='27'%20result='n'/%3E%3CfeDisplacementMap%20in='SourceGraphic'%20in2='n'%20scale='17'%20xChannelSelector='R'%20yChannelSelector='G'/%3E%3C/filter%3E%3C/defs%3E%3Cg%20filter='url(%23f)'%3E%3Crect%20x='-40'%20y='-25'%20width='300'%20height='150'%20fill='white'/%3E%3Crect%20x='-40'%20y='-8'%20width='364'%20height='11'%20fill='url(%23g)'/%3E%3Crect%20x='-40'%20y='4'%20width='420'%20height='9'%20fill='url(%23g)'/%3E%3Crect%20x='-40'%20y='13'%20width='438'%20height='12'%20fill='url(%23g)'/%3E%3Crect%20x='-40'%20y='25'%20width='383'%20height='14'%20fill='url(%23g)'/%3E%3Crect%20x='-40'%20y='41'%20width='368'%20height='9'%20fill='url(%23g)'/%3E%3Crect%20x='-40'%20y='50'%20width='357'%20height='13'%20fill='url(%23g)'/%3E%3Crect%20x='-40'%20y='64'%20width='392'%20height='11'%20fill='url(%23g)'/%3E%3Crect%20x='-40'%20y='76'%20width='395'%20height='13'%20fill='url(%23g)'/%3E%3Crect%20x='-40'%20y='90'%20width='360'%20height='10'%20fill='url(%23g)'/%3E%3C/g%3E%3C/svg%3E");
}

/* ------------------------------------------------------------
   1. WASH REVEAL
   Hidden state only applies once JS has added .js-wash to <html>,
   so the page is fully readable if JS fails or is blocked.
   ------------------------------------------------------------ */

.js-wash .WW-wash-in {
  --WW-wash-duration: 1.15s;
  --WW-wash-delay: 0ms;

  -webkit-mask-image: linear-gradient(105deg,
      #000 0 30%, rgba(0, 0, 0, 0.55) 42%, rgba(0, 0, 0, 0.15) 52%, transparent 62%);
  mask-image: linear-gradient(105deg,
      #000 0 30%, rgba(0, 0, 0, 0.55) 42%, rgba(0, 0, 0, 0.15) 52%, transparent 62%);
  -webkit-mask-size: 320% 100%;
  mask-size: 320% 100%;
  -webkit-mask-position: 100% 0;
  mask-position: 100% 0;
  -webkit-mask-repeat: no-repeat;
  mask-repeat: no-repeat;

  opacity: 0;
  transition:
    -webkit-mask-position var(--WW-wash-duration) cubic-bezier(0.33, 0.9, 0.4, 1) var(--WW-wash-delay),
    mask-position var(--WW-wash-duration) cubic-bezier(0.33, 0.9, 0.4, 1) var(--WW-wash-delay),
    opacity calc(var(--WW-wash-duration) * 0.55) ease-out var(--WW-wash-delay);
}

.js-wash .WW-wash-in.is-washed {
  -webkit-mask-position: 0 0;
  mask-position: 0 0;
  opacity: 1;
}

/* Drop bloom: the wash spreads outward from a point, like a drop
   of water hitting the paper. Good for images and single cards. */
.js-wash .WW-wash-in[data-wash="bloom"] {
  -webkit-mask-image: radial-gradient(circle at 30% 45%,
      #000 0 30%, rgba(0, 0, 0, 0.5) 48%, transparent 68%);
  mask-image: radial-gradient(circle at 30% 45%,
      #000 0 30%, rgba(0, 0, 0, 0.5) 48%, transparent 68%);
  -webkit-mask-size: 25% 25%;
  mask-size: 25% 25%;
  -webkit-mask-position: 35% 45%;
  mask-position: 35% 45%;
  transition:
    -webkit-mask-size var(--WW-wash-duration) cubic-bezier(0.32, 0.24, 0.2, 1) var(--WW-wash-delay),
    mask-size var(--WW-wash-duration) cubic-bezier(0.32, 0.24, 0.2, 1) var(--WW-wash-delay),
    opacity calc(var(--WW-wash-duration) * 0.5) ease-out var(--WW-wash-delay);
}

.js-wash .WW-wash-in[data-wash="bloom"].is-washed {
  -webkit-mask-size: 185% 185%;
  mask-size: 185% 185%;
}

/* Wash down: pigment settling from the top edge. Suits full-width bands. */
.js-wash .WW-wash-in[data-wash="down"] {
  -webkit-mask-image: linear-gradient(185deg,
      #000 0 28%, rgba(0, 0, 0, 0.5) 44%, transparent 60%);
  mask-image: linear-gradient(185deg,
      #000 0 28%, rgba(0, 0, 0, 0.5) 44%, transparent 60%);
  -webkit-mask-size: 100% 320%;
  mask-size: 100% 320%;
  -webkit-mask-position: 0 100%;
  mask-position: 0 100%;
}

.js-wash .WW-wash-in[data-wash="down"].is-washed {
  -webkit-mask-position: 0 0;
  mask-position: 0 0;
}

/* Stagger: JS numbers the children, CSS turns that into a delay.
   Cards then wash in one after another rather than as a block. */
.js-wash .WW-wash-stagger > * {
  --WW-wash-delay: calc(var(--WW-i, 0) * 0.15s);
}

/* ------------------------------------------------------------
   1b. BRUSH STROKE REVEAL — data-wash="brush"

   Three bristled strokes are laid down in overlapping passes, each
   covering a horizontal band. The leading edge is ragged and streaky
   rather than a clean gradient, and a wet pigment bloom travels just
   behind the edge before drying off.

   Cost note: three filtered SVG masks per element is not free. Use this
   on two or three feature sections, not on every block on the page.
   ------------------------------------------------------------ */

.js-wash .WW-wash-in[data-wash="brush"] {
  --WW-brush-duration: 1.5s;
  --WW-wet-pigment: rgba(232, 99, 58, 0.45);

  position: relative;
  opacity: 0;

  -webkit-mask-image: var(--WW-stroke-1), var(--WW-stroke-2), var(--WW-stroke-3);
  mask-image: var(--WW-stroke-1), var(--WW-stroke-2), var(--WW-stroke-3);
  -webkit-mask-size: 200% 42%, 200% 42%, 200% 42%;
  mask-size: 200% 42%, 200% 42%, 200% 42%;
  -webkit-mask-repeat: no-repeat, no-repeat, no-repeat;
  mask-repeat: no-repeat, no-repeat, no-repeat;
  -webkit-mask-position: 100% 0%, 100% 50%, 100% 100%;
  mask-position: 100% 0%, 100% 50%, 100% 100%;
  transition: none;
}

.js-wash .WW-wash-in[data-wash="brush"].is-washed {
  opacity: 1;
  animation: WW-brush-paint var(--WW-brush-duration)
             cubic-bezier(0.42, 0.68, 0.3, 1) var(--WW-wash-delay) forwards;
}

/* The three bands overlap in time, so the strokes read as one continuous
   scrubbing motion rather than three separate wipes. */
@keyframes WW-brush-paint {
  0% {
    -webkit-mask-position: 100% 0%, 100% 50%, 100% 100%;
    mask-position: 100% 0%, 100% 50%, 100% 100%;
  }
  48% {
    -webkit-mask-position: 0% 0%, 58% 50%, 100% 100%;
    mask-position: 0% 0%, 58% 50%, 100% 100%;
  }
  76% {
    -webkit-mask-position: 0% 0%, 0% 50%, 42% 100%;
    mask-position: 0% 0%, 0% 50%, 42% 100%;
  }
  100% {
    -webkit-mask-position: 0% 0%, 0% 50%, 0% 100%;
    mask-position: 0% 0%, 0% 50%, 0% 100%;
  }
}

/* Wet pigment riding the leading edge. It is clipped by the mask above,
   so colour only appears where paint has actually been laid down. */
.js-wash .WW-wash-in[data-wash="brush"]::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 2;
  pointer-events: none;
  background-image: linear-gradient(97deg,
      transparent 0 36%, var(--WW-wet-pigment) 47%,
      rgba(255, 255, 255, 0) 58%);
  background-size: 200% 100%;
  background-repeat: no-repeat;
  background-position: 100% 0;
  opacity: 0;
  mix-blend-mode: multiply;
}

.js-wash .WW-wash-in[data-wash="brush"].is-washed::after {
  animation: WW-brush-wet var(--WW-brush-duration)
             cubic-bezier(0.42, 0.68, 0.3, 1) var(--WW-wash-delay) forwards;
}

@keyframes WW-brush-wet {
  0%   { background-position: 100% 0; opacity: 0; }
  14%  { opacity: 1; }
  72%  { opacity: 1; }
  100% { background-position: 0 0; opacity: 0; }
}

/* Once the paint is down, drop the mask entirely. Guarantees no bristle
   gap is left sitting over the content, and releases the compositor layer. */
.js-wash .WW-wash-in[data-wash="brush"].is-dry {
  -webkit-mask-image: none;
  mask-image: none;
}

.js-wash .WW-wash-in[data-wash="brush"].is-dry::after { display: none; }

/* ------------------------------------------------------------
   2. BRUSH UNDERLINE
   An SVG stroke that paints itself in when its section reveals.
   ------------------------------------------------------------ */

.WW-brush-heading { position: relative; display: inline-block; }

.WW-brush {
  display: block;
  width: 100%;
  height: 14px;
  margin-top: -0.15em;
  overflow: visible;
}

.WW-brush path {
  fill: none;
  /* stroke: var(--coral, #E8633A); */
  stroke: var(--teal, #E8633A);
  stroke-width: 7;
  stroke-linecap: round;
  opacity: 0.5;
  filter: url(#WW-brush-edge);
  stroke-dasharray: 420;
  stroke-dashoffset: 420;
}

.js-wash .is-washed .WW-brush path,
.js-wash .WW-brush-heading.is-washed .WW-brush path {
  stroke-dashoffset: 0;
  transition: stroke-dashoffset 1.1s cubic-bezier(0.4, 0.8, 0.4, 1) 0.35s;
}

/* ------------------------------------------------------------
   3. BLOOM FIELD
   Slow ambient pigment drifting behind a section. Only transform
   and opacity animate, so it stays cheap; the ragged turbulence
   edge is rasterised once and never re-computed.
   ------------------------------------------------------------ */

.WW-has-blooms { position: relative; isolation: isolate; overflow: hidden; }
.WW-has-blooms > *:not(.WW-bloom-field) { position: relative; z-index: 1; }

.WW-bloom-field {
  position: absolute;
  inset: -10%;
  z-index: 0;
  pointer-events: none;
  opacity: 0.4;
  mix-blend-mode: multiply;
}

.WW-bloom-field svg { width: 100%; height: 100%; display: block; }

.WW-drift {
  transform-origin: center;
  transform-box: fill-box;
  animation: WW-drift-a 34s ease-in-out infinite alternate;
}

.WW-drift-2 { animation-name: WW-drift-b; animation-duration: 43s; }
.WW-drift-3 { animation-name: WW-drift-c; animation-duration: 51s; }

@keyframes WW-drift-a {
  from { transform: translate(0, 0) scale(1); }
  to   { transform: translate(4%, -3%) scale(1.08); }
}

@keyframes WW-drift-b {
  from { transform: translate(0, 0) scale(1.04); }
  to   { transform: translate(-5%, 4%) scale(0.96); }
}

@keyframes WW-drift-c {
  from { transform: translate(2%, 2%) scale(0.98); }
  to   { transform: translate(-3%, -4%) scale(1.1); }
}

/* ------------------------------------------------------------
   4. WET EDGE DIVIDER
   Ragged painted boundary between two sections.
   ------------------------------------------------------------ */

.WW-wet-edge {
  display: block;
  width: 100%;
  height: clamp(28px, 4vw, 56px);
  margin-bottom: -1px;
  pointer-events: none;
}

.WW-wet-edge path { fill: currentColor; }

/* ------------------------------------------------------------
   5. REDUCED MOTION — everything present, nothing moving
   ------------------------------------------------------------ */

@media (prefers-reduced-motion: reduce) {
  .js-wash .WW-wash-in,
  .js-wash .WW-wash-in[data-wash="bloom"],
  .js-wash .WW-wash-in[data-wash="down"],
  .js-wash .WW-wash-in[data-wash="brush"] {
    -webkit-mask-image: none;
    mask-image: none;
    opacity: 1;
    transition: none;
  }

  .js-wash .WW-wash-in[data-wash="brush"] { animation: none; }
  .js-wash .WW-wash-in[data-wash="brush"]::after { display: none; }
  .WW-brush path { stroke-dashoffset: 0; transition: none; }
  .WW-drift { animation: none; }
}