/* ==================================================================
   KYNA WATER SOLUTIONS - shared stylesheet

   One theme: dark. Deep green ground, ivory type, gold as the single
   accent. One corner radius (2px). One accent, everywhere.

   Everything that carries size is fluid: type and space interpolate
   continuously between a 360px viewport and a 1600px one, so the page
   is composed at every width rather than at four breakpoints.
   ================================================================== */

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

/* The UA rule for [hidden] is display:none at the lowest specificity, so any
   later `display: grid` on the same element silently un-hides it. The buy and
   rent panes are both lists, so without this they render on top of each other. */
[hidden] { display: none !important; }

:root {
  /* ================================================================
     TWO GROUNDS, ONE SET OF COMPONENTS

     The page reads dark / light / dark: header and hero at the top,
     light body, one dark block mid-page, dark footer.

     Rather than writing every component twice, the colour tokens below
     are CONTEXTUAL. :root carries the light values; the .on-dark block
     further down re-declares the same names with dark values. Any
     component built from these tokens works on either ground with no
     extra rules, and nothing can drift out of sync.

     Fixed values (never contextual) come first.
     ================================================================ */

  /* ---- Fixed ------------------------------------------------------- */
  --ink:    #071E1B;   /* the dark ground: header, hero, mid block, footer */
  --ink-2:  #0B2724;
  --ink-3:  #10332E;
  --paper:   #F7F5F0;  /* the light ground */
  --paper-2: #EFEAE0;  /* light alternate band */

  /* The technical drawings mask their own overlaps with #0B2724 fills,
     so a drawing plate must stay exactly that colour on every ground.
     This is why plates are --plate and not --surface. */
  --plate: #0B2724;

  /* The CTA fill is the same gold on both grounds, so the button looks
     identical everywhere. --ink on it measures 6.5:1. */
  --gold-fill: #C9952A;

  /* ---- Contextual: LIGHT is the default ---------------------------- */
  --bg:        var(--paper);
  --surface:   #FFFFFF;
  --surface-2: var(--paper-2);

  --text:     #0B2724;                     /* 15.0:1 on paper */
  --text-dim: #4A5754;                     /*  7.4:1 on paper */
  --text-mut: #5C6863;                     /*  5.3:1 on paper, 4.9 on paper-2 */

  /* Gold has to darken on a light ground to stay legible: the brand
     #C9952A only reaches 2.5:1 on paper, which fails even large-text
     AA. Same hue, deeper value, so it still reads as the brand accent. */
  --gold:     #7F5C0E;                     /*  5.6:1 on paper, 5.1 on paper-2 */
  --gold-lit: #6E5009;                     /*  6.6:1 on paper, smallest text */
  /* headline italics are large text, so they can stay a brighter gold
     and still clear the 3:1 large-text bar */
  --gold-display: #A87A18;                 /*  3.5:1 on paper, large text only */
  --gold-dim: rgba(138,100,16,0.3);

  --line:      rgba(11,39,36,0.14);
  --line-soft: rgba(11,39,36,0.07);
  --line-warm: rgba(11,39,36,0.1);

  --ghost-line: rgba(11,39,36,0.28);
  --field-bg:   #F7F5F0;
  --field-line: rgba(11,39,36,0.22);
  --placeholder: #6B7671;
  --err:        #9A3412;                   /*  6.0:1 on white */
  --sheen:      rgba(11,39,36,0.03);
  --inset-hl:   rgba(11,39,36,0.04);
  --shadow-lg:  0 30px 60px -40px rgba(11,39,36,0.4);
  --shadow-sm:  0 12px 30px -18px rgba(11,39,36,0.3);
  --flag-ok:    #4A5754;
  --flag-ok-line: rgba(11,39,36,0.2);

  /* ---- Shape. Locked. ---------------------------------------------- */
  --r: 2px;

  /* ---- Fluid type scale -------------------------------------------
     Each step is clamp(min, intercept + slope*vw, max), solved so the
     min lands at 360px and the max at 1600px. */
  --t-micro: clamp(0.60rem, 0.585rem + 0.065vw, 0.65rem);
  --t-label: clamp(0.66rem, 0.643rem + 0.077vw, 0.72rem);
  --t-small: clamp(0.875rem, 0.853rem + 0.097vw, 0.95rem);
  --t-body:  clamp(0.95rem, 0.918rem + 0.142vw, 1.06rem);
  --t-lead:  clamp(1.05rem, 0.977rem + 0.323vw, 1.30rem);
  --t-h3:    clamp(1.35rem, 1.161rem + 0.839vw, 2.00rem);
  --t-h2:    clamp(1.90rem, 1.406rem + 2.194vw, 3.60rem);
  --t-h1:    clamp(2.00rem, 1.129rem + 3.871vw, 5.00rem);

  /* ---- Fluid space scale ------------------------------------------- */
  --sp-3xs: clamp(0.25rem, 0.236rem + 0.065vw, 0.3rem);
  --sp-2xs: clamp(0.5rem,  0.464rem + 0.161vw, 0.625rem);
  --sp-xs:  clamp(0.75rem, 0.692rem + 0.258vw, 0.95rem);
  --sp-s:   clamp(1rem,    0.922rem + 0.348vw, 1.27rem);
  --sp-m:   clamp(1.5rem,  1.384rem + 0.516vw, 1.9rem);
  --sp-l:   clamp(2rem,    1.681rem + 1.419vw, 3.1rem);
  --sp-xl:  clamp(3rem,    2.216rem + 3.484vw, 5.7rem);
  --sp-2xl: clamp(4rem,    2.518rem + 6.587vw, 9.1rem);

  --gutter: clamp(1.25rem, 0.887rem + 1.613vw, 2.5rem);
  --maxw:   clamp(1180px, 84vw, 1400px);
  --maxw-narrow: 720px;

  /* ---- Motion ------------------------------------------------------ */
  --ease:   cubic-bezier(0.16, 1, 0.3, 1);
  --ease-2: cubic-bezier(0.4, 0, 0.2, 1);

  /* ---- Type stacks ------------------------------------------------- */
  --font-d: "Bodoni Moda", Didot, Georgia, serif;
  --font-b: Geist, "Segoe UI", -apple-system, BlinkMacSystemFont, sans-serif;
  --font-m: "Geist Mono", ui-monospace, "Cascadia Mono", monospace;

  /* ---- Layers. Documented so nobody invents a z-999. --------------- */
  --z-glow: 0;
  --z-sticky: 50;
  --z-header: 200;
  --z-overlay: 300;
  --z-float: 400;

  /* ---- Header, fluid, and readable by the layout -------------------
     --hdr-h is the RESTING height. The hero and interior page heroes
     offset themselves by it, so it must not change on scroll; the
     compact state uses --hdr-h-min instead and, being fixed, moves
     nothing below it. */
  --hdr-h:     clamp(60px, 56px + 1.2vw, 76px);
  --hdr-h-min: clamp(50px, 46px + 0.9vw, 60px);
  --hdr-pad:     clamp(0.55rem, 0.4rem + 0.5vw, 0.95rem);
  --hdr-pad-min: clamp(0.2rem, 0.15rem + 0.2vw, 0.35rem);
}

