/* ============================================================
   Bathroom Waterproofing Build Stack — scoped styles.
   ------------------------------------------------------------
   Every rule sits under .hr-wp so nothing leaks into another
   module. The reference file's :root block is deliberately NOT
   carried over: those tokens are loaded site-wide from
   colors_and_type.css and redeclaring them here would fork them.

   No hex codes anywhere, not one. Every translucent value in the
   reference was written as rgba over a colour that IS a token, so
   each one is a color-mix over that token instead and tracks the
   palette. The inline SVG diagram used to be the one exception and
   it left with the proof card.

   No JavaScript. Highlight, zoom and reveal are all CSS state.

   Colour split holds: neutral bands, blue for the eyebrow and the
   highlight, and green on exactly one thing, the numeral of a key
   layer, which is the only marker a key layer carries. Amber is gone
   entirely: it lived on the drain plug in the proof card's diagram.
   ============================================================ */

/* Three reserved heights. The copy block is anchored to the bottom of the card,
   so a title's distance from that edge is the sum of everything under it: the
   pill row, the reveal line and the description. Every one of those has to be a
   constant or the titles in a row do not agree, and the last attempt fixed only
   the pill, which left layer 02 a line high because its description runs to
   three lines where the other four run to two.

   --desc-h is TWO lines at 12px on 1.5, and it is a CEILING rather than a floor.
   As a floor it only guaranteed a minimum, so layer 02's three-line description
   pushed straight past it and its title sat a line above the other four. As a
   ceiling every description occupies exactly two lines at rest whatever it says,
   and every title lands on the same row.

   Nothing is lost to it. The height is exactly two line boxes, so the cut falls
   between lines rather than through one, the full sentence stays in the DOM, and
   hover lifts the ceiling so the rest of it appears.

   --more-h is only a ceiling for the reveal's max-height transition, NOT a
   reserved row. Reserving it was the wrong fix: it lifted the copy on all five
   cards into the middle of the picture at rest, when the ask was simply to
   bring 01 and 02 level with the three that were already right. */
/* CEILINGS ARE SIZED TO THE COPY, AND THAT IS WHAT STOPS THE BOUNCE.

   The reveal and the spacer move in opposite directions on hover: the reveal
   grows and pushes the bottom-anchored copy up, the spacer collapses and lets
   it back down. That is fine only while both are still travelling. A ceiling
   larger than the text it holds breaks it, because the growth reaches the text
   and stops while the spacer keeps giving space back for the rest of the
   duration, so the copy rises and then sinks again. With a 60px ceiling over
   two lines of reveal the height ran min(60e, 33) - 28e: up 17.8px by 56 per
   cent, back to 5.4px at the end. A 12px drop, and that was the jitter.

   So every ceiling here is the height of the longest copy it has to show, not a
   round number with room to spare. Then the growth is still moving when the
   spacer finishes and the copy only ever travels one way. Anything shorter than
   the ceiling simply never grows, which contributes nothing at all and is
   equally smooth.

   Re-measure these if the copy gets longer. Too small clips the last line, too
   large brings the bounce back. */
.hr-wp {
  --spec-h: 19px;
  --desc-h: 36px;
  /* Three lines at 12px on 1.5, which is exactly the longest description at
     every width the hover runs at. Three across below 1000px makes the cards
     wide enough for two lines, and five across above it never needs a fourth,
     so one value covers the range and nothing saturates early. */
  --desc-open: 54px;
  /* Reveal text plus the 8px gap, now folded into its padding. Three lines at
     11.5px on 1.45 is 50px, and the cards are narrow enough between 1000 and
     1200px to need all three. */
  --more-h: 60px;
  background: var(--hr-surface); padding: var(--section-y) 0;
}
/* Past 1200px the cards settle at 219px and the reveal fits on two lines, so
   the ceiling comes down to meet it. This is the width almost everyone hovers
   at, and here the travel is monotonic to within a pixel. */
@media (min-width: 1200px) {
  .hr-wp { --more-h: 44px; }
}
.hr-wp *, .hr-wp *::before, .hr-wp *::after { box-sizing: border-box; }
.hr-wp__wrap { max-width: var(--container-max); margin: 0 auto; padding: 0 var(--container-pad); }

