/* ============================================================================
   HYDRO2050 - THE INDUSTRY PAGE FAMILY
   ----------------------------------------------------------------------------
   One sheet for Pages 03, 04, 05 and 06: Aquaculture and food production,
   Dams lakes and ponds, Drinking water, Wastewater treatment. The four
   artboards are the same page with different content, so they are one
   stylesheet rather than four.

   Measured from Paper artboards 7FV-0, 7KT-0, 7OW-0 and 7SW-0 (1440) and
   AXS-0 (375). Page 03 is the only one of the four with a mobile artboard.
   Pages 04, 05 and 06 take their mobile layout from AXS-0, which is what
   site/DESIGN-NOTES.md records as the derivation rule for this family.

   The shell, measured once and true on all four:
     opening   padding-top 104, gap 28, h1 capped 900, deck capped 680,
               hero photograph 1376 x 420 bleeding right, 32 below the deck
     body      padding 104 / 112
     row       1312 wide, 1px --rule top border, padding 44 / 56, gap 48,
               a 376 heading column and a 680 copy column
     photo     1376 x 380 bleeding LEFT, 40 above and below
     band      the shared .band closing component
   ========================================================================== */

/* ===== OPENING =========================================================== */

.ind-open { padding-top: var(--rhythm-open); position: relative; isolation: isolate; overflow-x: clip; }
.ind-open__eyebrow { margin-bottom: 28px; }
.ind-open__title { max-width: 900px; margin-bottom: 28px; }
.ind-open__deck  { max-width: 680px; }

/* 28 flex gap plus the photograph wrapper's own 32 padding-top. */
/* 1376 x 420 on the artboard. Held as a ratio with a floor, so the band keeps
   its proportion between the two design widths instead of staying 420 tall in
   a 375 page. 190 is the height the 375 artboard draws. */
.ind-open__photo {
  position: relative;
  z-index: var(--z-content);
  margin-top: var(--rhythm-row-end);
  aspect-ratio: 1376 / 420;
  min-height: 190px;
  max-height: 420px;
}
.ind-open__photo img { width: 100%; height: 100%; object-fit: cover; }

/* ===== THE FOOD PRODUCTION COLLAGE =======================================
   Client pack of 10 September 2026, ask 26: "Can you do a collage of images,
   greenhouse; fish tank; dairy; piggery". He wrote it directly under the Food
   Production heading and above the first sub-heading, which is where the page
   opening sits, so the collage IS the opening rather than a fifth band added
   under it.

   IT REPLACES `aquaculture-hero.jpg`, one photograph of open-water pens. That
   picture answered a page called Horticulture & Aquaculture; this page now
   covers four settings and opening on one of them under-sells the other three.
   The file stays on disk and is recoverable from git.

   THE SLOT DOES NOT MOVE. Same `.bleed-right`, same 1376 by 420 band, same
   190 floor and 420 cap as `.ind-open__photo` above, so the page's silhouette
   is unchanged and only the fill is different. Four equal tracks, a 2px gutter
   in --page between them so the four read as four rather than as one long
   picture, and `object-fit: cover` on each tile.

   2 BY 2 BELOW 900, where four across would print a 93px tile at 375 and none
   of the four subjects survives that. The aspect ratio is dropped there and
   each tile takes 4:3 of its own half column instead, which is 450 wide at
   899 and 187 at 375. */

.fp-collage {
  position: relative;
  z-index: var(--z-content);
  margin-top: var(--rhythm-row-end);
  aspect-ratio: 1376 / 420;
  min-height: 190px;
  max-height: 420px;
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 2px;
  background: var(--page);
}
.fp-collage img { display: block; width: 100%; height: 100%; object-fit: cover; }

@media (max-width: 899px) {
  .fp-collage {
    aspect-ratio: auto;
    max-height: none;
    min-height: 0;
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }
  .fp-collage img { aspect-ratio: 4 / 3; height: auto; }
}

/* ===== BODY ============================================================== */

.ind-body {
  padding-top: var(--rhythm-open);
  padding-bottom: var(--rhythm-section);
  position: relative;
  isolation: isolate;
  /* The decorative artwork is drawn wider than the page on purpose, so the
     section clips it horizontally. `clip` rather than `hidden`: hidden would
     make this a scroll container and trap the reveal observer's rounding. */
  overflow-x: clip;
}

/* The decorative artwork these artboards place behind their sections is the
   shared .art treatment in css/layout.css: anchored to the section it
   decorates, sized against it, and masked so no motif is cut mid-shape. It
   sits under body copy, so every page carrying one is measured with
   tools/over-image-contrast.mjs against real rendered pixels. */
.ind-open > .wrap,
.ind-body > .wrap,
.ind-body > .ind-photo { position: relative; z-index: var(--z-content); }

/* ===== ROW =============================================================== */

.row {
  border-top: 1px solid var(--rule);
  padding-top: var(--rhythm-row);
  padding-bottom: var(--rhythm-row-end);
  display: grid;
  /* 376 + 48 + 680 = 1104 inside 1312. The artboard leaves the remaining 208
     empty rather than stretching the copy column, so the grid names a third
     track for it instead of letting the copy column absorb it.

     THE HEADING LANE IS A FRACTION, NOT A LITERAL. 376 / 1312 = 28.659%, so
     this resolves to the artboard's own 376 at 1440 and gives way with the
     page below it. As a literal it did not: the row is two columns from 900
     up, and at 900 the column is 772, which left 376 for a heading and 300
     for the copy the heading introduces. Measured at 900 / 940 / 1000 the
     copy ran 300 / 340 / 400 against a heading lane that never moved, so the
     subordinate column was the wider one across a 400px band of widths.
     The floor is the one partners.css already carries, for the same reason:
     below 280 a 34px heading breaks a short word across two lines. */
  grid-template-columns: minmax(280px, 28.659%) minmax(0, 680px) minmax(0, 1fr);
  column-gap: 48px;
}
.row--closed { border-bottom: 1px solid var(--rule); }

.row__title {
  font-family: var(--font-display);
  font-size: 34px;
  line-height: 42px;
  letter-spacing: -0.02em;
  font-weight: var(--wt-medium);
  color: var(--brand-navy);
}