/* both spellings: without the unprefixed one, Chrome on Android "boosts"
   small mono labels up to 16px and the type scale collapses */
html { -webkit-text-size-adjust: 100%; text-size-adjust: 100%; }

/* The canvas behind the document. Without a background here, the body's
   propagates to it, so rubber-banding past either end of the page on a
   phone shows a band of the light paper ground: a strip of near-white
   under the dark footer, which reads as the page having stopped short.
   Both ends of every page are dark (header and hero at the top, footer
   at the bottom), so the canvas is dark and the overscroll matches
   whichever end you pull. Declaring it here also stops the propagation,
   leaving body to paint its own box, which it fills. */
html { background-color: var(--ink); }
@media (prefers-reduced-motion: no-preference) { html { scroll-behavior: smooth; } }

/* ---- Contextual: DARK ---------------------------------------------
   Anything carrying .on-dark re-declares the same token names, so every
   component inside it flips ground, text, hairlines and accent together.
   The chrome (header, drawer, floating chat, footer) is always dark, so
   it opts in here rather than needing a class in the markup. */
.on-dark,
.tile-dark,
.hdr,
.mnav,
.wa,
.ftr {
  --bg:        var(--ink);
  --surface:   var(--ink-2);
  --surface-2: #0F332E;

  --text:     #F4EFE4;                     /* 14.7:1 on ink */
  --text-dim: rgba(244,239,228,0.66);      /*  6.8:1 on ink */
  --text-mut: rgba(244,239,228,0.54);      /*  4.9:1 on ink */

  --gold:     #C9952A;                     /*  6.3:1 on ink */
  --gold-lit: #E0B457;                     /*  9.1:1 on ink, small text */
  --gold-display: #C9952A;
  --gold-dim: rgba(201,149,42,0.16);

  --line:      rgba(201,149,42,0.16);
  --line-soft: rgba(201,149,42,0.09);
  --line-warm: rgba(244,239,228,0.1);

  --ghost-line: rgba(244,239,228,0.26);
  --field-bg:   rgba(244,239,228,0.045);
  --field-line: rgba(244,239,228,0.18);
  --placeholder: rgba(244,239,228,0.5);
  --err:        #F0A868;
  --sheen:      rgba(244,239,228,0.04);
  --inset-hl:   rgba(244,239,228,0.04);
  --shadow-lg:  0 40px 90px -55px rgba(0,0,0,0.9);
  --shadow-sm:  0 18px 40px -22px rgba(0,0,0,0.75);
  --flag-ok:    rgba(244,239,228,0.66);
  --flag-ok-line: rgba(244,239,228,0.1);

  background: var(--bg);
  color: var(--text-dim);
}