.hr-wp__eyebrow {
  display: block; font-family: var(--font-display); font-size: var(--fs-eyebrow); font-weight: 700;
  letter-spacing: .14em; text-transform: uppercase; color: var(--hr-blue); margin: 0 0 var(--sp-3);
}
.hr-wp__h2 {
  font-family: var(--font-display); font-size: clamp(28px, 3.4vw, 40px); font-weight: 800;
  line-height: 1.1; letter-spacing: -.01em; color: var(--hr-text-strong); margin: 0 0 14px;
}
/* Richtext: the theme colours every <p> dark grey, so the colour has to land on
   the <p> and not on the wrapper, or the intro renders in the wrong tone. */
.hr-wp__intro,
.hr-wp__intro p {
  font-family: var(--font-sans); font-size: var(--fs-body); line-height: 1.7;
  color: var(--hr-body); margin: 0 0 var(--sp-4);
}
.hr-wp__intro p:last-child { margin-bottom: 0; }

/* ---------- Header: copy left, photograph right ----------
   Half the container each, which is what makes the photograph read as an
   establishing shot rather than an illustration beside a paragraph. At the
   1240px container that is a 548px image, 438px tall at 5:4.

   The copy is centred against it rather than top aligned. The intro runs to
   three or four lines and the image to 438, so top aligning left the text
   stranded against the top edge with a third of the column empty under it.

   minmax(0, 1fr) on both, not 1fr: a plain 1fr floors at min-content, and the
   heading's longest word would push the columns off 50/50 at narrow widths. */
.hr-wp__head {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: clamp(24px, 4vw, 56px);
  align-items: center;
  margin-bottom: var(--sp-7);
}
/* No image set, so the copy takes the width back rather than sitting in a
   half-empty row. The measure is capped here and only here: full-bleed body
   copy across 1144px is unreadable, but inside the 548px column it never
   binds. */
.hr-wp__head--solo { grid-template-columns: minmax(0, 1fr); }
.hr-wp__head--solo .hr-wp__intro { max-width: 640px; }

/* min-width:0 so a long unbroken word in the heading cannot force the copy
   column wider than its half and push the photograph off the grid. */
.hr-wp__head-copy { min-width: 0; }
.hr-wp__head-media { margin: 0; }
/* aspect-ratio plus the width and height attributes means the row is the right
   height before the photograph arrives, so the strip below never jumps. */
.hr-wp__head-img {
  display: block; width: 100%; height: 100%;
  aspect-ratio: 5 / 4;
  object-fit: cover;
  border-radius: var(--r-md);
  box-shadow: var(--shadow-md);
}

/* One column below 900. The image goes under the copy, in source order, and
   keeps its 5:4 so it stays the same picture rather than a letterboxed crop. */
@media (max-width: 899px) {
  .hr-wp__head { grid-template-columns: minmax(0, 1fr); gap: var(--sp-6); }
  .hr-wp__intro { max-width: 640px; }
}

/* ---------- The strip ----------
   Five cards, one row, left to right. It replaced five full-width bands read
   bottom to top: each held about fifteen words in 175px of page, and five of
   them cost the best part of a thousand pixels for very little content.

   NO max-width here, and none on the rail below or on the header above. All
   three take their width from .hr-wp__wrap alone, which is --container-max
   minus a --container-pad that is itself a clamp(). Pinning any of them to a
   fixed pixel width would hold the alignment at one viewport and lose it at
   every other, which is exactly how the edges drifted apart before. The card
   width falls out of the grid: (container - 4 x 12px) / 5. */
.hr-wp__strip {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 12px;
}

.hr-wp__card {
  position: relative;
  aspect-ratio: 3 / 4;
  overflow: hidden;
  border-radius: var(--r-md);
  background: var(--hr-navy);          /* also the no-photo fallback */
  box-shadow: var(--shadow-sm);
  transition: transform var(--dur) var(--ease-out),
              box-shadow var(--dur) var(--ease-out);
}
/* :focus-within sits beside :hover on every rule below, so a keyboard reaches
   the reveal line and the lift without a pointer. */