.row__copy { display: flex; flex-direction: column; gap: 24px; }
.row__copy--tight { gap: 20px; }

.row__copy > p {
  font-size: 18px;
  line-height: 30px;
  color: var(--ink);
}
/* Written as `> p.row__note`, specificity 0,2,1, because the plain
   `.row__note` at 0,1,0 lost to `.row__copy > p` above it at 0,1,1 and the
   note rendered as body copy: 18 / 30 in --ink rather than 16 / 26 muted. */
.row__copy > p.row__note { font-size: 16px; line-height: 26px; color: var(--ink-muted); }

/* ===== BODY PHOTOGRAPH ===================================================
   1376 inside a 1312 column, pushed to the right edge, so it overhangs the
   LEFT page edge by 64 and only its right corners are rounded. The mirror of
   the hero. */

.ind-photo {
  margin-block: var(--rhythm-block);
  aspect-ratio: 1376 / 380;
  min-height: 180px;
  max-height: 380px;
}
.ind-photo img { width: 100%; height: 100%; object-fit: cover; }

/* ===== DEFINITION LIST ===================================================
   Pages 05 and 06 use a 220 term against a 436 definition; Page 04's named
   sites use 260 against 396 with a shorter row. Both are the same component. */

.deflist { padding-top: 8px; }
/* The first list in a row sits under the row's own heading, which already
   carries the gap, so it does not add a second one. */
.deflist--flush { padding-top: 0; }
/* The term lane is a fraction of the copy column for the reason .row's
   heading lane is: 220 / 680 = 32.353%, which is the artboard's 220 at the
   artboard's 680 and gives way below it. As a literal 220 inside a copy
   column that is 396 at 900, the definition got 152 and the terms were the
   wider half of a term-and-definition pair. The floor keeps a two-word term
   on two lines rather than four. */
.deflist__row {
  display: grid;
  grid-template-columns: minmax(150px, 32.353%) minmax(0, 1fr);
  gap: 24px;
  padding-block: 18px;
  border-top: 1px solid var(--rule);
}
.deflist__row:last-child { border-bottom: 1px solid var(--rule); }
.deflist__term {
  font-family: var(--font-display);
  font-size: 16px;
  line-height: 26px;
  font-weight: var(--wt-medium);
  color: var(--brand-navy);
}
.deflist__def { font-size: 16px; line-height: 26px; color: var(--ink); }

/* 260 / 680 = 38.235%. This is the row that failed hardest as a literal: at
   900 the definition track resolved to 16px, so "Municipal lake, dense algal
   bloom. Has a case study." set one word per line and ran past the column's
   own rule. */
.deflist--sites .deflist__row { grid-template-columns: minmax(160px, 38.235%) minmax(0, 1fr); padding-block: 14px; }
.deflist--sites .deflist__def { color: var(--ink-muted); }   /* 5.51:1 on page */

/* ===== BENEFITS LIST =====================================================
   Page 03. A plus marker in a fixed 12px slot so the labels form one lane,
   then the result. The 375 artboard draws this as a disclosure; see the
   mobile block at the foot of this file. */

.benefits { padding-top: 8px; }
.benefits__label { display: block; padding-bottom: 14px; }
.benefits__row {
  display: grid;
  grid-template-columns: 12px minmax(0, 1fr);
  gap: 12px;
  padding-block: 12px;
  border-top: 1px solid var(--rule);
}
.benefits__row:last-child { border-bottom: 1px solid var(--rule); }
.benefits__mark { color: var(--azure-ink); font-size: 16px; line-height: 26px; }
.benefits__text { font-size: 16px; line-height: 26px; color: var(--ink); }

/* Desktop shows the list open under an eyebrow. The summary is the mobile
   control and does not exist here. */
.benefits > summary { display: none; }

/* ===== FIGURE LIST =======================================================
   Page 06's four published energy figures, side by side rather than one
   picked. The numeral sits in a fixed 112 slot so the captions form a lane.

   UNUSED SINCE 20 SEPTEMBER 2026. The client asked for that section to come off
   the wastewater page and it was the only thing on the site using these rules.
   Kept rather than deleted, the same call `.panel__source` above got: the
   section is one revert away and re-deriving the 112 lane is a worse trade
   than a few lines of dead CSS. Delete this block and its mobile step below
   once the removal is confirmed. */

.figlist__row {
  display: grid;
  grid-template-columns: 112px minmax(0, 1fr);
  gap: 20px;
  align-items: baseline;
  padding-block: 16px;
  border-top: 1px solid var(--rule);
}
.figlist__row:last-child { border-bottom: 1px solid var(--rule); }
/* Paper sets these in Archivo 100, which is not a Hydro2050 brand face and is
   left over from the superseded mockup. Montserrat 300 is the brand face's
   hairline weight, at the measured size, leading and tracking. */
.figlist__fig {
  font-family: var(--font-display);
  font-size: 28px;
  line-height: 32px;
  letter-spacing: -0.04em;
  font-weight: var(--wt-light);
  color: var(--brand-navy);
}
.figlist__cap { font-size: 16px; line-height: 26px; color: var(--ink-muted); }

/* ===== SLAB ==============================================================
   Pages 04 and 05. Tinted rows with no gap and no radius, so they read as one
   continuous panel rather than as separate cards. */

.slab { display: flex; flex-direction: column; }
.slab__item {
  display: grid;
  gap: 24px;
  padding: 20px 24px;
  background: var(--surface);
}
.slab--numbered .slab__item { grid-template-columns: 12px minmax(0, 1fr); }
/* 200 against the item's own 632 content box, which is the 680 copy column
   less this item's 24px padding either side. Same fraction treatment as the
   definition list above. */
.slab--termed   .slab__item { grid-template-columns: minmax(140px, 31.646%) minmax(0, 1fr); }
.slab__n { color: var(--azure-ink); font-size: 16px; line-height: 26px; }
.slab__term {
  font-family: var(--font-display);
  font-size: 16px;
  line-height: 26px;
  font-weight: var(--wt-medium);
  color: var(--azure-ink);       /* 4.53:1 on --surface */
}
.slab__body { font-size: 16px; line-height: 26px; color: var(--ink); }

/* ===== PANEL ============================================================= */