/* A dark section carries its own gallery light. This used to be one
   fixed layer over the whole page, which no longer works now that most
   of the page is light. Painted as a background-image on the section
   itself rather than a pseudo-element, so it cannot end up behind the
   section's own background-color. */
.on-dark,
.ftr {
  background-image:
    radial-gradient(100% 120% at 78% 0%, rgba(201,149,42,0.09), transparent 62%),
    radial-gradient(80% 100% at 4% 10%, rgba(201,149,42,0.04), transparent 60%);
}

body {
  font-family: var(--font-b);
  font-size: var(--t-body);
  font-weight: 400;
  line-height: 1.72;
  color: var(--text-dim);
  background: var(--bg);
  overflow-x: hidden;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
  text-rendering: optimizeLegibility;
}

img, svg { max-width: 100%; display: block; }
a { color: inherit; }
::selection { background: rgba(201,149,42,0.28); color: var(--text); }

:focus-visible { outline: 2px solid var(--gold-lit); outline-offset: 3px; border-radius: var(--r); }

.skip {
  position: absolute; left: -9999px; top: 0; z-index: var(--z-overlay);
  background: var(--gold-fill); color: var(--ink);
  padding: var(--sp-xs) var(--sp-s); font-family: var(--font-m); font-size: var(--t-label);
}
.skip:focus { left: 0; }

/* -- Layout ------------------------------------------------------- */
/* Anything that can be an anchor target has to clear the fixed header,
   or the browser scrolls its top edge to y=0 and the header covers it.
   Measured before this existed: 56px of every anchored section was
   hidden, on every page except the legal ones. scroll-margin costs
   nothing until something is actually scrolled to. */
[id] { scroll-margin-top: calc(var(--hdr-h-min) + var(--sp-s)); }

.wrap { width: 100%; max-width: var(--maxw); margin: 0 auto; padding: 0 var(--gutter); }
.wrap-narrow { max-width: var(--maxw-narrow); }
.sec { padding: var(--sp-2xl) 0; }
.sec-tight { padding: var(--sp-xl) 0; }

/* An alternate band is a raised surface, not a different theme.
   Hairlines rather than a hard edge, so the change reads as light
   falling on the page instead of a seam. */
/* A dark block has to echo the header and footer EXACTLY, so band-alt
   raised tint must not lighten it a step. */
.on-dark.band-alt,
.on-dark.band { background-color: var(--ink); }

.band-alt {
  background-color: var(--surface-2);
  border-top: 1px solid var(--line-soft);
  border-bottom: 1px solid var(--line-soft);
}

/* -- Spacing utilities ---------------------------------------------
   These exist so no page carries a hard-coded rem in a style attribute.
   Every one of them scales with the viewport. */
.mt-s { margin-top: var(--sp-s); }
.mt-m { margin-top: var(--sp-m); }
.mt-l { margin-top: var(--sp-l); }
.mb-s { margin-bottom: var(--sp-s); }
.mb-m { margin-bottom: var(--sp-m); }
.center { justify-content: center; }
.rule-top { margin-top: var(--sp-l); padding-top: var(--sp-m); border-top: 1px solid var(--line-soft); }

/* -- Shared type -------------------------------------------------- */
.eyebrow {
  display: inline-flex; align-items: center; gap: var(--sp-xs);
  font-family: var(--font-m);
  font-size: var(--t-label); font-weight: 500; letter-spacing: 0.2em;
  text-transform: uppercase; color: var(--gold-lit);
  margin-bottom: var(--sp-s);
}
.eyebrow::before { content: ""; width: clamp(20px, 1.6vw, 30px); height: 1px; background: currentColor; }

