/* ============================================================================
   HYDRO2050 - LAYOUT
   ----------------------------------------------------------------------------
   Loaded on every page immediately after base.css. It holds the three systems
   that stop a page being a set of artboard coordinates: the stacking order,
   the vertical rhythm, and the decorative-art treatment.

   WHY THIS FILE EXISTS
   The build was extracted from a Paper file whose artboards are absolutely
   positioned, and that came through into the CSS and into inline style
   attributes. Art was pinned to 1440 page coordinates by subtracting half the
   artboard from fifty percent, paired with a literal `top:`. Sections carried
   fixed heights, and every sheet chose its own section padding, so nothing
   agreed with anything. At any width that was not exactly 1440 or 375 the
   art slid off the section it decorated, motifs were cut mid-shape at hard
   rectangle edges, and runs of empty ground opened between blocks.

   Nothing below refers to a page coordinate. Every value is either a token, a
   percentage of the element's own section, or a clamp between the two measured
   design widths.
   ========================================================================== */

:root {

  /* --- STACKING ORDER ------------------------------------------------------
     Six named layers, applied as a system rather than one element at a time.
     Before this, sixteen z-index values were scattered across nine sheets and
     the recurring "art at 0, a .wrap at 1" pair was rewritten per page. The
     numbers are small on purpose: every section that carries art also carries
     `isolation: isolate`, so these compete only with their own siblings and a
     section can never paint over its neighbour.

     The three chrome layers are the exception and are absolute, because the
     header, the drawer and the loading curtain compete with the whole page. */

  --z-art:      0;    /* decorative artwork behind a section's content       */
  --z-media:    1;    /* photography, video and the scrims that sit on them  */
  --z-content:  2;    /* type, controls, anything a visitor reads or presses */
  --z-lifted:   3;    /* content that straddles a section seam, e.g. the
                         enquiry card lifted across the footer edge          */

  --z-drawer:  55;    /* mobile drawer: below the bar it opens from          */
  --z-chrome:  60;    /* the sticky header, and anything it writes           */
  --z-curtain: 90;    /* the Home loading curtain, over the whole document   */
  --z-skip:   200;    /* skip link, above everything once it is focused      */

  /* --- VERTICAL RHYTHM -----------------------------------------------------
     One scale, named by the job a step does, matching the token file's
     convention of naming by role rather than by a size ladder.

     Each step is a clamp between the value measured on the 375 artboard and
     the value measured on the 1440 artboard, fluid in between, so a section
     boundary lands on the design at both design widths and interpolates at
     every width between them instead of stepping at a media query.

     The arithmetic, for a step running from `a` at 375 to `b` at 1440:
        slope = (b - a) / (1440 - 375)      expressed as vw
        base  = a - slope * 375             expressed as px
     so the clamp reads clamp(a, calc(base + slope), b). Each line below
     carries its own a and b as a comment; nothing here is a round number
     chosen for looking tidy.                                               */

  --rhythm-open:    clamp(32px, calc( 6.648px + 6.7606vw), 104px);  /*  32 ->104 */
  --rhythm-section: clamp(48px, calc(25.465px + 6.0094vw), 112px);  /*  48 ->112 */
  --rhythm-lg:      clamp(56px, calc(33.465px + 6.0094vw), 120px);  /*  56 ->120 */
  --rhythm-sm:      clamp(32px, calc(17.916px + 3.7559vw), 72px);  /*  32 -> 72 */
  --rhythm-row:     clamp(24px, calc(16.958px + 1.8779vw), 44px);  /*  24 -> 44 */
  --rhythm-row-end: clamp(28px, calc(18.141px + 2.6291vw), 56px);  /*  28 -> 56 */
  --rhythm-block:   clamp(24px, calc(18.366px + 1.5023vw), 40px);  /*  24 -> 40 */
  --rhythm-tight:   clamp(16px, calc(11.775px + 1.1268vw), 28px);  /*  16 -> 28 */

  /* The single largest run of empty ground the rhythm allows between two
     content blocks. tools/rhythm.mjs evaluates the same clamp and fails any
     page that exceeds it, so the number is enforced rather than asserted. */
  --rhythm-max-void: clamp(72px, calc(55.070px + 4.5070vw), 120px); /*  72 ->120 */

  /* --- COLUMN-ALIGNED PADDING ---------------------------------------------
     For a full-bleed block that has to put its type back on the content
     column. `.wrap` cannot be used inside one, because the block's own ground
     runs the full width and only its padding can hold the column.

     The artboard expresses these as a distance from the PAGE edge, 64 for the
     navy band and 80 for the footer, and at exactly 1440 that reads as right:
     the column starts at 64, so 64 lands on it. At any wider viewport a page
     coordinate is only a page coordinate, and at 1920 the band's headline sat
     240px left of the column the section above it used, which is the "photo
     bands stepping in and out at odd notches" the review called out.

     Percentages of the block, not vw: `.wrap` centres in the content box, and
     a vw-based figure is a scrollbar's width out from it. */
  --pad-column:    calc(50% - var(--content) / 2);          /* on the column   */

  /* --pad-column-in USED TO ADD 16px AND NO LONGER DOES.

     The artboard draws the closer row, the contact plinth, the link lanes and
     the legal strip at 80 from the page edge while every section above them
     sits on the 64 column, so the footer's whole left edge stood 16px inside
     the page's. Measured at 1440 on all thirteen pages: `.closer__headline`
     at x80, `.plinth__brand` at x80, the legal strip at x80, against a
     `.wrap` at x64 and a `.band__inner` at x64. Alignment before anything
     else: a 16px shear between the last section of a page and the first line
     of its footer is the kind of thing a reader registers without being able
     to name it, and it is on every page of the site at once.

     Kept as its own name rather than folded into --pad-column because the
     four rules in chrome.css that use it mean "the footer's own indent",
     which is a different statement from "the content column" even while the
     two resolve to the same number. If the footer ever earns its inset back,
     this is the one line that returns it to all thirteen pages.             */
  --pad-column-in: var(--pad-column);

  /* --- ARTBOARD PIXEL TO COLUMN FRACTION -----------------------------------
     One multiplier, so a width measured off the 1440 artboard converts to a
     fluid share of the content column mechanically rather than by hand:

        width: calc(376 * var(--col));
        grid-template-columns: minmax(280px, calc(376 * var(--col))) ... ;

     At 1440 that resolves to its measured pixel value exactly, because
     --content is 1312 there and 1312 / 1312 is 1. Either side of 1440 the
     element takes its proportional share of the width the column gains or
     loses instead of standing still, which is the whole of the defect the
     client reported by dragging his window.

     It lives here rather than being rewritten per sheet because the same
     figures recur: 376 is a heading column in the industry family, in
     how-it-works and on Contact; 320 is one on the legal pages and in the
     how-it-works steps. Always pair it with a `minmax()` floor. A fraction
     with no floor collapses its column under its own content in the last
     stretch above the breakpoint, which is what the floors in .hero__stage
     and .partner are there to stop.                                         */
  --col: calc(var(--content) / 1312);

  /* The height an anchor target has to clear to sit below the sticky header.
     It is the INNER bar's height, not the whole <header>'s: the header carries
     32px of outer padding at rest and drops it once stuck, and a target only
     ever lands while the header is stuck. 84 above the breakpoint, 64 below.

     Declared here so a page whose JavaScript never runs still offsets its
     anchors correctly. js/motion.js overwrites it on <html> with the bar's
     real measured height, which is what keeps this honest if the nav ever
     wraps to two lines. */
  --header-h: 84px;
}

