/* ============================================================================
   HYDRO2050 - BASE
   ----------------------------------------------------------------------------
   Reset, document defaults, the layout primitives every page uses, and the
   type scale. Component-level styling lives in components.css; anything that
   moves lives in motion.css.
   ========================================================================== */

*, *::before, *::after { box-sizing: border-box; }

html {
  -webkit-text-size-adjust: 100%;
  /* No scroll-behavior: smooth here. Lenis owns smooth scrolling, and the two
     fight: the CSS property makes the browser animate a jump that Lenis is
     already animating, which lands short and then snaps. */
}

body {
  margin: 0;
  background: var(--page);
  color: var(--ink);
  font-family: var(--font-body);
  font-size: var(--text-body);
  line-height: var(--lh-body);
  font-weight: var(--wt-regular);
  -webkit-font-smoothing: antialiased;
  /* The design bleeds photography off the right of the page. Without this a
     bleeding element would add a horizontal scrollbar at every width.

     `clip`, NOT `hidden`, and the difference is the whole reason the header
     can be sticky. Any overflow value other than `visible` makes the element a
     scroll container, and `position: sticky` sticks to its nearest scrolling
     ancestor: with `hidden` on body, the header's scroll container was body
     rather than the viewport, so it stuck to a box that never scrolls and the
     property silently did nothing. `clip` clips exactly the same pixels and
     creates no scroll container, so the header's ancestor chain reaches the
     viewport again. css/industry.css already used clip for this reason. */
  overflow-x: clip;
}

h1, h2, h3, h4, p, figure, blockquote, ul, ol { margin: 0; }
ul, ol { padding: 0; list-style: none; }
img, video, svg { display: block; max-width: 100%; }
button { font: inherit; color: inherit; border: 0; background: none; cursor: pointer; }
a { color: inherit; }

/* --- TYPE ------------------------------------------------------------------
   One class per role, matching the tokens. Every size traces to a measured
   value in site/DESIGN-NOTES.md. Nothing here is heavier than 600. */

.t-display, .t-title, .t-section, .t-closer, .t-band,
.t-tile, .t-card, .t-stat, .t-stat-sm, .t-figure {
  font-family: var(--font-display);
  font-weight: var(--wt-medium);
  color: var(--brand-navy);
  text-wrap: balance;
}

.t-display { font-size: var(--text-display); line-height: var(--lh-display); letter-spacing: var(--track-display); }
.t-title   { font-size: var(--text-title);   line-height: var(--lh-title);   letter-spacing: var(--track-title); }
.t-section { font-size: var(--text-section); line-height: var(--lh-section); letter-spacing: var(--track-section); }
.t-closer  { font-size: var(--text-closer);  line-height: var(--lh-closer);  letter-spacing: var(--track-band); }
.t-band    { font-size: var(--text-band);    line-height: var(--lh-band);    letter-spacing: var(--track-band); }
.t-tile    { font-size: var(--text-tile);    line-height: var(--lh-tile);    letter-spacing: var(--track-band); }
.t-card    { font-size: var(--text-card);    line-height: var(--lh-card); }
/* The two figure roles are the only display type on the site set lighter than
   medium. Paper draws them in Archivo 100 and Archivo 300; Montserrat 300 is
   what carries that hairline weight in the brand face. Without this they
   inherit --wt-medium from the group rule above and read as slabs. */
.t-stat    { font-size: var(--text-stat);    line-height: var(--lh-stat);    letter-spacing: -0.06em; font-weight: var(--wt-light); }
.t-stat-sm { font-size: var(--text-stat-sm); line-height: var(--lh-stat-sm); letter-spacing: -0.04em; font-weight: var(--wt-light); }
.t-figure  { font-size: var(--text-figure);  line-height: var(--lh-figure);  letter-spacing: var(--track-figure); }

.t-lead    { font-size: var(--text-lead);    line-height: var(--lh-lead);    color: var(--ink); }
.t-body    { font-size: var(--text-body);    line-height: var(--lh-body);    color: var(--ink); }
.t-prose   { font-size: var(--text-prose);   line-height: var(--lh-prose);   color: var(--ink-muted); }
.t-small   { font-size: var(--text-small);   line-height: var(--lh-small); }
.t-caption { font-size: var(--text-caption); line-height: var(--lh-caption); color: var(--ink-muted); }