.title {
  font-family: var(--font-d);
  font-weight: 400;
  font-variation-settings: "opsz" 60;
  font-size: var(--t-h2);
  line-height: 1.1;
  letter-spacing: -0.018em;
  color: var(--text);
  text-wrap: balance;
}
.title em {
  font-style: italic;
  font-variation-settings: "opsz" 60;
  color: var(--gold-display);
  /* descender clearance for italic y g j p q */
  line-height: 1.16;
  padding-bottom: 0.08em;
  display: inline-block;
}
.title-sm { font-size: var(--t-h3); letter-spacing: -0.012em; }
/* --gold-display is only cleared for large text, and --t-h3 falls under
   the 24px large-text bar below a ~646px viewport. The small title
   therefore takes the deeper gold, which clears 4.5:1 at any size. On a
   dark ground the two tokens are the same value, so nothing moves. */
.title-sm em { color: var(--gold); }

.lead { font-size: var(--t-lead); line-height: 1.7; color: var(--text-dim); max-width: 56ch; font-weight: 300; }
.body-sm { font-size: var(--t-small); line-height: 1.75; color: var(--text-dim); max-width: 62ch; }

/* -- Buttons ------------------------------------------------------ */
.btn {
  display: inline-flex; align-items: center; justify-content: center; gap: var(--sp-2xs);
  white-space: nowrap;
  padding: clamp(0.8rem, 0.74rem + 0.26vw, 1rem) clamp(1.35rem, 1.12rem + 1.03vw, 2.15rem);
  border-radius: var(--r);
  border: 1px solid transparent;
  font-family: var(--font-m);
  font-size: var(--t-label); font-weight: 500; letter-spacing: 0.11em;
  text-transform: uppercase; text-decoration: none;
  cursor: pointer;
  transition: background 0.35s var(--ease-2), color 0.35s var(--ease-2),
              border-color 0.35s var(--ease-2), transform 0.18s var(--ease-2),
              box-shadow 0.35s var(--ease-2);
}
.btn i { font-size: 1.15em; }
.btn:active { transform: translateY(1px); }

/* gold fill, ink text - 6.3:1 */
.btn-primary { background: var(--gold-fill); color: var(--ink); border-color: var(--gold-fill); }
.btn-primary:hover { background: var(--gold-lit); border-color: var(--gold-lit); box-shadow: 0 10px 30px -8px rgba(201,149,42,0.4); }

/* outlined, ivory text - 14.7:1 */
.btn-ghost { background: transparent; color: var(--text); border-color: var(--ghost-line); }
.btn-ghost:hover { background: var(--sheen); border-color: var(--text); }

/* outlined in gold, gold text - 6.3:1 */
.btn-gold { background: transparent; color: var(--gold-lit); border-color: rgba(201,149,42,0.4); }
.btn-gold:hover { background: rgba(201,149,42,0.09); border-color: var(--gold); }

.btn-sm { padding: var(--sp-2xs) var(--sp-s); font-size: var(--t-micro); }
.btn-row { display: flex; flex-wrap: wrap; gap: var(--sp-2xs); }

.link-arrow {
  display: inline-flex; align-items: center; gap: var(--sp-2xs);
  min-height: 24px;   /* WCAG 2.5.8: the label is small, the target is not */
  font-family: var(--font-m); font-size: var(--t-label); letter-spacing: 0.11em;
  text-transform: uppercase; color: var(--gold-lit); text-decoration: none;
  border-bottom: 1px solid var(--gold-dim);
  padding-bottom: var(--sp-3xs);
  transition: border-color 0.3s, gap 0.3s var(--ease);
}
.link-arrow:hover { border-color: var(--gold); gap: var(--sp-s); }

/* -- Header --------------------------------------------------------
   Sits transparent over the hero, then earns a ground and a hairline
   once the page has scrolled. Driven by an IntersectionObserver on a
   sentinel, never a scroll listener. */
/* Three explicit tracks, 1fr / auto / 1fr, so the centre column is
   centred on the viewport no matter how wide the side columns are.
   The tracks are declared rather than auto-placed, and each child is
   assigned its column, because below 1000px the nav is display:none
   and auto-placement would otherwise slide the logo into column 1. */