.panel {
  background: var(--surface);
  border-radius: 8px;
  padding: 32px;
  display: flex;
  flex-direction: column;
  gap: 20px;
}
.panel__body { font-size: 16px; line-height: 26px; color: var(--ink); }

/* ===== FIGURES =========================================================== */

/* Paper sets both of these in Archivo 100 at 56px. Substituted as above. */
.stats { display: flex; gap: 56px; flex-wrap: wrap; }
.stat { display: flex; flex-direction: column; gap: 6px; }
.stat__line { display: flex; align-items: baseline; gap: 10px; flex-wrap: wrap; }
.stat__value {
  font-family: var(--font-display);
  font-size: 56px;
  line-height: 56px;
  letter-spacing: -0.06em;
  font-weight: var(--wt-light);
  color: var(--brand-navy);
}
.stat__to {
  font-family: var(--font-display);
  font-size: 18px;
  line-height: 24px;
  font-weight: var(--wt-medium);
  color: var(--ink-muted);
}
.stat__label { font-size: 16px; line-height: 24px; color: var(--ink-muted); }
/* NOTHING USES `.panel__source` AS OF 18 SEPTEMBER 2026. The client's pack
   of 13 September took the source line off five case studies, and the
   reading taken on 18 September took the last two off as well, since all
   seven cited the same document of his own. The rule is kept rather than
   deleted because that reading is his to reverse, and a reversal that has
   to re-derive a 616 measure is a worse trade than five lines of dead CSS.
   If he confirms the removal, this and its mobile step below come out. */
.panel__source { font-size: 16px; line-height: 24px; color: var(--ink-muted); max-width: 616px; }

.figure-inline {
  display: flex;
  align-items: baseline;
  gap: 16px;
  padding-top: 8px;
  flex-wrap: wrap;
}
.figure-inline__val {
  font-family: var(--font-display);
  font-size: 56px;
  line-height: 56px;
  letter-spacing: -0.06em;
  font-weight: var(--wt-light);
  color: var(--brand-navy);
}
/* --ink-muted-art, not --ink-muted. This caption sits over the brand's dot
   field, where the darkest pixel the pattern puts behind it measured
   rgb(226,228,236) and left --ink-muted at 4.34:1, under the 4.5 body floor.
   The system already defines this darker sibling for exactly this case. */
.figure-inline__cap {
  font-size: 16px;
  line-height: 24px;
  color: var(--ink-muted-art);
  max-width: 340px;
}

/* ===== THE ROLLING RESULTS, wastewater only ==============================
   Added 20 September 2026 on the client's instruction to put the home page's
   rotating figure on this page in place of the 800+ mV oxidation reduction
   potential.

   IT IS DRIVEN BY js/evidence.js UNCHANGED. That file needs exactly two class
   names, `.stat__row`, which it toggles `is-swapping` on, and `.stat__dot`,
   which it creates for each result. Everything else it reaches by
   `data-stat-*`. So those two are declared here and the rest of the block is
   named `.rollup__*`.

   WHY NOT REUSE `.stat__*` WHOLESALE. This file already owns `.stat`,
   `.stat__label`, `.stat__line`, `.stat__value` and `.stat__to` for the paired
   before-and-after figures on the case study family, at a different size, a
   different weight and a different colour. Two unrelated components sharing
   three class names is a defect waiting on whichever is edited first.

   THE FIGURE IS 56 / 56, which is `.figure-inline__val` exactly, because this
   block stands where that one stood and the page's figure scale should not
   move. The home page runs the same component at a 56 to 96 clamp, which is
   drawn for a column beside an 88px display heading and would be the largest
   type on this page. */
