/*
  Wave background. Renders at ALL viewport sizes -- only the wheel/keyboard
  section PAGING (js/scroll-engine.js's fp-active mode) is gated to
  pointer:fine devices; the wave visuals and their section-tracking are not.

  One element: a stack of full-viewport silhouettes, each of which morphs
  between an at-rest shape (the user's Waves.svg curves) and a top-band
  shape (love-ed's flat profile). See js/waves.js for why those cannot be
  the same shape, and why nothing slides any more. Its background colour comes from
  wave-config.json's `backdrop` and is set in JS, since it has to stay in
  step with the palette the layers are generated from -- every layer is
  notched in the bottom-right, so the backdrop is what shows through the
  deepest cut.
*/

/* Text contrast auto-flips with the current section's field colour
   (js/waves.js computes WCAG relative luminance every section change and
   sets this attribute) -- sections don't hardcode light/dark text, since
   that broke twice already as the wave palette changed under it. */
body[data-wave-contrast="dark"] {
  --text-primary: var(--cream-100);
  --text-secondary: var(--blue-100);
}

/* The light branch is NOT redundant, and leaving it out was a real bug.
   Wave layer colours come from wave-config.json, not from CSS tokens, so
   the artwork does not follow the theme toggle -- a section can perfectly
   well show a pale field while the dark theme is on. With only the dark
   branch declared, that case fell through to the theme's own
   --text-primary, which in dark mode is cream: cream copy on a pale pink
   wave. Whichever way the field goes, the measured luminance wins. */
/* --ink-900 until the case-study pages arrived: they put a lede paragraph
   on the field, and a paragraph is normal-size text, so this pair owes 4.5:1
   rather than a heading's 3:1. --ink-900 tops out at 4.10 on the worst wave
   colour. --band-ink is the near-black that reaches 4.64, and it is the same
   pair the band's own flip uses -- one ink for everything that sits on the
   artwork, whichever layer of it. */
body[data-wave-contrast="light"] {
  --text-primary: var(--band-ink);
  --text-secondary: var(--gray-600);
}

/* The band at the top of the screen is a DIFFERENT colour from the field
   below it -- it is the previous section's layer on its way out -- and the
   section title now sits in it. So it gets its own flip, measured against
   its own colour; using the field's flip put ink on a navy band the first
   time the two disagreed. */
/* --band-floor is set on the root element by js/waves.js, in px; see the
   --band-height comment in css/variables.css. */
/* PER BAND, not per page, and the attribute is set on both. js/waves.js
   measures every heading currently on screen and stamps each band with its
   own answer; `body` keeps the current section's as a fallback for a heading
   that was not measured. A band is a nearer ancestor than body, so where both
   carry the attribute the band's value is the one the title inherits.

   It was body alone, and in flow mode that cannot be right: the deck is an
   ordinary scrolling document there, so the NEXT section's heading comes on
   screen -- out in the field, well below the band -- while the current
   section's is still in place. One attribute cannot answer for two headings
   over two different colours. Swept at 375x812, that is every one of the
   frames where the ink was wrong on all 24 sample points at once: not a
   marginal contrast, simply the other colour's answer. */
/* ---- One ink, always cream, with the RING carrying the contrast ----
   This used to be two branches keyed to `data-band-contrast`: cream type on a
   dark band, near-black on a pale one. The flip was correct and it looked
   wrong. Near-black type with a pale ring reads as a smudge -- the ring is
   the lighter of the two and sits OUTSIDE the letter, so it fattens the
   glyph without defining it -- where cream type with a dark ring reads as
   type on every ground the deck has.

   It is only affordable because the ring is opaque. The letter's immediate
   background is the ring, not the wave, and white against the ring is 12.96:1
   at worst across all four palettes. See the outline-pair row in
   tools/check-contrast.js.

   WHAT IT COSTS, stated plainly: on a pale band the letter FACE is now close
   in value to the field behind it -- 1.11:1 at worst, measured -- so the word
   is held by its ring alone. With the flip it was near-black at 16:1 against
   that same field. The ring is what makes the claim, and on those grounds it
   has to be wide enough to be a background rather than a hairline; see the
   pixel floor on --ring in css/layout.css.

   `data-band-contrast` is still published by js/waves.js. Nothing here reads
   it any more -- it is kept because the measurement behind it is what a
   ground-aware ring WIDTH would key off, which is the open question on this
   treatment. */
.section__band {
  /* --paper-50, pure white, NOT the --cream-100 everything else on this site
     is set in. Cream is right for copy on a card, where it is the warm end of
     a paper-and-ink pair. Inside a ring it is not copy any more, it is the
     face of a sign, and the face wants the full value: white lifts the pair
     against the ring from 12.13:1 to 12.96:1 at worst, and stops the letter
     reading as slightly dirty next to the ring's hard edge. */
  --band-text: var(--paper-50);
  --band-outline: var(--band-ink);
}