.hdr {
  position: fixed; inset: 0 0 auto 0; z-index: var(--z-header);
  display: grid;
  /* minmax(0, ...) rather than a bare 1fr: a bare fr floors at the
     track's content width, so on a narrow viewport the hamburger would
     widen its own track and shove the centre column off-centre. */
  grid-template-columns: minmax(0, 1fr) auto minmax(0, 1fr);
  align-items: center;
  column-gap: var(--sp-s);
  height: var(--hdr-h);
  padding: var(--hdr-pad) var(--gutter);
  background: transparent;
  border-bottom: 1px solid transparent;
  box-shadow: 0 0 0 0 rgba(0,0,0,0);
  transition: height 300ms ease, padding 300ms ease, background 300ms ease,
              border-color 300ms ease, box-shadow 300ms ease,
              backdrop-filter 300ms ease;
}
/* Compact state. The header is position:fixed, so shrinking it moves
   nothing below it: no reflow, no layout jump. */
.hdr.scrolled {
  height: var(--hdr-h-min);
  padding-block: var(--hdr-pad-min);
  /* 0.94 rather than 0.86: the body behind the header is light now, and
     at 0.86 the glass washed the navy out to a grey-green */
  background: rgba(7,30,27,0.94);
  backdrop-filter: blur(20px) saturate(140%); -webkit-backdrop-filter: blur(20px) saturate(140%);
  border-bottom-color: var(--line);
  box-shadow: 0 12px 30px -14px rgba(0,0,0,0.65);
}

/* 80px tall, so the observer flips the class once that much has been
   scrolled past. Absolute and 1px wide, so it costs no layout. */
#hdr-sentinel { position: absolute; top: 0; left: 0; width: 1px; height: 80px; }

/* The mark is lifted from the supplied logo render, so it carries the
   real embossed metal. The wordmark beside it is typeset, because the
   full lockup's strapline stops being legible under about 90px. */
.hdr-logo {
  grid-column: 2; justify-self: center;
  display: flex; align-items: center; justify-content: center;
  /* Below ~320px the wordmark is gone and the mark alone is 20px across,
     under the 24px target floor. The mark keeps its size; the hit area
     grows around it. This floor replaces the min-width: 0 that used to
     sit here to stop the wordmark forcing the centre grid track wide:
     24px is far too small to blow the track out, and the side tracks are
     minmax(0, 1fr) precisely so they can give way first. */
  min-width: 24px; min-height: 24px;
  gap: 0;                       /* the wordmark carries its own margin, so
                                   collapsing it leaves no orphaned gap */
  text-decoration: none;
}
.hdr-logo img {
  height: clamp(28px, 2.4vw, 38px); width: auto;
  transition: height 300ms ease;
}
.hdr.scrolled .hdr-logo img { height: clamp(24px, 2vw, 31px); }

/* Collapses to nothing in the compact state, taking its own margin with
   it, so the mark ends up centred on the viewport. max-width is set just
   above the natural width of the block, so the collapse starts moving
   immediately rather than idling through dead space. */
.hdr-word {
  margin-left: var(--sp-xs);
  max-width: 150px;   /* natural width runs 89-105px across the range;
                         the headroom is for a font fallback rendering wider */
  overflow: hidden;
  opacity: 1;
  transition: max-width 300ms ease, margin-left 300ms ease, opacity 200ms ease;
}
.hdr.scrolled .hdr-word { max-width: 0; margin-left: 0; opacity: 0; }

.lw {
  font-family: var(--font-d); font-size: clamp(0.95rem, 0.88rem + 0.32vw, 1.2rem);
  letter-spacing: 0.26em; color: var(--text); line-height: 1; display: block;
  white-space: nowrap;
}
.ls {
  display: block; font-family: var(--font-m); font-size: clamp(0.44rem, 0.42rem + 0.1vw, 0.52rem);
  letter-spacing: 0.24em; text-transform: uppercase; color: var(--gold);
  margin-top: var(--sp-3xs);
  white-space: nowrap;
}

/* right column: CTA and hamburger, pinned to the end */
.hdr-end {
  grid-column: 3; justify-self: end;
  display: flex; align-items: center; gap: var(--sp-xs);
}

/* The drawer is the navigation at every width, so the header carries no
   link list of its own: hamburger left, logo centre, CTA right, filling
   all three tracks so the logo stays centred and nothing looks lopsided. */
.hdr-cta { flex-shrink: 0; }

.burger {
  grid-column: 1; justify-self: start;
  display: inline-flex;
  width: clamp(38px, 3vw, 44px); height: clamp(38px, 3vw, 44px);
  align-items: center; justify-content: center;
  background: transparent; border: 1px solid var(--line);
  border-radius: var(--r); color: var(--text); cursor: pointer;
  transition: border-color 0.3s, background 0.3s;
}
.burger:hover { border-color: var(--gold); background: rgba(201,149,42,0.08); }
.burger i { font-size: 1.3rem; }