@media (max-width: 899px) {
  :root { --header-h: 64px; }
}

/* ============================================================================
   SECTION TREATMENTS
   ----------------------------------------------------------------------------
   Six, and six is the whole vocabulary. Every bleed, clip and decorative
   effect on the site is one of them. If a section needs a seventh, that is a
   signal the design has drifted, not that the list is short.

   READ THIS BEFORE USING THEM. The thirteen pages were built before this file
   existed and each implements its treatment through its own page class, so at
   the time of writing NOTHING in the site markup carries a `.sec` class. The
   classes below are the canonical definition and the place to add a new
   section; the per-page rules are the existing implementations of the same six
   shapes. Their correspondence:

     1. plain            .p09-open, .p10-open, .p02-open, .legal-open, ...
     2. bled photography .bleed in css/base.css
     3. off one edge     .bleed-right / .bleed-left in css/base.css
     4. full-bleed band  .band in css/components.css, .hardware in css/home.css
     5. carrying art     .p08-open, .p01-process, .ind-body, .p09-team, ...
     6. media layer      .band__photo / .band__scrim, .hero__media

   That gap is not cosmetic and it cost something. Treatment 5 below carries
   the content lift that keeps an art layer behind its own section's type, and
   because each page writes its own version of that rule, one page was written
   without it and had the wave painting over its h1 at every width from 900 up.
   Migrating the pages onto these classes is the way that stops being possible;
   until then, tools/audit.mjs asserts the paint order on every page at every
   width, which is why the defect is now caught rather than merely fixed.
   ========================================================================== */

/* 1. PLAIN. Content on the 1312 column, rhythm padding, and a stacking context
      of its own so nothing inside it can paint over a neighbouring section.
      `isolation: isolate` is the systemic half of the layering fix: it means a
      z-index inside a section is a statement about that section only.        */