.on-navy .t-display, .on-navy .t-title, .on-navy .t-section,
.on-navy .t-band, .on-navy .t-card, .on-navy .t-stat-sm { color: var(--on-navy); }
.on-navy .t-lead, .on-navy .t-body { color: var(--on-navy-mute); }

/* The eyebrow. An all-caps tracked label is a well-known design tell when it
   is decoration. It is not decoration here: it is the category a study is
   filed under and the only place that category is written in words, so
   removing it would remove information. One per section, never stacked. */
.eyebrow {
  font-family: var(--font-display);
  font-size: var(--text-caption);
  font-weight: var(--wt-semi);
  line-height: var(--lh-caption);
  letter-spacing: var(--track-eyebrow);
  text-transform: uppercase;
  color: var(--azure-ink);   /* 4.94:1 on page, 4.53:1 on surface */
}
.eyebrow--on-navy { color: var(--brand-turq); }  /* 5.89:1 on navy */
.eyebrow--mute    { color: var(--on-navy-mute); } /* 6.69:1 on navy */
.eyebrow--muted   { color: var(--ink-muted); }   /* 5.51:1 on page */

/* --- LAYOUT ----------------------------------------------------------------
   Three containers, and the difference between them is the whole grid system.

   .wrap   holds the 1312 content column and the 64px gutter. Type lives here.
   .bleed  widens to 1376, a 32px margin. Body photography lives here.
   A hero photograph uses neither: it runs off the page edge on one side, so it
   is positioned against the viewport, not against a column. */

.wrap  { width: var(--content); margin-inline: auto; }
.bleed { width: var(--bleed);   margin-inline: auto; }

/* Photography breaking the right page edge. The element starts at the content
   column's left edge and runs to the viewport edge, so its width is whatever
   is left. Only the left corners are rounded, because the right corners are
   off-screen and rounding them would put a notch at the page edge.

   `margin-left: auto` is what puts it there, and it is load-bearing. With a
   width, a margin-right and margin-left left at its initial 0, all three are
   non-auto, the box is over-constrained, and CSS resolves that in a left-to-
   right document by throwing the specified margin-right away and honouring
   margin-left. The box then sat flush at 0 and ran to the column's RIGHT edge,
   which is a mirror image of this rule's intent: on all seven pages that use
   it the hero photograph bled off the LEFT of the page and its left edge sat
   64px outside the h1 directly above it. Nothing caught it because the box is
   the correct width and overflows nothing, it is simply on the wrong side.

   An auto margin-left absorbs the remainder instead, so the equation solves
   for the left edge rather than discarding the right one. It resolves correctly
   whether this element is a sibling of .wrap or nested inside it, which the
   original formula did not: it only ever worked nested, and every use on the
   site is a sibling. */
.bleed-right {
  width: calc(50vw + var(--content) / 2);
  margin-left: auto;
  margin-right: calc(50% - 50vw);
  border-radius: var(--r-photo) 0 0 var(--r-photo);
  overflow: hidden;
}
.bleed-left {
  width: calc(50vw + var(--content) / 2);
  margin-left: calc(50% - 50vw);
  border-radius: 0 var(--r-photo) var(--r-photo) 0;
  overflow: hidden;
}

.section { padding-block: var(--s-24); }
.section--tight { padding-block: var(--s-16); }
.section--surface { background: var(--surface); }
.section--navy { background: var(--surface-navy); color: var(--on-navy); }

.stack > * + * { margin-top: var(--s-6); }
.stack-lg > * + * { margin-top: var(--s-10); }

.measure    { max-width: 680px; }
.measure-lg { max-width: 900px; }

/* --- ACCESSIBILITY ---------------------------------------------------------
   One focus style, defined once, on :focus-visible only so a mouse click does
   not draw a ring. Measured: --focus is 4.94:1 on page and 4.53:1 on surface,
   both past the 3:1 a focus indicator has to clear.

   Over any dark ground the ring switches to turquoise. That list has to name
   EVERY dark surface, and three were missing from it: the industry tiles, the
   capabilities band and the footer's contact plinth. Eight rings on those
   surfaces were drawing --focus over navy at 1.94:1 and one at 1.43:1, all
   well under the 3:1 floor. Measured for turquoise: 5.89:1 on --brand-navy,
   7.24:1 on the dark tile, 5.14:1 on a capability row, and 4.34:1 on the
   capabilities gradient's lightest stop, which is the worst case in the set.

   The switch is on .tile--dark, not .tile. The light tiles now carry an 84 to
   92 percent WHITE scrim under their type, where turquoise measures 1.63:1.
   They keep --focus, which is 4.94:1 on white and 3.60:1 over the darkest
   photograph that scrim can admit. */