.mnav {
  position: fixed; inset: var(--hdr-h) 0 0 0; z-index: var(--z-overlay);
  background: var(--bg);
  padding: var(--sp-l) var(--gutter) var(--sp-xl);
  display: flex; flex-direction: column;
  overflow-y: auto;
  transition: top 300ms ease;
}
/* the drawer starts below the header, so it has to follow it up */
.hdr.scrolled ~ .mnav { top: var(--hdr-h-min); }
.mnav a:not(.btn) {
  font-family: var(--font-d); font-size: clamp(1.5rem, 1.2rem + 1.3vw, 2.1rem);
  color: var(--text); text-decoration: none;
  padding: var(--sp-xs) 0;
  border-bottom: 1px solid var(--line-soft);
}
.mnav a:not(.btn)[aria-current="page"] { color: var(--gold); }
.mnav .btn { margin-top: var(--sp-m); width: 100%; }

/* The CTA stays in the header at every width, so it sheds weight as the
   header narrows rather than disappearing. Both thresholds come from
   measuring the gap between the three columns, not from guessing:
   clearance at the tightest point is 43px just above 620, and 44px just
   above 340, which is enough to absorb a webfont swapping in wider than
   its fallback.
   First the button drops to its icon, the same way the floating chat
   button does. Then the wordmark drops, leaving the logo mark, which is
   what the scrolled state shows anyway. */
@media (max-width: 620px) {
  .hdr-cta .cta-label { display: none; }
  .hdr-cta { padding-inline: var(--sp-xs); }
}
@media (max-width: 340px) {
  .hdr-word { display: none; }
}

/* -- Floating WhatsApp -------------------------------------------- */
.wa {
  position: fixed; right: var(--gutter); bottom: var(--gutter);
  z-index: var(--z-float);
  display: inline-flex; align-items: center; gap: var(--sp-2xs);
  padding: var(--sp-xs) var(--sp-s);
  background: var(--surface); color: var(--text); text-decoration: none;
  border: 1px solid var(--line);
  border-radius: var(--r);
  font-family: var(--font-m); font-size: var(--t-label); letter-spacing: 0.1em; text-transform: uppercase;
  box-shadow: var(--shadow-sm);
  transition: background 0.3s, border-color 0.3s, transform 0.2s var(--ease);
}
.wa:hover { background: var(--ink-3); border-color: var(--gold); }
.wa:active { transform: translateY(1px); }
.wa i { font-size: 1.2rem; color: var(--gold-lit); }
@media (max-width: 560px) { .wa span { display: none; } .wa { padding: var(--sp-xs); } }

/* -- Footer --------------------------------------------------------
   No divider above the legal line. The whitespace does that job. */
.ftr { padding: var(--sp-xl) 0 var(--sp-l); border-top: 1px solid var(--line-soft); }
/* no seam where a dark section runs straight into the dark footer */
.on-dark + .ftr { border-top-color: transparent; }
.ftr-grid {
  display: grid;
  gap: var(--sp-l) var(--sp-m);
  grid-template-columns: repeat(auto-fit, minmax(min(210px, 100%), 1fr));
  padding-bottom: var(--sp-xl);
}
.ftr-grid > :first-child { grid-column: span 1; min-width: min(280px, 100%); }
/* the footer has the room for the full lockup, strapline and all */
.ftr-logo { display: inline-flex; text-decoration: none; margin-bottom: var(--sp-s); }
.ftr-logo img { height: clamp(106px, 9vw, 140px); width: auto; }
.ftr-tag { font-size: var(--t-small); color: var(--text-dim); max-width: 32ch; line-height: 1.7; }
.ftr h3 {
  font-family: var(--font-m); font-size: var(--t-micro); font-weight: 500;
  letter-spacing: 0.18em; text-transform: uppercase;
  color: var(--gold); margin-bottom: var(--sp-s);
}
.ftr ul { list-style: none; display: grid; gap: var(--sp-2xs); }
.ftr a { color: var(--text-dim); text-decoration: none; font-size: var(--t-small); transition: color 0.25s; }
.ftr a:hover { color: var(--gold-lit); }
.ftr-legal {
  display: flex; flex-wrap: wrap; gap: var(--sp-2xs) var(--sp-m); justify-content: space-between;
  font-family: var(--font-m); font-size: var(--t-micro); letter-spacing: 0.1em;
  color: var(--text-mut);
}
/* the floating chat button is pinned to the viewport bottom, so the last
   line of the footer has to clear it when the page is scrolled to the end */