.hr-wp__card:hover,
.hr-wp__card:focus-within {
  transform: translateY(-3px);
  box-shadow: var(--shadow-lg);
}

.hr-wp__photo {
  position: absolute; inset: 0;
  width: 100%; height: 100%;
  object-fit: cover; display: block; margin: 0;
  transition: transform 600ms var(--ease-out);
}
/* The photographs were framed for a landscape slot and are cropped to portrait
   here, so the editor picks which part survives the crop rather than
   re-shooting. Driven off a data attribute, not an inline style. */
.hr-wp__photo[data-focal="top"]    { object-position: center top; }
.hr-wp__photo[data-focal="center"] { object-position: center center; }
.hr-wp__photo[data-focal="bottom"] { object-position: center bottom; }
.hr-wp__card:hover .hr-wp__photo,
.hr-wp__card:focus-within .hr-wp__photo { transform: scale(1.06); }

/* Lighter again, shorter again, and this time shaped so there is no edge to
   see. It peaks at 71% instead of 78 and is gone by 90% of the card instead of
   running to the very top.

   THE SHAPE is what fixes the visible line. The previous ramp held flat and
   then dropped 36 points inside twelve percent of the card, and a straight
   segment that steep reads as a band however few colours are in it. This is a
   raised cosine: it leaves the plateau with zero slope and arrives at zero with
   zero slope, so neither end of the fade has a corner in it. Eleven stops
   sample that curve, which is enough that the interpolation between them is
   invisible.

   MEASURED AGAINST A REALISTIC PHOTOGRAPH, not a theoretical one. The earlier
   numbers were taken against pure white, the brightest pixel that can exist,
   which demanded 62% navy everywhere the copy sits and is what kept the tint so
   heavy. A genuinely bright tile-and-grout shot is nearer 224 grey, and that
   needs 56%. Against it: 5.68:1 at the bottom line, 6.96:1 at the title, 6.51:1
   at the reveal and 4.52:1 at the highest the copy climbs on hover.

   The trade is stated rather than hidden: over a genuinely blown-out white area
   of a photograph the topmost line on hover would land near 3.6:1. If a shot
   turns out to have a window or a wall like that behind the copy, the fix is
   --tint-peak back up, or a different crop, not lighter text. */
.hr-wp {
  --tint-peak: 71%;   /* through the copy */
  --tint-hold: 40%;   /* how far up the peak is carried */
  --tint-end:  90%;   /* where it reaches nothing */
}
.hr-wp__tint {
  position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(0deg,
    color-mix(in srgb, var(--hr-navy) var(--tint-peak), transparent) 0%,
    color-mix(in srgb, var(--hr-navy) var(--tint-peak), transparent) 36%,
    color-mix(in srgb, var(--hr-navy) 69%, transparent) 45%,
    color-mix(in srgb, var(--hr-navy) 58%, transparent) 54%,
    color-mix(in srgb, var(--hr-navy) 46%, transparent) 60%,
    color-mix(in srgb, var(--hr-navy) 34%, transparent) 66%,
    color-mix(in srgb, var(--hr-navy) 24%, transparent) 72%,
    color-mix(in srgb, var(--hr-navy) 15%, transparent) 77%,
    color-mix(in srgb, var(--hr-navy) 8%, transparent) 82%,
    color-mix(in srgb, var(--hr-navy) 3%, transparent) 86%,
    color-mix(in srgb, var(--hr-navy) 0%, transparent) 90%);
}

/* Large and quiet. It is decorative, and duplicated by the heading order and
   by the rail under the strip, so it does not have to pass AA. On a key layer
   it goes green: with the blue background tint gone, that is now the only
   marker one carries. */
.hr-wp__num {
  position: absolute; left: 11px; top: 11px; z-index: 2;
  font-family: var(--font-display); font-size: 24px; font-weight: 800; line-height: 1;
  color: color-mix(in srgb, var(--hr-text-on-color) 55%, transparent);
}
.hr-wp__card--key .hr-wp__num { color: var(--hr-green); }