:focus-visible {
  outline: 2px solid var(--focus);
  outline-offset: 3px;
  border-radius: 3px;
}
.on-navy :focus-visible,
.section--navy :focus-visible,
.site-header :focus-visible,
.nav-drawer :focus-visible,
.legal :focus-visible,
.plinth :focus-visible,
.tile--dark :focus-visible,
.capabilities :focus-visible,
.band :focus-visible {
  outline-color: var(--brand-turq);
}

.skip-link {
  position: absolute;
  left: var(--s-4);
  top: -100px;
  z-index: var(--z-skip);
  padding: var(--s-3) var(--s-5);
  background: var(--page);
  color: var(--brand-navy);
  border: 2px solid var(--brand-navy);
  border-radius: var(--r-input);
  font-family: var(--font-display);
  font-weight: var(--wt-semi);
  transition: top var(--dur-hover) var(--ease-out);
}
.skip-link:focus { top: var(--s-4); }

.visually-hidden {
  position: absolute; width: 1px; height: 1px;
  margin: -1px; padding: 0; overflow: hidden;
  clip: rect(0 0 0 0); clip-path: inset(50%); white-space: nowrap;
}

/* --- MOBILE ----------------------------------------------------------------
   Below the breakpoint the composition changes: the nav becomes a drawer and
   photography goes edge to edge with its height capped rather than insetting,
   which is why .bleed-right and .bleed-left collapse to a plain full-width
   block here. The gutter does NOT change here any more, it ramps continuously
   from 64 to 20 across the whole range and tokens.css owns it.

   The mobile artboards use far fewer sizes than the desktop ones: one display
   size at 34, one section size at 22, body at 17, caption at 13. Several
   desktop roles therefore collapse onto the same value here, which is the
   design's own decision, not a shortcut. */

@media (max-width: 899px) {
  :root {
    /* --content, --bleed and --gutter are NOT restated here any more. They
       used to be, as `100vw - 40px`, `100vw` and 20px against the desktop
       64, and those two definitions do not meet: dragging from 900 to 899
       WIDENED the content column from 772 to 859, so the page jumped 87px
       outward as the window got narrower and every element measured against
       the column went with it. tokens.css now runs one fluid gutter, 20 at
       375 to 64 at 1440, which passes through the breakpoint without a step
       and still lands on both artboards exactly. */

    /* The 375 -> 900 half of the type ramp. Its far end is the value
       tokens.css starts its own ramp from, so the two meet at the breakpoint
       instead of stepping across it. Only the five roles whose desktop value
       is genuinely larger than the mobile one ramp here; the rest are the
       mobile artboard's own figures and stay flat below 900, unchanged.

       Arithmetic, for a step running from `a` at 375 to `b` at 900:
          slope = (b - a) / 525, expressed as vw, so (b - a) / 5.25
          base  = a - slope * 375 */

    --text-display: clamp(34px, calc(3.4286vw + 21.143px), 52px);  /* 34 -> 52 */
    --lh-display:   clamp(39px, calc(2.8571vw + 28.286px), 54px);  /* 39 -> 54 */
    --text-title:   clamp(34px, calc(0.3810vw + 32.571px), 36px);  /* 34 -> 36 */
    --lh-title:     39px;
    --text-section: clamp(22px, calc(1.7143vw + 15.571px), 31px);  /* 22 -> 31 */
    --lh-section:   clamp(27px, calc(2.0952vw + 19.143px), 38px);  /* 27 -> 38 */
    --text-closer:  clamp(34px, calc(0.7619vw + 31.143px), 38px);  /* 34 -> 38 */
    --lh-closer:    clamp(40px, calc(0.3810vw + 38.571px), 42px);  /* 40 -> 42 */
    --text-stat:    clamp(34px, calc(4.1905vw + 18.286px), 56px);  /* 34 -> 56 */
    --lh-stat:      clamp(42px, calc(2.6667vw + 32px), 56px);  /* 42 -> 56 */

    --text-band:    22px;  --lh-band:    28px;
    --text-tile:    22px;  --lh-tile:    27px;
    --text-card:    22px;  --lh-card:    28px;
    --text-stat-sm: 34px;  --lh-stat-sm: 42px;
    --text-figure:  22px;  --lh-figure:  28px;
    --text-lead:    17px;  --lh-lead:    27px;
    --text-body:    17px;  --lh-body:    26px;
    --text-prose:   15px;  --lh-prose:   23px;
    --text-small:   15px;  --lh-small:   22px;
    --text-caption: 13px;  --lh-caption: 18px;
    --text-head:    22px;  --lh-head:    28px;
    --text-copy:    17px;  --lh-copy:    26px;

    --track-display: -0.03em;
    --track-title:   -0.03em;
    --track-section: -0.02em;
  }

  body { font-size: var(--text-body); line-height: var(--lh-body); }

  .section { padding-block: var(--s-12); }
  .section--tight { padding-block: var(--s-10); }

  .bleed-right, .bleed-left {
    width: 100vw;
    margin-inline: calc(50% - 50vw);
    border-radius: 0;
  }
}