.ftr-legal { padding-bottom: clamp(3.5rem, 3.2rem + 1.2vw, 4.5rem); }

/* -- Scroll reveal ------------------------------------------------ */
.rv { opacity: 0; transform: translateY(18px); transition: opacity 0.9s var(--ease), transform 0.9s var(--ease); }
.rv.in { opacity: 1; transform: none; }
.d1 { transition-delay: 0.08s; } .d2 { transition-delay: 0.16s; }
.d3 { transition-delay: 0.24s; } .d4 { transition-delay: 0.32s; }

@media (prefers-reduced-motion: reduce) {
  *, *::before, *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
    scroll-behavior: auto !important;
  }
  .rv { opacity: 1; transform: none; }
}

/* ==================================================================
   MOTION

   The brief was luxury, and the thing that reads as expensive is not
   more movement, it is movement that looks considered: one idea
   arriving at a time, at a pace a person can follow, and never
   between a visitor and what they came for.

   So the rules here are:
   - Nothing animates that the visitor is waiting on. The page is
     legible the instant it paints; motion only sequences it.
   - Every effect is transform and opacity, which the compositor can
     run without touching layout, so none of this costs a reflow.
   - There is still not one scroll listener. Reveals are an
     IntersectionObserver, and the parallax is native scroll-driven
     CSS, which runs off the main thread entirely.
   - Everything collapses under prefers-reduced-motion, and the
     no-script stylesheet resets every initial state, or a blocked
     script would leave the page half invisible.
   ================================================================== */

/* -- Headlines rise out of a mask ---------------------------------
   Clipped rather than wrapped in an overflow box, so no extra markup
   and no risk of an inline-block italic being cut off. The negative
   insets leave room for descenders and the italic overhang. */
.rv .title,
.rv h1 {
  clip-path: inset(0 0 105% 0);
  transform: translateY(0.28em);
  transition: clip-path 1s var(--ease), transform 1s var(--ease);
}
.rv.in .title,
.rv.in h1 {
  clip-path: inset(-18% 0 -28% 0);
  transform: none;
}

/* -- The eyebrow rule draws itself in ------------------------------ */
.rv .eyebrow::before {
  transform: scaleX(0); transform-origin: left center;
  transition: transform 0.8s var(--ease) 0.15s;
}
.rv.in .eyebrow::before { transform: none; }

/* -- Children arrive in sequence, not all at once ------------------
   The container itself stops moving, because two nested transforms
   read as a wobble rather than a sequence. */
.rv.stagger { opacity: 1; transform: none; }
.rv.stagger > * {
  opacity: 0; transform: translateY(14px);
  transition: opacity 0.8s var(--ease), transform 0.8s var(--ease);
}
.rv.stagger.in > * { opacity: 1; transform: none; }
.rv.stagger.in > *:nth-child(1) { transition-delay: 0ms; }
.rv.stagger.in > *:nth-child(2) { transition-delay: 70ms; }
.rv.stagger.in > *:nth-child(3) { transition-delay: 140ms; }
.rv.stagger.in > *:nth-child(4) { transition-delay: 210ms; }
.rv.stagger.in > *:nth-child(5) { transition-delay: 280ms; }
.rv.stagger.in > *:nth-child(6) { transition-delay: 350ms; }
.rv.stagger.in > *:nth-child(7) { transition-delay: 420ms; }
.rv.stagger.in > *:nth-child(8) { transition-delay: 490ms; }
.rv.stagger.in > *:nth-child(n+9) { transition-delay: 560ms; }

/* -- The drawer pulls down from under the header ------------------- */
.mnav {
  opacity: 0; transform: translateY(-10px);
  transition: opacity 0.3s var(--ease-2), transform 0.42s var(--ease);
}
.mnav.is-open { opacity: 1; transform: none; }
.mnav a {
  opacity: 0; transform: translateY(12px);
  transition: opacity 0.45s var(--ease), transform 0.45s var(--ease), color 0.3s, border-color 0.3s;
}
.mnav.is-open a { opacity: 1; transform: none; }
.mnav.is-open a:nth-child(1) { transition-delay: 60ms; }
.mnav.is-open a:nth-child(2) { transition-delay: 105ms; }
.mnav.is-open a:nth-child(3) { transition-delay: 150ms; }
.mnav.is-open a:nth-child(4) { transition-delay: 195ms; }
.mnav.is-open a:nth-child(5) { transition-delay: 240ms; }
.mnav.is-open a:nth-child(6) { transition-delay: 285ms; }
.mnav.is-open a:nth-child(n+7) { transition-delay: 330ms; }