.sec {
  position: relative;
  isolation: isolate;
  padding-block: var(--rhythm-section);
}
.sec--open  { padding-block: var(--rhythm-open) 0; }
.sec--tight { padding-block: var(--rhythm-sm); }
.sec--lg    { padding-block: var(--rhythm-lg); }
.sec--flush-top    { padding-block-start: 0; }
.sec--flush-bottom { padding-block-end: 0; }

/* 2. BLED PHOTOGRAPHY. The photograph widens from the 1312 type column to the
      1376 bleed, leaving a 32px margin. Type never follows it out.          */
.sec__bleed { width: var(--bleed); margin-inline: auto; }

/* 3. PHOTOGRAPHY OFF ONE PAGE EDGE. The design's hero and body photographs
      each run off one side of the page, so only the corners still on screen
      are rounded. These are base.css's .bleed-right and .bleed-left, folded
      in here rather than duplicated: the classes keep their names, and this
      block is the documentation of what treatment they are.

      They are declared with `width` plus a negative margin rather than with a
      viewport-width transform so they cost no compositor layer and so the
      radius applies to the real box.                                        */
.sec__edge-right,
.sec__edge-left {
  width: calc(50vw + var(--content) / 2);
  overflow: hidden;
}
.sec__edge-right { margin-right: calc(50% - 50vw); border-radius: var(--r-photo) 0 0 var(--r-photo); }
.sec__edge-left  { margin-left:  calc(50% - 50vw); border-radius: 0 var(--r-photo) var(--r-photo) 0; }

@media (max-width: 899px) {
  .sec__edge-right, .sec__edge-left {
    width: 100vw;
    margin-inline: calc(50% - 50vw);
    border-radius: 0;
  }
}

/* 4. FULL-BLEED BAND. Ground colour runs the full viewport width and the type
      inside it holds the content column. The navy closing band and the near
      black hardware band are both this shape.                               */
.sec--band {
  width: 100vw;
  margin-inline: calc(50% - 50vw);
  padding-inline: var(--gutter);
}

/* 5. SECTION CARRYING DECORATIVE ART. Declares the stacking context the .art
      layer below is positioned inside, and lifts the section's own children
      above it without each page having to write the pair again. Only DIRECT
      children are lifted, so a `.art` nested deeper still sits behind.      */
.sec--art > *:not(.art) { position: relative; z-index: var(--z-content); }

/* 6. MEDIA LAYER. A photograph, video or scrim that fills its section behind
      the type. Named so a scrim and the thing it darkens cannot end up on the
      same layer by accident, which is how a scrim ends up over its own type. */
.sec__media { position: absolute; inset: 0; z-index: var(--z-media); pointer-events: none; }

/* ============================================================================
   THE DECORATIVE-ART TREATMENT
   ----------------------------------------------------------------------------
   One class, `.art`, with modifiers. Its rules, in order of how much trouble
   breaking each one caused:

   1. It is positioned against ITS OWN SECTION, never against a page
      coordinate. Every offset below is a percentage or an inset keyword.
   2. Its size is expressed against its section, never as an artboard pixel
      width. `cover` and `contain` do the work; --art-scale tunes the crop.
   3. IT ALWAYS CARRIES AN EDGE FADE. The pattern files are opaque JPEGs on a
      white ground, so at 0.2 to 0.3 opacity in a plain box they painted a
      rectangle with four hard edges and a motif sliced mid-shape wherever the
      box ended. The mask dissolves the artwork to transparent before it
      reaches its own edge, on every side. There is no modifier that turns
      this off.
   4. It never paints behind body text at an opacity that costs contrast.
      That is defect 5 in DESIGN-NOTES and a standing rule. Anything placed
      behind type goes through tools/over-image-contrast.mjs first.
   5. It is aria-hidden and pointer-events: none.

   `mix-blend-mode: multiply` is the fourth line of defence: the pattern files
   carry a white ground, and multiply drops pure white out entirely, so the
   layer can never grey a section it sits on. Over a dark ground the same job
   is done by `screen`, which is .art--on-dark.
   ========================================================================== */