/* --------------------------------------------------------------- CREDITS
   Image credits. Every photograph on this build is a background: the tiles
   carry a scrim and type over them, the capability rows sit at 0.2 opacity
   behind their figures, and the hardware and closing bands are full bleed.
   None of them has a caption slot, so a per-image caption would have to be
   invented and placed over artwork that was measured to the pixel.

   A credit list is the normal way to discharge attribution for background
   photography, and it is the slot the held images drop into as they clear.
   The format follows the case study card captions in the Paper file:
   "Subject. Author, Licence." with the licence carrying its deed URI.

   Only images whose source and licence are established appear here. The rest
   are recorded in /IMAGERY-ATTRIBUTION.md with a hold or flag verdict and are
   deliberately absent, because a credit naming a source nobody has verified is
   worse than no credit at all. */
.credits {
  padding-block: var(--s-10);
  border-top: 1px solid var(--rule);
}
.credits__title {
  font-family: var(--font-display);
  font-size: var(--text-caption);
  font-weight: var(--wt-semi);
  line-height: var(--lh-caption);
  letter-spacing: var(--track-eyebrow);
  text-transform: uppercase;
  color: var(--ink-muted);          /* 5.51:1 on page */
  margin: 0 0 var(--s-4);
}
.credits__list {
  list-style: none;
  margin: 0;
  padding: 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: var(--s-3) var(--s-8);
}
/* min-height is the project's 44px tap-target floor, not a look. A credit line
   is 19px of type, so the links inside it were 18px high and tools/audit.mjs
   failed all eight widths on them. The row is grown to 44px and the link is
   given 13px of block padding cancelled by an equal negative margin, so the
   hit box reaches 45px while the text stays exactly where it was measured.
   The 12px row gap absorbs the half-pixel each side, so hit areas on stacked
   rows do not overlap. */
.credits__list li {
  font-size: var(--text-caption);
  line-height: var(--lh-caption);
  color: var(--ink-muted);
  min-height: 44px;
  display: flex;
  align-items: center;
}
.credits__list a {
  color: var(--azure-ink);          /* 4.94:1 on page */
  text-decoration: underline;
  text-underline-offset: 2px;
  display: inline-block;
  padding-block: 13px;
  margin-block: -13px;
}
.credits__list a:hover { color: var(--azure-hover); }

/* 899, not 900. Every other breakpoint on the site is `max-width: 899px`, so
   at exactly 900 this one rule was already on its mobile layout while the
   whole page around it was still on its desktop one. A single-width straddle
   is precisely the class of defect a sweep in 40px steps walks past. */
@media (max-width: 899px) {
  .credits__list { grid-template-columns: minmax(0, 1fr); }
}

/* HEAD SESSION 024, reported by the client from the rendered page: the first
   credit read "Cristian Palmer,Unsplash License" with no space.

   The text node is correct; `.credits__list li` was `display: flex`. A flex
   container turns every child into a flex item and wraps the loose text around
   them in anonymous items, and **whitespace between flex items is discarded**.
   So ", Cristian Palmer, " lost its trailing space against the anchor beside
   it, on every credit line carrying a link. It was never a typo.

   The flex was only ever there to centre one line of type inside the 44px tap
   floor, so it is replaced with padding, which does the same job without
   blockifying the anchors. Grid would have had the identical fault. The floor
   itself is unaffected: it lives on the anchor's own 13px padding and negative
   margin, which is what the audit measures, not on the row. */
.credits__list li {
  display: block;
  min-height: 0;
  padding-block: 12px;
}