/* -- Buttons: a light passes over the gold ------------------------- */
.btn { position: relative; overflow: hidden; }
.btn-primary::after {
  content: ""; position: absolute; inset: 0; pointer-events: none;
  background: linear-gradient(105deg, transparent 38%, rgba(255,255,255,0.3) 50%, transparent 62%);
  transform: translateX(-130%);
  transition: transform 0.75s var(--ease);
}
.btn-primary:hover::after,
.btn-primary:focus-visible::after { transform: translateX(130%); }
.btn:hover { transform: translateY(-1px); }
.btn:active { transform: translateY(1px); transition-duration: 0.06s; }

/* -- The drawings drift against the scroll -------------------------
   Native scroll-driven animation: no listener, no JS, no main-thread
   work, and where it is unsupported the drawing simply sits still. */
@supports (animation-timeline: view()) {
  @media (prefers-reduced-motion: no-preference) {
    @keyframes kyna-drift {
      from { transform: translateY(16px); }
      to   { transform: translateY(-16px); }
    }
    .prod-media .frame.is-draw > img,
    .cell-art.is-draw > img {
      animation: kyna-drift linear both;
      animation-timeline: view();
      animation-range: cover 0% cover 100%;
    }
  }
}

@media (prefers-reduced-motion: reduce) {
  .rv .title, .rv h1 { clip-path: none; transform: none; }
  .rv .eyebrow::before { transform: none; }
  .rv.stagger > * { opacity: 1; transform: none; }
  .mnav { opacity: 1; transform: none; }
  .mnav a { opacity: 1; transform: none; }
  .btn:hover, .btn:active { transform: none; }
}

/* ==================================================================
   THE TECHNICAL DRAWINGS

   Both are inlined into the page rather than loaded through <img>,
   for one reason: vector-effect is ignored in an <img>. Verified, not
   assumed - the same glyph rendered at 1x and 4x scales identically
   with and without it. Inline, it works, and that is what lets the two
   drawings hold the same line weight.

   Why that matters here: the files are drawn at different densities,
   960 units across for the RO skid against 460 for the softener, and
   they render at different pixel widths that change with the
   breakpoint. Left to scale, one drawing's lines came out twice as
   thick as the other's on a phone. Pinned, stroke-width means screen
   pixels and a line is the same weight in both drawings, at every size,
   on every page.

   THE LADDER IS THREE WEIGHTS AND NO MORE. Both files use the same
   three numbers, and they are painted pixels:
       1.3   structure, vessels, pipe runs, the skid
       1.0   detail, bed lines, internals, ticks
       0.85  the finest detail, such as the feed pump's fins
   Adding a fourth weight to one drawing and not the other is how they
   drifted apart the first time.

   The rules for the RO drawing live here rather than in a <style>
   block inside the SVG, because inlined that would be a document-level
   inline stylesheet, which the content policy refuses. */
.dwg { display: block; height: auto; }
.dwg path, .dwg rect, .dwg circle, .dwg line, .dwg polyline, .dwg polygon {
  vector-effect: non-scaling-stroke;
}

/* The .bp-line, .bp-thin and .bp-gold rules were removed with the old
   RO schematic. The drawing that replaced it is inked with stroke
   attributes in the file itself, the way assets/softener.svg always
   was, so both drawings now carry their own weights and neither needs
   a class from here. What is still needed from this file is the
   non-scaling-stroke rule above, which is what keeps the two drawings
   at the same line weight. */

/* There is no label media query any more. Both of the ones that used
   to sit here existed only because the RO drawing carried words: one
   hid them below 620px, where nine of them painted at 5.3px and two
   collided, and the other nudged the machine 2.19% right to undo the
   off-centring that hiding them caused. The drawing has no words at
   any width now, its canvas is centred on the machine itself, and it
   needs no help from a breakpoint. If labels ever come back, both
   rules come back with them. */

/* Two phone numbers now sit under "Call us" on the contact page, so the
   links in that list stack instead of running together on one line.
   Block also gives each its own full-width hit area, which is what keeps
   both of them over the 24px target floor. */
.cinfo div > a {
  display: flex; align-items: center;
  /* 26 rather than 24: stacked, two 24px targets sit exactly 24px apart
     and only just clear WCAG 2.5.8. The extra two pixels put both the
     size and the spacing comfortably over the line instead of on it. */
  min-height: 26px;
}