.hr-wp__card-in {
  position: absolute; left: 0; right: 0; bottom: 0; z-index: 1;
  padding: 14px 14px 16px;
}
.hr-wp__title {
  font-family: var(--font-display); font-size: 14px; font-weight: 700;
  color: var(--hr-text-on-color); margin: 0 0 5px; line-height: 1.25;
}
/* Held to two lines at rest, and it opens to whatever it needs on hover.

   min-height was the wrong constraint: it set a floor and let a longer
   description push past it, which is why layer 02 still sat a line above the
   other four. This is a ceiling instead, and because --desc-h is exactly two
   line boxes the cut lands between lines rather than through one, so there is
   no sliced glyph to notice.

   overflow, never display:none or visibility:hidden, so the whole sentence
   stays in the DOM for crawlers and assistive tech even while two lines of it
   show. Hover lifts the ceiling and the rest of the sentence appears, which is
   the same moment the reveal opens and the copy is expected to move anyway. */
.hr-wp__desc {
  margin: 0; max-height: var(--desc-h); overflow: hidden;
  font-family: var(--font-sans); font-size: 12px; line-height: 1.5;
  color: color-mix(in srgb, var(--hr-text-on-color) 86%, transparent);
  transition: max-height .3s var(--ease-out);
}
.hr-wp__card:hover .hr-wp__desc,
.hr-wp__card:focus-within .hr-wp__desc { max-height: var(--desc-open); }
/* Expands on hover, as it did originally. max-height and opacity, never
   display:none or visibility:hidden, so the line stays in the DOM for crawlers
   and assistive tech whether or not anything is hovering. */
/* ONE animated property, not three. The 8px gap above this line used to be a
   margin that animated alongside the height, and the fade ran on its own
   shorter clock, so a single reveal was three interpolations landing at two
   different times. The gap is padding now, inside the clip, so max-height
   carries it for free and there is nothing to keep in step. box-sizing is
   border-box on everything in this module, so max-height:0 closes the padding
   with the text. The fade is on the same 300ms as the movement, so the line
   finishes arriving exactly when it stops moving. */
.hr-wp__more {
  max-height: 0; opacity: 0; overflow: hidden; margin: 0; padding-top: 8px;
  font-family: var(--font-sans); font-size: 11.5px; line-height: 1.45;
  /* White, not --hr-blue-100. That shade is dimmer against a light photograph
     and it was the single measurement holding the tint heavy: it needed more
     navy behind it than anything else on the card. The blue was a decorative
     accent and this is the copy people actually read. */
  color: var(--hr-text-on-color);
  transition: max-height .3s var(--ease-out),
              opacity .3s var(--ease-out);
}
.hr-wp__card:hover .hr-wp__more,
.hr-wp__card:focus-within .hr-wp__more { max-height: var(--more-h); opacity: 1; }

/* Translucent white with a blur behind it, so it reads as sitting on the photo
   rather than punched through it. The blur is progressive: without support the
   16% white still carries the label. */
/* inline-flex at a stated height rather than inline-block with vertical
   padding, so the pill's height is a number this file knows rather than
   whatever the inherited line-height works out to. The spacer below matches it. */
.hr-wp__spec {
  display: inline-flex; align-items: center;
  height: var(--spec-h); margin-top: 9px;
  font-family: var(--font-display); font-size: 10.5px; font-weight: 700; letter-spacing: .03em;
  background: color-mix(in srgb, var(--hr-text-on-color) 16%, transparent);
  -webkit-backdrop-filter: blur(3px);
  backdrop-filter: blur(3px);
  color: var(--hr-text-on-color);
  padding: 0 9px; border-radius: var(--r-pill); white-space: nowrap;
}
/* Holds the pill's place on the two layers that carry no spec, so all five
   titles land on one line at rest.

   It collapses on hover, and that is the point: the reveal opens into the empty
   space this spacer was holding rather than on top of it. Two lines of reveal
   want 42px and the spacer gives back 28, so those cards grow 14px instead of
   42 and the copy barely shifts. The three cards that carry a real pill have no
   spare space to give, so they grow the full amount, exactly as they did before
   any of this. */
   The height and the margin used to collapse as two separate interpolations.
   They are one number now, 19 for the pill plus the 9 above it, so the spacer
   still holds exactly the pill's place but gives it back in a single move. */