/* ---- The outline's colour comes from the PALETTE, and from JS ----
   The two values above are the fallback, not the intent: they are the
   neutral pair, and they are what a band wears for the frame before it is
   first measured (and what it would wear with no JS at all, though the
   sections are fetched by js/main.js, so a page with no JS has no heading to
   outline either).

   The real value is written per band by _outlineFor in js/waves.js: the
   stack's own darkest entry behind cream type, its own lightest behind
   near-black. It has to come from JS because it depends on which PALETTE the
   section is drawn in, and the palettes live in wave-config.json rather than
   in CSS -- the same reason the wave colours themselves are not tokens. */

/* ---- Why the ink alone is not enough, below 1024px ----
   The flip picks ONE colour for the whole heading, and in flow mode there
   are frames where the heading has two behind it: the artwork follows scroll
   offset rather than a page turn, so a wave edge crosses the type on the way
   into every section. Over #C979DE and #38379D at once -- the pair behind
   "Resume / Skills & Education" as its section comes in -- the better of the
   two inks still only reaches 2.70:1.

   So the type carries a halo in whichever direction the ink is NOT, and this
   token is that direction. It is a token rather than two literals because a
   halo that stays dark while the ink goes dark is the original bug with
   extra steps.

   Opaque here; the shadows that use it take their own alphas, since a halo
   is a stack of four and one figure could not serve all of them. Used by
   .section__title / .section__subtitle in css/layout.css, where the
   arithmetic for those alphas is -- and where it is also confined to flow
   mode, which is the only place it is needed. */

#wave-art-box {
  position: fixed;
  inset: 0;
  z-index: 0;
  overflow: hidden;
  pointer-events: none;
}

/* The SVG is TALLER than the viewport: its viewBox is one layer, and a
   layer is 110% of a viewport high (matching the reference's 110vh
   images). That extra height is where an edge sits when it is parked below
   the screen. #wave-art-box clips it back to the viewport.

   Declared BEFORE the desktop rule below, which overrides its left and
   width at equal specificity -- in the other order this one wins and the
   whole composition slides 280px left, under the rail. */
#wave-art-box svg {
  position: absolute;
  top: 0;
  left: 0;
  width: 100%;
  height: 110%;
  display: block;
}

/* The COMPOSITION sits beside the sidebar; the CANVAS runs on under it.

   The composition's whole point is the cascade fanning out of the
   bottom-right corner, and it is balanced against the width it is drawn in
   -- laying it out across the full viewport put its left edge under a
   280px column and threw that balance off. So the SVG still starts at the
   sidebar's edge and draws exactly what it always did there, matching the
   #main offset in css/layout.css at the same breakpoint.

   But the sidebar is frosted glass now (css/nav.css), and glass over the
   bare backdrop colour is just a flat tint. So the box itself spans the
   whole viewport, the SVG is allowed to paint outside its own box, and
   each layer's flat run-out to the left (REACH_LEFT in js/wave-path.js)
   carries the field and the band on under the rail, where the blur softens
   them into the same wash the cards show.

   Below it there IS no rail -- the sidebar is a drawer that slides over the
   page (css/nav.css) -- so the art gets the full width back and is only
   inset from the top, by the bar. */
@media (min-width: 1024px) {
  #wave-art-box svg {
    left: var(--nav-width);
    width: calc(100% - var(--nav-width));
    overflow: visible;
  }
}

@media (max-width: 1023.98px) {
  #wave-art-box {
    top: var(--topbar-height);
  }
}

/* Each layer's bottom edge casts onto whatever is beneath it, which is the
   reference's layered cut-paper depth. A plain drop-shadow is enough here
   -- unlike earlier versions, where the stack was built by positioning and
   a layer's shadow would have fallen on the wrong side. Now the layer
   above is genuinely on top of the one below, so the shadow lands where it
   should. The other three edges sit outside the canvas and never cast.

   No transition here either: the shape itself is animated, in js/waves.js,
   by interpolating the edge and rebuilding the path each frame. There is no
   transform left for CSS to ease. */
/* The shadow itself is set inline per layer by layerShadow() in
   js/waves.js. Same colour throughout; only the opacity varies, because
   one value cannot work across this palette -- it reads on the light
   layers and fades out on the navy ones, which are also the layers closest
   together in value and so the ones that most need separating. */

/* Reduced motion is handled in js/waves.js, which snaps straight to the
   target shape instead of running the morph. */