.rollup {
  display: flex;
  flex-direction: column;
  gap: 10px;
  padding-top: 12px;
}
.rollup__label {
  font-family: var(--font-display);
  font-size: 15px;
  font-weight: var(--wt-semi);
  letter-spacing: 0.04em;
  text-transform: uppercase;
  color: var(--brand-blue-cta, #00A0E6);
  min-height: 24px;
}
.rollup__row {
  display: flex;
  align-items: baseline;
  gap: 16px;
  flex-wrap: wrap;
  min-height: 60px;
  align-content: center;
}
/* Lining figures give 182,385 and 936 different glyph advances and the arrow
   slides between results. Tabular holds it still. */
.rollup__val {
  font-family: var(--font-display);
  font-size: 56px;
  line-height: 56px;
  letter-spacing: -0.06em;
  font-weight: var(--wt-light);
  color: var(--brand-navy);
  font-variant-numeric: tabular-nums;
  font-feature-settings: 'tnum' 1;
}
.rollup__to { display: none; }
.rollup__arrow { color: var(--brand-turq); flex-shrink: 0; align-self: center; }
.rollup__unit {
  font-family: var(--font-display);
  font-size: 20px;
  font-weight: var(--wt-medium);
  line-height: 24px;
  color: var(--ink-muted);
}
.rollup__unit[hidden] { display: none; }
.rollup__note {
  max-width: 340px;
  font-size: 16px;
  line-height: 24px;
  color: var(--ink-muted-art);   /* the dot field sits behind this block too */
  min-height: 48px;
}
/* Opacity only. The row shares a baseline with the list above it and anything
   that moves vertically reads as the section twitching every five seconds. */
.rollup .stat__row,
.rollup__note { transition: opacity 200ms var(--ease-out); }
.rollup .stat__row.is-swapping,
.rollup__note.is-swapping { opacity: 0; }

/* The 28 x 20 button is the tap target and the 3px bar inside it is the mark,
   the same split the home page makes: six lozenges at the tap floor under a
   56px figure would outweigh it. */
/* GAP 16, NOT 8, AND THE REASON IS THE TAP TARGET, not the spacing. The
   44 x 44 box below is centred on a 28px bar, so the pitch between bars is
   what caps each target's width: at an 8px gap the pitch is 36 and the next
   dot's box starts there. 16 makes the pitch exactly 44, so each target is a
   full 44 x 44 with no dead space between them. This is the measurement
   css/home.css already made for the same control; it is repeated here rather
   than inherited because that file does not load on this page. */
.rollup__dots { display: flex; gap: 16px; padding-top: 4px; }
.rollup__dots .stat__dot {
  position: relative;
  width: 28px;
  height: 20px;
  padding: 8px 0;
  border: 0;
  background: none;
  cursor: pointer;
  -webkit-appearance: none;
  appearance: none;
}
.rollup__dots .stat__dot::before {
  content: '';
  display: block;
  height: 3px;
  border-radius: 2px;
  background: #00477B33;
  transition: background var(--dur-hover) var(--ease-out);
}
/* The button is 28 x 20 and the visible bar is 3px of that, so the whole
   control is under the 44px tap floor on both axes. The target grows around
   the mark with a transparent pseudo-element, centred, so nothing in the
   layout moves and six fat lozenges do not appear under the figure. ::before
   is already the bar, so this is ::after. */
.rollup__dots .stat__dot::after {
  content: '';
  position: absolute;
  left: 50%;
  top: 50%;
  width: 44px;
  height: 44px;
  translate: -50% -50%;
}
.rollup__dots .stat__dot:hover::before { background: #00477B66; }
.rollup__dots .stat__dot[aria-current="true"]::before { background: var(--brand-turq); }
.rollup__dots .stat__dot:focus-visible { outline: 3px solid var(--focus); outline-offset: 2px; }

@media (prefers-reduced-motion: reduce) {
  .rollup .stat__row,
  .rollup__note,
  .rollup__dots .stat__dot::before { transition: none; }
}

/* ===== MOBILE ============================================================
   From artboard AXS-0, "Page 03, Aquaculture and food production 375". The
   other three pages in the family have no 375 artboard and take these values,
   which is recorded in DESIGN-NOTES.md.

   The measured changes: the two columns become one, the row's 1px --rule top
   border becomes a 2px --brand-azure one, photography goes edge to edge with
   its height capped and no radius, and the 56px figures drop to 34 / 42 in
   Montserrat 500 rather than the desktop hairline. */

@media (max-width: 899px) {
  .ind-open { padding-top: var(--rhythm-open); }
  .ind-open__eyebrow { margin-bottom: 14px; }
  .ind-open__title { margin-bottom: 14px; }
  .ind-open__photo { margin-top: var(--rhythm-tight); aspect-ratio: auto; height: 190px; max-height: none; }

  .ind-body { padding-top: 0; padding-bottom: 0; }
  /* The artboards do not carry the desktop artwork at 375, and this build adds
     no parallax and no pattern behind body copy below the breakpoint. */
  .ind-art { display: none; }

  .row {
    display: block;
    border-top: 2px solid var(--brand-azure);
    padding-top: 18px;
    padding-bottom: 0;
    margin-top: 44px;
  }
  .row--closed { border-bottom: 0; }
  .ind-body .row:first-of-type { margin-top: 48px; }

  .row__title { font-size: 22px; line-height: 28px; margin-bottom: 12px; }
  .row__copy { gap: 12px; }
  .row__copy > p { font-size: 17px; line-height: 26px; }
  .row__copy > p.row__note { font-size: 15px; line-height: 23px; }

  .ind-photo { margin-block: var(--rhythm-row) 0; aspect-ratio: auto; height: 180px; max-height: none; }

  /* Lists lose their fixed lanes and stack, because a 220px term slot leaves
     115px for its definition at 375. */
  .deflist, .deflist--sites { padding-top: 6px; }
  .deflist__row,
  .deflist--sites .deflist__row { display: block; padding-block: 14px; }
  .deflist__term { margin-bottom: 2px; }
  .deflist__def { font-size: 15px; line-height: 23px; }

  .figlist__row { display: block; padding-block: 14px; }
  .figlist__fig { font-size: 28px; line-height: 34px; }
  .figlist__cap { font-size: 15px; line-height: 23px; }

  .slab--termed .slab__item { display: block; }
  .slab--termed .slab__term { margin-bottom: 2px; }
  .slab__item { padding: 16px 18px; }
  .slab__body { font-size: 15px; line-height: 23px; }

  .panel {
    border-radius: 0;
    padding: 20px 18px 22px;
    gap: 14px;
    margin-top: 6px;
  }
  .panel__body, .panel__source { font-size: 15px; line-height: 23px; }

  /* The 56px hairline figures do not survive at 375: the artboard sets them
     at 34 / 42 in Montserrat 500, one value per line rather than a row. */
  .stats { flex-direction: column; gap: 14px; }
  .stat { gap: 2px; }
  .stat__line { display: block; }
  .stat__value, .figure-inline__val {
    font-size: 34px;
    line-height: 42px;
    letter-spacing: -0.03em;
    font-weight: var(--wt-medium);
  }
  .stat__to { font-size: 34px; line-height: 42px; color: var(--brand-navy); }
  .stat__label { font-size: 13px; line-height: 16px; }

  .figure-inline { display: block; padding-top: 6px; }
  .figure-inline__cap { font-size: 13px; line-height: 20px; max-width: none; }

  /* The 56px hairline figure does not survive at 375 any better here than it
     does above, so the rolling result takes the same 34 / 42 step, and the
     reserved heights come off because a stacked row sets its own. */
  .rollup__val { font-size: 34px; line-height: 42px; letter-spacing: -0.03em; font-weight: var(--wt-medium); }
  .rollup__row { gap: 10px; min-height: 0; }
  .rollup__arrow { display: none; }
  .rollup__to { display: inline; font-family: var(--font-display); font-size: 34px; line-height: 42px; font-weight: var(--wt-medium); color: var(--brand-navy); }
  .rollup__unit { font-family: var(--font-body); font-size: 15px; line-height: 18px; }
  .rollup__label { font-size: 13px; line-height: 16px; min-height: 0; }
  .rollup__note { font-size: 13px; line-height: 20px; max-width: none; min-height: 0; }

  /* The benefits list becomes the disclosure the artboard draws: a 52px header
     row with a 1px rule under it and a 44px chevron control.

     ONE DIFFERENCE FROM THE ARTBOARD, and it is deliberate. Paper draws the
     disclosure CLOSED with a "Tap to open" hint. It ships open, so nothing on
     the page is hidden from a visitor who never taps and no JavaScript is
     needed to reach six lines of results. The control still collapses it. */
  .benefits { padding-top: 6px; }
  .benefits > summary {
    display: flex;
    align-items: center;
    justify-content: space-between;
    height: 52px;
    border-bottom: 1px solid var(--rule);
    list-style: none;
    cursor: pointer;
    font-family: var(--font-display);
    font-size: 17px;
    line-height: 22px;
    font-weight: var(--wt-semi);
    color: var(--brand-navy);
  }
  .benefits > summary::-webkit-details-marker { display: none; }
  .benefits__chev {
    width: 44px; height: 44px;
    display: flex; align-items: center; justify-content: center;
    flex-shrink: 0;
    color: var(--azure-ink);
    transition: transform var(--dur-hover) var(--ease-out);
  }
  /* The disclosure header is the only control in this stylesheet, and it had
     no hover state. It lives below the breakpoint, where most visitors have no
     pointer at all, but a narrow window on a desktop is the same layout with a
     mouse in it, and a control that does not answer the pointer reads as
     inert. The chevron carries it, because the label is navy at 9.60:1 and has
     nowhere darker to go. */
  .benefits > summary:hover { color: var(--azure-hover); }      /* 6.65:1 on page */
  .benefits > summary:hover .benefits__chev { color: var(--azure-hover); }
  .benefits[open] > summary .benefits__chev { transform: rotate(180deg); }
  .benefits__label { display: none; }
  .benefits__row { padding-block: 10px; }
  .benefits__text { font-size: 15px; line-height: 23px; }

  /* The closing band sits 56 below the last row on the 375 artboard. */
  .ind-body + .band { margin-top: 56px; }
}

/* ===== RESEARCH AND VALIDATION ===========================================
   research-validation.html only. It uses the industry page template unchanged
   for its opening, rows, definition lists, panel and closing band; these two
   components are the parts that template does not already carry.

   Written after the rest of the file so it cannot alter the specificity of
   anything above it, and both selectors are scoped to their own class rather
   than to element names, so no existing page picks them up. */

/* The bullet list. base.css strips list markers site-wide, so the rule has to
   draw its own. It uses the same 2px --brand-azure top border the home page's
   `.uni__areas` chips use, turned on its side into a leading rule, so a list
   on this page and a chip on the home page read as the same family. */
.rv-list { display: flex; flex-direction: column; }
.rv-list li {
  position: relative;
  padding-left: 26px;
  padding-block: 7px;
  font-size: 18px;
  line-height: 30px;
  color: var(--ink);
}
.rv-list li::before {
  content: '';
  position: absolute;
  left: 0;
  /* Centred on the first line's cap height rather than on the line box: at
     30px leading a dot at 50% sits below the text it belongs to when the item
     wraps to two lines, which four of the items on this page do. */
  top: 20px;
  width: 10px;
  height: 2px;
  background: var(--brand-azure);
}

/* A compact variant, for a list that sits INSIDE a definition list's own
   definition lane. The parent cell sets 16 / 26, and an 18 / 30 list in it
   read as a second, larger voice inside a smaller one. The marker moves with
   the type: 4px padding plus half of a 26px line box, less one, puts the rule
   on the first line's cap height the way the full size version does.
   Added September 2026 for the Food Production page, where the livestock and
   farm wastewater blocks each carry a short benefit list per row. */
.rv-list--tight li { font-size: 16px; line-height: 26px; padding-block: 4px; padding-left: 22px; }
.rv-list--tight li::before { top: 16px; }

/* The client's paired "Hydro:" line, one under each named benefit on the Food
   Production page. It takes --azure-ink, which is the same ink the benefit
   markers and the slab terms already use on this page family, so the pairing
   reads without a new colour or a new type size. */
.deflist__hydro { color: var(--azure-ink); }

/* The institution block. Same two-lane proportion as `.deflist__row`, so the
   logo lane lines up with the term lane of the definition lists further down
   the page rather than starting at its own arbitrary width. */
.inst {
  display: grid;
  grid-template-columns: minmax(180px, 32.353%) minmax(0, 1fr);
  gap: 32px;
  padding: 32px;
  background: var(--surface);
  border-radius: 8px;
}
.inst__id { display: flex; flex-direction: column; gap: 14px; }
/* The marks come at four different aspect ratios, 4.19 Adelaide, 3.42 Monash,
   2.95 La Trobe and 2.36 UNSW, the first three supplied as SVG and UNSW as a
   raster. Sizing by WIDTH alone would print UNSW 78% taller than Adelaide and
   make the narrowest mark look like the largest institution. All four are
   given the same 44px ink height instead and take whatever width that implies,
   which is how a logo wall is set. */
.inst__logo { display: block; }
.inst__logo img { display: block; width: auto; height: 44px; }
.inst__dept {
  font-size: 16px;
  font-weight: var(--wt-semi);
  line-height: 24px;
  color: var(--ink-muted);
}
.inst__detail { display: flex; flex-direction: column; gap: 20px; }
.inst__detail > p { font-size: 17px; line-height: 28px; color: var(--ink); }
/* The sentence that introduces a list belongs to the list, so it loses the
   20px gap below it and keeps the one above. */
.inst__lead-in { margin-bottom: -14px; }
.inst .rv-list li { font-size: 17px; line-height: 28px; }
.inst .rv-list li::before { top: 19px; }

@media (max-width: 899px) {
  .rv-list li { padding-left: 20px; padding-block: 5px; font-size: 17px; line-height: 26px; }
  .rv-list li::before { top: 18px; width: 8px; }

  .inst { grid-template-columns: 1fr; gap: 20px; padding: 24px; }
  .inst__logo img { height: 36px; }
  .inst__dept { font-size: 15px; line-height: 22px; }
  .inst__detail > p { font-size: 17px; line-height: 26px; }
  .inst .rv-list li { font-size: 17px; line-height: 26px; }
  .inst .rv-list li::before { top: 17px; }
}

/* ===== THE RESEARCH PAGE'S OPENING, THE FOUR UNIVERSITY MARKS ============
   research-validation.html only. The band that bleeds off the right edge under
   that page's h1 held a laboratory photograph and now holds the four marks the
   page is about, at the client's instruction. Nothing else is in it.

   IT IS A NEW BLOCK, NOT AN OVERRIDE. `.ind-open__photo` above is used by ten
   other pages, so a composition wanted on one of them cannot borrow its selector.
   This keeps `.bleed-right`, which is what draws the band off the page edge and
   rounds the two corners still on screen, and supplies the rest itself.

   THE GROUND IS --surface, NOT NAVY. Adelaide's ink is #140F50 and the Monash
   crest is #006CAB, measured at 1.80:1 and 1.71:1 on --brand-navy, so half of
   each mark would go. On --surface they are around 16:1 and 5.2:1 and they are
   the artwork each brand book specifies. Nothing here tints, filters, fades or
   blends a mark, the same rule `.rv-panel__chip` follows in css/home.css.

   THE HAIRLINES ARE THE GRID GAP, not eight borders meeting in the middle. The
   container is painted --rule and the four cells are painted --surface, so a
   1px gap shows through as one clean line and no cell has to know which of its
   own edges is interior. `.bleed-right` already carries `overflow: hidden`, so
   the cells take the band's 14px left radius without being told to.

   ONE INK HEIGHT FOR ALL FOUR, which is the rule `.inst__logo` states above,
   applied to four aspect ratios rather than two: 4.19 Adelaide, 3.42 Monash,
   2.95 La Trobe, 2.36 UNSW. Sized by width the narrowest would print as the
   biggest institution. 96px is the desktop ceiling and UNSW sets it, being the
   one raster in the set: at 96 it renders 226 x 96 out of a 330 x 140 native
   file, which is inside both its own pixels and the 250 x 106 cap this task was
   given for it. That cap is the task's, not a rule this repository carried
   before, so it is written here rather than cited. At 1440 the four render 402,
   328, 283 and 226 wide, measured rather than calculated.

   THE BAND'S HEIGHT COMES FROM THE MARKS, not from the artboard. Two rows of
   96 + 56 + 56 plus the 1px gap is 417, against the 420 `.ind-open__photo`
   holds as its ceiling, so it lands where the design drew it without being
   pinned there. Deriving it means the band cannot outlive its content and
   leave the run of empty ground tools/rhythm.mjs exists to catch.
   ========================================================================== */

.rv-hero {
  --rv-mark-h: clamp(48px, 6.8vw, 96px);    /* 61 at 900, 96 from 1412 up */
  --rv-pad-y:  clamp(28px, 3.889vw, 56px);  /* 35 at 900, 56 at 1440      */
  --rv-pad-x:  clamp(20px, 2.778vw, 40px);  /* 25 at 900, 40 at 1440      */

  position: relative;
  z-index: var(--z-content);
  margin-top: var(--rhythm-row-end);
  display: grid;
  /* minmax(0, 1fr), not 1fr. A grid track's default floor is min-content, and
     the min-content of a cell holding an 800px lockup is not zero. A 0 floor on
     the track is what keeps a mark from widening its own column, and it makes a
     `min-width: 0` on the cell redundant: probed at 375, 600, 900 and 1440 with
     the cell forced back to `min-width: auto`, no box moved. */
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 1px;
  background: var(--rule);
}
.rv-hero__cell {
  display: grid;
  place-items: center;
  padding: var(--rv-pad-y) var(--rv-pad-x);
  background: var(--surface);
  text-decoration: none;
  color: inherit;
  transition: background-color var(--dur-hover) var(--ease-out);
}
.rv-hero__cell:hover { background: var(--page); }
/* The ring is inset by 3 rather than offset outward. `.bleed-right` clips its
   children, so a ring drawn outside a cell would be cut along the band's own
   edges, which is three sides of every one of the four. --focus is 4.53:1 on
   --surface, past the 3:1 an indicator has to clear. */
.rv-hero__cell:focus-visible {
  outline: 3px solid var(--focus);
  outline-offset: -3px;
}
/* Height drives and width follows, which is what holds the four to one ink
   height. max-width is the second axis, and it is a guard rather than a working
   cap: measured across the range, Adelaide, the widest at 4.19, renders 402.1
   into the 607.5 a cell leaves it at 1440 and 167.5 into 335 at 375, so it
   never binds and the four never go ragged. Removing it changes nothing today.
   It is there because a mark added later at a wider aspect would meet it, and
   `contain` then shrinks that mark rather than squashing it. */
.rv-hero__cell img {
  display: block;
  width: auto;
  height: var(--rv-mark-h);
  max-width: 100%;
  object-fit: contain;
}

@media (max-width: 899px) {
  /* `.bleed-right` collapses to a plain 100vw block below the breakpoint, so a
     cell is half the viewport rather than half of 1376 and the marks come down
     with it. The largest this query ever draws is 57.5 at 899, its own top
     width. The 58 is a guard and not a value reached here, 6.4vw needing 906 to
     get there, and it is left in so that moving the breakpoint outward cannot
     let a mark grow past the step the desktop rule takes at 900. */
  .rv-hero {
    --rv-mark-h: clamp(36px, 6.4vw, 58px);
    --rv-pad-y:  clamp(20px, 3.6vw, 34px);
    --rv-pad-x:  clamp(16px, 3.2vw, 32px);
  }
  /* No hover on touch, the same call `.rv-panel` makes in css/home.css: a hover
     state a tap leaves stuck on is worse than none. */
  .rv-hero__cell:hover { background: var(--surface); }
}

/* THREE MARKS RATHER THAN FOUR, `partners.html` only, client email of
   20 September 2026 dropping La Trobe. research-validation.html still opens on
   four and keeps the base rules above, so this is a modifier and not an edit to
   them.

   ABOVE 900 THE ROW IS THREE ACROSS. Height still drives, so each mark keeps
   the shared ink height and simply gets a wider cell, 458 instead of 344 at
   1376.

   FROM 600 UP IT IS THREE ACROSS AS WELL, client request of 25 September
   2026: "make all 3 tiles here the same size". Between 600 and 899 this used
   to stay two up with the odd mark taking a full-width row, so UNSW sat in a
   cell twice the width of the other two. Three columns fit here once the side
   padding comes in: the ink height in this range is 6.4vw, so Adelaide at 4.19
   needs 26.8vw, and a third of the row less 2.4vw a side leaves 28.5vw. That
   is 161 into 170 at 600 and 185 into 196 at 690, so `contain` never has to
   shrink a mark and the three keep one ink height. Below 600 the base rule's
   single column already gives every cell the same size. */
@media (min-width: 600px) {
  .rv-hero--three { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}
@media (min-width: 600px) and (max-width: 899px) {
  .rv-hero--three { --rv-pad-x: clamp(12px, 2.4vw, 24px); }
}

@media (max-width: 599px) {
  /* ONE COLUMN, because by here the two-up cell has stopped carrying a mark
     that reads as a hero. At 600, the narrowest width two columns survive, the
     shared ink height is 38px against the 36px this same page prints these same
     four marks at inside `.inst` further down, so the opening would be the size
     of the body. One column gives each mark the page's own gutter, 40px of ink
     at 375 and 44 from 550 up.

     The band gets taller when it restacks, 164 to 339 at the switch. That is
     the composition changing rather than a defect: four marks in a column are
     four rows, and the alternative is a mark too small to identify. */
  .rv-hero {
    --rv-mark-h: clamp(40px, 8vw, 44px);
    --rv-pad-y:  clamp(16px, 3.4vw, 20px);
    --rv-pad-x:  var(--gutter);

    grid-template-columns: minmax(0, 1fr);
  }
}


/* ===== A PARTNER PAGE'S OPENING, THAT UNIVERSITY'S OWN MARK ==============
   The four `partner-*.html` pages, at the client's instruction of 11 September
   2026: "need the university logo as the hero for their independent pages".
   Each page opens on the mark of the institution it is about and nothing else.

   WHAT IT REPLACES. The four pages opened on `.art--wave`, the client's own
   brand wave, chosen when the marks were held back as unlicensed. That
   decision is now the client's to make and he has made it, so the wave comes
   off these four openings: a logo does not go in front of a moving decorative
   ground, and the wave was only ever standing in for the mark.

   IT IS A NEW BLOCK, NOT A MODIFIER OF `.rv-hero` ABOVE. That block solves a
   different problem, four marks held to one ink height inside one band, and
   its hairline grid, its two-then-one column ramp and its cell links are all
   answers to being four. One mark needs none of it, and borrowing the selector
   would tie research-validation.html's composition to these four pages.

   THE GROUND IS --surface, NOT NAVY AND NOT THE WAVE, for the reason set out
   under "THE RESEARCH PAGE'S OPENING" above and measured there: Adelaide's ink
   is #140F50 and the Monash crest #006CAB, 1.80:1 and 1.71:1 on --brand-navy
   against roughly 16:1 and 5.2:1 on --surface. Nothing here tints, filters,
   fades, crops or recolours a mark.

   ONE INK HEIGHT ACROSS ALL FOUR PAGES, height driving and width following,
   which is the rule `.inst__logo` and `.rv-hero` already state. The four come
   at 4.19, 3.42, 2.95 and 2.36, so sizing by width would print the narrowest
   as the largest institution, and these four pages are read one after another
   from partners.html.

   106px IS THE DESKTOP CEILING AND UNSW SETS IT. It is the one raster in the
   set, 330 x 140 native, and at 106 it renders 250 x 106, which is the cap
   recorded for that file under "THE RESEARCH PAGE'S OPENING" and is a
   downscale rather than an upscale. At 1440 the four render 444, 363, 313 and
   250 wide, measured rather than calculated.

   THE TWO RAMPS MEET AT 900 rather than stepping across it: 80px of ink at
   900 from either side, 56 at 375 and 106 at 1440. The padding ramps are
   built the same way. A step here would move the mark, and a mark that jumps
   size as the window narrows is the defect this file's `--gutter` note
   describes in the type ramp.

   CLEAR SPACE IS PADDING, AND IT IS DERIVED FROM THE MARK. The band gives
   the mark at least three quarters of its own ink height above and below at
   every width, 80 against 106 at 1440 and 42 against 56 at 375, so the air
   around a lockup grows and shrinks with the lockup rather than staying at
   some figure the page picked once.
   ========================================================================== */

.pt-hero {
  --pt-mark-h: clamp(80px, calc(36.67px + 4.815vw), 106px);   /* 80 at 900, 106 at 1440 */
  --pt-pad-y:  clamp(60px, calc(26.70px + 3.700vw), 80px);    /* 60 at 900,  80 at 1440 */
  --pt-pad-x:  clamp(40px, 4.4444vw, 64px);                   /* 40 at 900,  64 at 1440 */

  position: relative;
  z-index: var(--z-content);
  margin-top: var(--rhythm-row-end);
  display: grid;
  /* minmax(0, 1fr) for the reason `.rv-hero` gives: a track's default floor is
     min-content, and the min-content of a cell holding an 800px lockup is not
     zero. The 0 floor is what lets `max-width: 100%` on the mark bind before
     the band can be widened by its own contents. */
  grid-template-columns: minmax(0, 1fr);
  place-items: center;
  padding: var(--pt-pad-y) var(--pt-pad-x);
  background: var(--surface);
}

/* Height drives, width follows, and `max-width` is the second axis. It is a
   guard rather than a working cap: the widest of the four, Adelaide at 4.19,
   renders 444 into the 1248 the band leaves it at 1440 and 235 into 335 at
   375, so it never binds today. It is here because `contain` then shrinks a
   mark to fit instead of squashing it, which is the behaviour wanted if a
   fifth partner arrives at a wider aspect. */
.pt-hero img {
  display: block;
  width: auto;
  height: var(--pt-mark-h);
  max-width: 100%;
  object-fit: contain;
}

@media (max-width: 899px) {
  /* `.bleed-right` collapses to a plain full-width block below the breakpoint,
     so the band is the viewport rather than 1376 and the mark comes down with
     it. 56 at 375 leaves Adelaide, the widest, at 235 inside the 335 the
     gutter leaves, so the narrowest clear space on the page is 50 a side. */
  .pt-hero {
    --pt-mark-h: clamp(56px, calc(38.86px + 4.5714vw), 80px);  /* 56 at 375, 80 at 900 */
    --pt-pad-y:  clamp(42px, calc(29.14px + 3.4286vw), 60px);  /* 42 at 375, 60 at 900 */
    --pt-pad-x:  var(--gutter);
  }
}

/* ===== FILM AND CAPTIONED PHOTOGRAPHY ====================================
   Added for lake-albert.html, the first industry-family page that carries a
   self-hosted film and a run of matched before/after photographs.

   WHY NOT .explainer. The home page's film component lives in css/home.css,
   which no page in this family loads, and it is built around a custom play
   button, a scroll dock and js/motion.js. None of that is wanted here: this
   film is the point of the page rather than an interruption in it, so it uses
   the browser's own controls and no JavaScript at all.

   preload="none" is set on the element, not here, but it is the reason this
   block can afford a 13 MiB film: nothing is fetched until the reader presses
   play. The poster is a still, and it is all that loads. */

.ind-video {
  margin: 0;
  display: flex;
  flex-direction: column;
  gap: 14px;
}
.ind-video video {
  display: block;
  width: 100%;
  height: auto;
  aspect-ratio: 16 / 9;
  background: var(--brand-navy);
  border-radius: 8px;
}
.ind-video figcaption { font-size: 16px; line-height: 24px; color: var(--ink-muted); }

/* A single photograph inside the copy column, with its own caption. The
   caption is part of the figure, so it stays with the picture when the row
   reflows. */
.figbox { margin: 0; display: flex; flex-direction: column; gap: 12px; }
.figbox img { display: block; width: 100%; height: auto; border-radius: 8px; }
.figbox figcaption { font-size: 16px; line-height: 24px; color: var(--ink-muted); }

/* The before/after pair. Two equal lanes, because the comparison is the
   content: unequal lanes would suggest one of the two states matters more.
   The photographs come off the client's own report at different crops, so
   each lane fixes a 4:3 box and covers it rather than letting one picture set
   a row height the other has to match with letterboxing. */
.pair {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: 20px;
}
.pair__item { margin: 0; display: flex; flex-direction: column; gap: 12px; }
.pair__item img {
  display: block;
  width: 100%;
  /* height: auto is load-bearing, not tidiness. base.css sets `max-width: 100%`
     on img with no matching `height: auto`, so a picture carrying width and
     height attributes keeps the attribute height as its SPECIFIED height. A
     specified height leaves `aspect-ratio` nothing to resolve, and it is
     ignored: measured before this line, the 620 x 1342 unit photograph
     rendered 464 wide by 1342 tall inside a lane meant to be 4:3. */
  height: auto;
  aspect-ratio: 4 / 3;
  object-fit: cover;
  border-radius: 8px;
}
.pair__item figcaption { font-size: 16px; line-height: 24px; color: var(--ink-muted); }
/* The state word carries the meaning, so it is the part that is weighted.
   --azure-ink rather than --brand-azure: measured 4.53:1 on --page, where the
   lighter brand azure is 2.9:1 and fails body contrast. */
.pair__state {
  font-family: var(--font-display);
  font-weight: var(--wt-medium);
  color: var(--azure-ink);
}

@media (max-width: 599px) {
  /* Below 600 the two lanes are ~150px each, which is too small to read a
     bloom in. Stacked, each picture gets the full column and the pair still
     reads as a pair because the captions name the two dates. */
  .pair { grid-template-columns: 1fr; gap: 24px; }
}

/* ONE PHOTOGRAPH KEEPS ITS OWN FRAME, head session 024.
   `.pair__item img` crops everything to 4:3 with object-fit: cover, which is
   right for the pairs it was written for. `lake-albert-unit-shore.jpg` is
   900 x 498, a 1.807:1 frame, so cover discarded 118px from each side: it cut
   the Hydro2050 cabinet in half, and the cabinet is the entire subject of the
   caption beneath it, while slicing the client's own burned-in label mid-word
   so "Eastern Side / April 2026" rendered as "Easte / Apri".

   Cropping a client's labelled site photograph until its label is unreadable
   is worse than two items in a pair having different heights, so this one is
   given the source's own ratio. With the box matching the file, `contain` and
   `cover` resolve identically, and `contain` is stated so that a re-crop of the
   file letterboxes rather than silently starting to cut again. */
.pair__item img[src$="lake-albert-unit-shore.jpg"] {
  aspect-ratio: 900 / 498;
  object-fit: contain;
}


/* ===== THE CLIENT'S RESEARCH PAPER =======================================
   Added 11 September 2026. The 10 September pack carried an eight page PDF,
   "Research, Validation & Scientific Foundation", and its text was rewritten
   into research-validation.html and the four partner pages. The client then
   asked for the document itself to be readable, so it ships at
   assets/docs/hydro2050-research-validation-and-scientific-foundation.pdf,
   byte identical to the file he sent, and six pages offer it:
   research-validation.html, partners.html and the four partner-*.html pages.

   WHY THIS IS NOT A BUTTON. `.btn` is the one action a page asks for, and on
   all six of these pages that action is already taken by the closing band. A
   document offered partway down a page is a second destination rather than a
   second call to action, so it takes the site's own `.link-rule` underline
   with a line of meta under it.

   THE META LINE IS NOT DECORATION. Extent and weight are what a reader on a
   metered connection wants before they tap, and the link opens in a new tab so
   that tapping it does not take the page away from them. There is no other
   `target="_blank"` in this build, deliberately: this is the only link that
   leaves the page for a file rather than for another page. 475 KB is the
   file's real size and has to be corrected here and in the six pages if the
   file is ever replaced.

   ONE BLOCK, TWO PLACEMENTS. In an `.ind-open` wrap it sits under the deck
   with a hairline above it, which is the separation `.row` already uses in
   this family. Inside a `.row__copy` it is the last thing in the copy, and the
   hairline and the measure both come off because the row sets its own. */

.docref {
  margin-top: var(--rhythm-row-end);
  padding-top: var(--rhythm-row);
  border-top: 1px solid var(--rule);
  display: flex;
  flex-direction: column;
  gap: 10px;
  max-width: 680px;               /* the deck's measure, so they share an edge */
}

.row__copy .docref {
  margin-top: 0;
  padding-top: 0;
  border-top: 0;
  max-width: none;
}

/* --ink-muted is 5.51:1 on --page and 5.06:1 on --surface, so it clears the
   4.5 body floor on both grounds this block is used over. The 16 / 26 pair is
   `.row__note`, which is the same job in the same family. */
.docref__meta { font-size: 16px; line-height: 26px; color: var(--ink-muted); }

@media (max-width: 899px) {
  .docref__meta { font-size: 15px; line-height: 23px; }
}