.art {
  position: absolute;
  z-index: var(--z-art);
  pointer-events: none;
  user-select: none;

  /* Default: fills its section. Every modifier below changes these four and
     nothing else, so an art layer's position is always readable in one line. */
  inset: var(--art-top, 0) var(--art-right, 0) var(--art-bottom, 0) var(--art-left, 0);

  background-repeat: no-repeat;
  background-position: var(--art-pos, 50% 50%);
  background-size: var(--art-size, cover);

  opacity: var(--art-opacity, 0.22);
  mix-blend-mode: multiply;

  /* THE EDGE FADE. `farthest-side` draws an ellipse touching the far side on
     each axis, so one gradient fades all four edges of any aspect ratio. Solid
     to --art-core, gone by --art-edge, which leaves the outer fifth of the box
     as dead margin the motif never reaches. */
  -webkit-mask-image: radial-gradient(farthest-side at var(--art-focus, 50% 50%),
                      #000 0%, #000 var(--art-core, 38%), transparent var(--art-edge, 82%));
          mask-image: radial-gradient(farthest-side at var(--art-focus, 50% 50%),
                      #000 0%, #000 var(--art-core, 38%), transparent var(--art-edge, 82%));
}

/* Over navy and near-black grounds. Multiply would erase the artwork; screen
   keeps it and drops the black instead. */
.art--on-dark { mix-blend-mode: screen; }

/* ---- WHICH ARTWORK ------------------------------------------------------
   The file is chosen by a class, not by an inline background-image, so no page
   carries a path or a coordinate in a style attribute. */
.art--wave      { background-image: url(../assets/img/pattern-wave.jpg); }
.art--dots      { background-image: url(../assets/img/pattern-dots.jpg); }
.art--bluewhite { background-image: url(../assets/img/pattern-bluewhite.jpg); }
.art--a         { background-image: url(../assets/img/pattern-a.jpg); }
.art--b         { background-image: url(../assets/img/pattern-b.jpg); }
.art--c         { background-image: url(../assets/img/pattern-c.jpg); }

/* Page 08's opening mirrors Page 01's. Paper's node is missing the scaleX(-1)
   that makes the two read differently; this supplies it. Recorded defect 2. */
.art--mirror { transform: scaleX(-1); }

/* ---- WHERE IN ITS SECTION -----------------------------------------------
   Percentages and inset keywords only. Each modifier also moves the mask's
   focus, so the fade stays centred on the artwork rather than on the box: a
   layer anchored to the right of its section fades leftward from the right.  */

.art--top    { --art-bottom: 40%; --art-focus: 50% 30%; }
.art--bottom { --art-top: 40%;    --art-focus: 50% 70%; }

.art--right  { --art-left: 34%; --art-right: -8%; --art-focus: 62% 50%; }
.art--left   { --art-right: 34%; --art-left: -8%; --art-focus: 38% 50%; }

.art--top-right    { --art-left: 34%; --art-right: -8%; --art-bottom: 42%; --art-focus: 62% 30%; }
.art--top-left     { --art-right: 34%; --art-left: -8%; --art-bottom: 42%; --art-focus: 38% 30%; }
.art--bottom-right { --art-left: 34%; --art-right: -8%; --art-top: 42%;    --art-focus: 62% 70%; }
.art--bottom-left  { --art-right: 34%; --art-left: -8%; --art-top: 42%;    --art-focus: 38% 70%; }

/* A band of artwork sitting across the whole section, shallower than it is
   wide. Used where the artboard drew a wide strip rather than a field. */
.art--strip { --art-top: 0; --art-bottom: auto; height: clamp(140px, 26vw, 320px); }

/* ---- HOW BIG ------------------------------------------------------------
   `cover` is the default and is what keeps the motif's scale sane at every
   width. --art-scale crops further INTO the artwork, so the motif reads at a
   larger size on a narrow section without the layer's box changing at all. */
.art--zoom  { --art-size: 170% auto; }
.art--zoom2 { --art-size: 260% auto; }
.art--wide  { --art-size: 100% auto; }

/* ---- HOW STRONG ---------------------------------------------------------
   Four steps, matching the four opacities the artboards actually use. */
.art--faint  { --art-opacity: 0.12; }
.art--soft   { --art-opacity: 0.18; }
.art--medium { --art-opacity: 0.26; }
.art--strong { --art-opacity: 0.34; }

/* ---- MOBILE -------------------------------------------------------------
   Standing rule on this project, carried over unchanged: no decorative
   artwork paints behind body copy below the breakpoint. A section's art layer
   is kept only where it sits beside content rather than behind it, which is
   the .art--keep-mobile opt-in; everything else stops at the breakpoint.    */
@media (max-width: 899px) {
  .art:not(.art--keep-mobile) { display: none; }
  .art--keep-mobile { --art-opacity: 0.14; }
}

/* ============================================================================
   ANCHOR TARGETS
   ----------------------------------------------------------------------------
   The CSS half of "a sticky header must not cover the thing an anchor points
   at". The JavaScript half is goTo() in js/motion.js, which reads the bar's
   real height at call time. Both are needed: this one covers a plain browser
   jump, a page loaded with a #hash already in the URL, and the non-Lenis
   scrollIntoView path.
   ========================================================================== */

#main,
#main [id] {
  scroll-margin-top: calc(var(--header-h) + 24px);
}

/* ============================================================================
   REDUCED MOTION
   ----------------------------------------------------------------------------
   Nothing in this file animates. The mask and the blend mode are static, so
   they are correct under prefers-reduced-motion with no guard of their own.
   The guard that matters for the art layer is the contrast one, which is
   measured rather than declared: see tools/over-image-contrast.mjs.
   ========================================================================== */