.hr-wp__spec-gap {
  display: block; height: calc(var(--spec-h) + 9px); margin-top: 0;
  transition: height .3s var(--ease-out);
}
.hr-wp__card:hover .hr-wp__spec-gap,
.hr-wp__card:focus-within .hr-wp__spec-gap { height: 0; }

/* ---------- Build-order rail ----------
   Replaces the old bottom-to-top footnote. The strip reads left to right now,
   so the line says which end is which. Decorative and aria-hidden: the order
   is already carried by the source order of the headings. */
.hr-wp__flow {
  display: flex; align-items: center; gap: var(--sp-3);
  margin: var(--sp-4) 0 0;
}
.hr-wp__flow-end {
  flex: none;
  font-family: var(--font-display); font-size: 11px; font-weight: 800;
  letter-spacing: .1em; text-transform: uppercase; color: var(--hr-text-muted);
}
.hr-wp__flow-line {
  flex: 1 1 auto; height: 2px; border-radius: 2px;
  background: linear-gradient(90deg, var(--hr-border), var(--hr-blue));
}

/* ---------- Responsive ---------- */

/* Three across between 720 and 999. The layout reads 3 then 2, and the orphan
   row is the better trade than a scroll on a tablet. */
@media (max-width: 999px) {
  .hr-wp__strip { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

/* ---------- Phones: one card per row, nothing to swipe ----------
   This was a horizontal swipe rail and three of the five layers could not be
   reached on a real phone. The rail measures fine under emulation, scrolls
   programmatically, and has no clipping ancestor or touch-action blocking it,
   so whatever went wrong is device specific. Two known candidates: iOS Safari
   mislays aspect-ratio flex items inside an overflow-x container, and
   scroll-snap-type x mandatory on a card taller than the viewport loses the
   gesture to vertical panning often enough that the rail reads as stuck.

   Rather than guess at a bug that cannot be reproduced here, the dependency on
   swiping is gone. One column, full width, all five in the page's own vertical
   scroll. A layer cannot be unreachable if there is nothing to reach it with.

   It costs length, and that is the trade. Five 3:4 cards at 335px wide run
   about 2300px on a 375px screen. Worth it: a build sequence with two of its
   five steps missing is not a build sequence.

   THE ASPECT RATIO STAYS 3:4. The tint ramp is a percentage of card height and
   was calibrated against that shape to clear AA where the copy sits. A shorter
   card slides the copy up into the thin end of the ramp and the contrast goes
   with it. */
@media (max-width: 719px) {
  .hr-wp__strip {
    grid-template-columns: minmax(0, 1fr);
    gap: 16px;
  }
  .hr-wp__title { font-size: 15px; }
  .hr-wp__desc { font-size: 13.5px; }
  /* One card per row, so nothing shares a row and there is no title to align
     to. Every reservation comes off, the reveal is simply always open because
     there is no hover to open it with, and the copy sits naturally against the
     bottom of the card. */
  .hr-wp__more { font-size: 12.5px; max-height: none; opacity: 1; }
  .hr-wp__desc { max-height: none; }
  .hr-wp__spec-gap { display: none; }
}

/* Touch devices have no hover, so the reveal line is permanently visible under
   the description rather than expanding into view.

   Gated on (hover: none), NOT on a width. A touch laptop at 1200px has exactly
   the same problem as a phone, and a width query would leave it with content
   reachable only by a pointer it does not have. */
@media (hover: none) {
  .hr-wp__more { max-height: none; opacity: 1; }
  .hr-wp__spec-gap { height: 0; }
  /* No hover to open it with, so a clamped description would simply lose its
     last line for good. Titles stop agreeing, which does not matter here:
     one card fills the screen. */
  .hr-wp__desc { max-height: none; }
}

@media (prefers-reduced-motion: reduce) {
  .hr-wp__card, .hr-wp__photo, .hr-wp__more, .hr-wp__spec, .hr-wp__spec-gap { transition: none !important; }
  .hr-wp__card:hover, .hr-wp__card:focus-within { transform: none !important; }
  .hr-wp__card:hover .hr-wp__photo,
  .hr-wp__card:focus-within .hr-wp__photo { transform: none !important; }
}
