/* Living Water Tech — site styles.
   Self-hosted fonts, no CDN, no inline styles: the CSP in _headers stays all-'self'.

   DESIGN INTENT (see DESIGN.md):
   The signature element is the Continuity Line — a vertical timeline that carries
   the page's structure. It exists because the one thing this company has that a
   competitor cannot fabricate is elapsed time: a system still shipping releases
   years in. The line is information, not ornament, which is why it is the spine
   rather than a decoration hung off the side. */

/* ---------- fonts ---------- */
@font-face {
  font-family: "Bricolage";
  src: url("/fonts/bricolage-var.d73fd1f9.woff2") format("woff2");
  font-weight: 200 800;
  font-stretch: 75% 100%;
  font-display: swap;
}
@font-face {
  font-family: "PublicSans";
  src: url("/fonts/publicsans-var.13dbb4bd.woff2") format("woff2");
  font-weight: 100 900;
  font-display: swap;
}
@font-face {
  font-family: "PlexMono";
  src: url("/fonts/plexmono.374e47b0.woff2") format("woff2");
  font-weight: 400;
  font-display: swap;
}

/* ---------- tokens ----------
   Palette is 6 named values, each with one job. Deliberately NOT the SaaS
   white+blue default, and deliberately not the cream/serif/terracotta cluster:
   #F3F5F3 is a cool paper, green-leaning, distinct from the warm #F4F1EA that
   AI-generated pages converge on.
   Concept: water takes its colour from what it runs through — deep water, moss,
   silt, and the mineral (brass) it leaves behind. */
/* Palette aligned to the logo (2026-08-12). The site previously ran a moss/ochre
   scheme whose primary sat at hue 164 while the logo's sits at 191 — 27 degrees
   apart, so on one screen they read as slightly dirty versions of each other.
   The logo is the brand's fixed point, so the site moves to it, not the reverse.

   The gold does NOT become one token. #E2A53C measures 1.98:1 on this paper —
   far under the 4.5:1 body text requires. So it is split: --brass is the
   text-safe darkening used for labels, --gold is the true brand gold and is
   permitted on NON-TEXT only (nodes, rules, the mark). Collapsing the two is how
   a palette quietly fails accessibility while still looking on-brand. */
:root {
  --abyss:  #0C1E24;   /* text, deep water                     */
  --moss:   #275C68;   /* headings accent, structure — logo deep */
  /* --silt was #6E827A until measured: 3.73:1 on --paper, under the 4.5:1 that
     WCAG AA requires for body text. It carried .when, .nav, .tags and every
     service description — i.e. most of the small type on the page, which is
     precisely where contrast matters most. Now 5.5:1, measured not assumed. */
  --silt:   #566E75;   /* secondary text, the line itself      */
  --paper:  #F3F5F3;   /* page ground, cool paper              */
  /* #9A7328 measured 3.95:1 — fine for the timeline dots (non-text, 3:1 applies)
     but it also carries the .how labels, which are 12.5px text needing 4.5:1.
     Darkened to 5.0:1 so one token can serve both without a special case. */
  --brass:  #8A6410;   /* TEXT-SAFE gold — labels only          */
  --gold:   #E2A53C;   /* brand gold — NON-TEXT only            */
  --live:   #2E8C9E;   /* "still running" signal — one use only*/

  --rule:   #D8E2E4;

  --step-0: 0.9375rem;
  --step-1: 1.0625rem;
  --step-2: clamp(1.25rem, 1.1rem + 0.7vw, 1.5rem);
  --step-3: clamp(1.6rem, 1.3rem + 1.5vw, 2.25rem);
  --step-4: clamp(2.4rem, 1.6rem + 4vw, 4.5rem);

  /* spacing scale — every gap comes from here, no stray values */
  --s1: 0.5rem;  --s2: 0.75rem; --s3: 1rem;   --s4: 1.5rem;
  --s5: 2rem;    --s6: 3rem;    --s7: 4.5rem; --s8: 7rem;

  --pad-x: clamp(1.25rem, 5vw, 3rem);
  --rail: 2.25rem;      /* distance from rail line to content */
}

@media (prefers-color-scheme: dark) {
  :root {
    --abyss:  #E6EEF1;
    --moss:   #8DC3CE;
    --silt:   #93A8AE;
    --paper:  #0A161B;
    --brass:  #E2A53C;
    --gold:   #E2A53C;
    --live:   #6FD8DC;
    --rule:   #1C2E35;
  }
}

*, *::before, *::after { box-sizing: border-box; }
html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  margin: 0;
  background: var(--paper);
  color: var(--abyss);
  font-family: "PublicSans", ui-sans-serif, system-ui, sans-serif;
  font-size: var(--step-1);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
}

.wrap { width: 100%; max-width: 62rem; margin-inline: auto; padding-inline: var(--pad-x); }

a { color: var(--moss); text-underline-offset: 3px; text-decoration-thickness: 1px; }
a:hover { color: var(--abyss); }

:focus-visible { outline: 2px solid var(--brass); outline-offset: 3px; }

h1, h2, h3 {
  font-family: "Bricolage", ui-sans-serif, system-ui, sans-serif;
  font-weight: 700;
  line-height: 1.05;
  margin: 0 0 var(--s3);
  font-variation-settings: "wdth" 88;
}
h1 { font-size: var(--step-4); letter-spacing: -0.035em; max-width: 14ch; }
h2 { font-size: var(--step-3); letter-spacing: -0.02em; }
h3 { font-size: var(--step-2); letter-spacing: -0.012em; }

p { margin: 0 0 var(--s3); max-width: 64ch; }
.lede { font-size: var(--step-2); color: var(--silt); line-height: 1.5; max-width: 48ch; }

.mono {
  font-family: "PlexMono", ui-monospace, monospace;
  font-size: 0.78rem;
  letter-spacing: 0.02em;
  font-variant-numeric: tabular-nums;
}

.skip { position: absolute; left: -9999px; background: var(--abyss); color: var(--paper); padding: var(--s2) var(--s3); }
.skip:focus { left: var(--s3); top: var(--s3); z-index: 20; }

/* ---------- header — deliberately not a sticky bar with a pill CTA ---------- */
.site-head { border-bottom: 1px solid var(--rule); }
.site-head .wrap {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--s4); padding-block: var(--s4);
}
.brand {
  font-family: "Bricolage", sans-serif; font-weight: 700;
  font-size: clamp(1.24rem, 1.05rem + 0.7vw, 1.6rem);
  letter-spacing: -0.032em; font-variation-settings: "wdth" 84;
  color: var(--abyss); text-decoration: none;
  display: inline-flex; align-items: center; gap: 0.6rem;
}
/* The mark carries its own dimensions so it reserves space before the SVG loads —
   an unsized image here would shift the whole header and cost CLS, which is
   currently 0 and worth keeping there. */
/* 56x53 = the mark's own 674.1 x 636.5 bounding box (ratio 1.059), not a guessed
   box: the previous mark was flat and wide (1.283), so reusing its 68x53 would
   have stretched this one. Height is held at 53 so the header's optical weight
   is unchanged. */
.brand-mark { width: 56px; height: 53px; display: block; flex: none; }
@media (max-width: 34rem) { .brand-mark { width: 41px; height: 39px; } }
.nav { display: flex; gap: var(--s4); }
.nav a { color: var(--silt); text-decoration: none; font-size: var(--step-0); }
.nav a:hover { color: var(--abyss); }

/* ---------- hero ---------- */
.hero { padding-block: var(--s8) var(--s7); }
.hero h1 em {
  font-style: normal;
  color: var(--moss);
}
.hero .lede { margin-top: var(--s4); }
.hero-act { margin-top: var(--s5); display: flex; flex-wrap: wrap; gap: var(--s4); align-items: center; }
.btn {
  font-family: "PublicSans", sans-serif; font-size: var(--step-0); font-weight: 600;
  text-decoration: none; padding: var(--s2) var(--s4);
  background: var(--abyss); color: var(--paper);
  border-radius: 2px;   /* squared — the rounded pill belongs to the timeline nodes */
}
.btn:hover { background: var(--moss); color: var(--paper); }
.btn-plain { color: var(--silt); text-decoration: none; font-size: var(--step-0); }
.btn-plain:hover { color: var(--abyss); }

/* Credibility line. Sits under the CTA rather than in a logo strip: the clients
   cannot be named, so the honest form is a typed statement of category, not a
   row of borrowed marks. */
.cred {
  margin-top: var(--s6); padding-top: var(--s3);
  border-top: 1px solid var(--rule);
  color: var(--silt); max-width: 54ch; line-height: 1.8;
}

/* ---------- THE CONTINUITY LINE (signature) ----------
   One rail down the page. Entries hang off it at their real dates. Sections do
   not restart it; that continuity IS the argument. */
.rail { position: relative; padding-left: var(--rail); }
.rail::before {
  content: "";
  position: absolute; left: 5px; top: 0; bottom: 0;
  width: 1px; background: var(--rule);
}

.node { position: relative; padding-block: var(--s5); }
.node::before {                     /* the dot on the rail */
  content: "";
  position: absolute; left: calc(-1 * var(--rail) + 1px); top: calc(var(--s5) + 0.3em);
  width: 9px; height: 9px; border-radius: 50%;
  background: var(--gold);
}
.node + .node { border-top: 1px solid var(--rule); }
.node .when { display: block; color: var(--silt); margin-bottom: var(--s2); }
.node h3 { margin-bottom: var(--s2); }
.node p { color: var(--abyss); }
.node p + p { margin-top: calc(-1 * var(--s1)); }

/* The one live signal on the page: this system is still shipping.
   Information, not decoration — and it is the only animation. */
.node.is-live::before { background: var(--live); }
.node.is-live::after {
  content: "";
  position: absolute; left: calc(-1 * var(--rail) - 3px); top: calc(var(--s5) + 0.3em - 4px);
  width: 17px; height: 17px; border-radius: 50%;
  border: 1px solid var(--live);
  animation: pulse 3.4s ease-out infinite;
}
@keyframes pulse {
  0%   { transform: scale(0.45); opacity: 0; }
  35%  { opacity: 0.75; }
  100% { transform: scale(1); opacity: 0; }
}

.tags { display: flex; flex-wrap: wrap; gap: var(--s1) var(--s3); margin: var(--s3) 0 0; padding: 0; list-style: none; }
.tags li { color: var(--silt); }
.tags li::before { content: "· "; color: var(--rule); }
.tags li:first-child::before { content: ""; }

/* ---------- sections ---------- */
section { padding-block: var(--s7); }
.band { border-top: 1px solid var(--rule); }
.eyebrow {
  color: var(--silt); margin: 0 0 var(--s4);
  display: flex; align-items: baseline; gap: var(--s2);
}
.eyebrow::after { content: ""; flex: 1; height: 1px; background: var(--rule); }

/* services: a definition list, not a card grid */
.svc { display: grid; gap: 0; margin-top: var(--s5); }
.svc > div {
  display: grid; grid-template-columns: minmax(0, 15rem) minmax(0, 1fr);
  gap: var(--s3) var(--s5); padding-block: var(--s4);
  border-top: 1px solid var(--rule);
}
/* Pin the columns explicitly. Auto-placement put a SECOND paragraph into the next
   row's first column, where it sat under the heading looking like a stray footnote
   — an entry with one paragraph looked fine, so the bug only appeared once entries
   grew a second one. The heading spans all rows so multi-paragraph entries stay in
   one column of prose. */
.svc > div > h3 { grid-column: 1; grid-row: 1 / -1; align-self: start; }
.svc > div > p  { grid-column: 2; }
.svc > div > p + p { margin-top: calc(-1 * var(--s1)); }
.svc > div:last-child { border-bottom: 1px solid var(--rule); }
.svc h3 { margin: 0; }
.svc p { margin: 0; color: var(--silt); }
.svc .lead-mark { color: var(--brass); }
@media (max-width: 44rem) {
  .svc > div { grid-template-columns: 1fr; gap: var(--s2); }
}

/* approach: numbered ONLY because these are sequential stages of an engagement */
.how { margin-top: var(--s5); display: grid; gap: var(--s5); }
.how > div { display: grid; grid-template-columns: 6.5rem minmax(0, 1fr); gap: var(--s4); }
.how .n { color: var(--brass); margin: 0; padding-top: 0.45em; grid-column: 1; }
@media (max-width: 34rem) { .how > div { grid-template-columns: 1fr; gap: var(--s1); } }
.how h3 { margin-bottom: var(--s1); }
.how p { margin: 0; color: var(--silt); }

/* ---------- contact ---------- */
.mail {
  font-family: "Bricolage", sans-serif; font-weight: 700;
  font-size: var(--step-3); letter-spacing: -0.025em; font-variation-settings: "wdth" 86;
  color: var(--abyss); text-decoration: none;
  border-bottom: 2px solid var(--gold); padding-bottom: 2px;
  display: inline-block; margin-block: var(--s3) var(--s4);
}
.mail:hover { color: var(--moss); border-color: var(--moss); }

/* ---------- footer ---------- */
.site-foot { border-top: 1px solid var(--rule); padding-block: var(--s5); color: var(--silt); }
.site-foot p { margin: 0 0 var(--s1); max-width: none; font-size: var(--step-0); }

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .node.is-live::after { animation: none; opacity: 0.5; transform: scale(1); }
  * { transition: none !important; }
}

/* ---------- the reel ----------
   A CSS-only slideshow. No JavaScript, deliberately: the CSP stays all-'self'
   with no script beyond Cloudflare's beacon, nothing blocks render, and there is
   no library to age. Five slides share one 30s timeline, each offset by 6s.

   Every slide's text is real content in the HTML, so crawlers and screen readers
   receive the whole list; the animation only chooses what is on screen. Under
   prefers-reduced-motion the stage un-stacks into a plain list, which is why the
   slides use absolute positioning rather than transforms on a track — a track
   would need JS to reset. */
.reel { margin-top: var(--s5); }
.reel-stage { position: relative; min-height: 11.5rem; }
@media (max-width: 40rem) { .reel-stage { min-height: 15rem; } }

.reel-slide {
  position: absolute; inset: 0 auto auto 0; width: 100%;
  opacity: 0;
  animation: reel-cycle 32s cubic-bezier(.33, 0, .16, 1) infinite;
}
.reel-slide:nth-child(1) { animation-delay:  0s; }
.reel-slide:nth-child(2) { animation-delay:  8s; }
.reel-slide:nth-child(3) { animation-delay: 16s; }
.reel-slide:nth-child(4) { animation-delay: 24s; }

@keyframes reel-cycle {
  0%    { opacity: 0; transform: translate3d(0, 18px, 0); }
  2%    { opacity: 1; transform: none; }
  22%   { opacity: 1; transform: none; }
  25%   { opacity: 0; transform: translate3d(0, -14px, 0); }
  100%  { opacity: 0; transform: translate3d(0, 18px, 0); }
}

.reel-kicker {
  color: var(--brass); margin: 0 0 var(--s2);
  letter-spacing: 0.16em;
}
.reel-lede {
  font-size: var(--step-2); color: var(--abyss); line-height: 1.45;
  max-width: 46ch; margin: 0;
}

/* Progress ticks: each fills across its own 8s window, so the reel reads as a
   sequence with a position rather than as content randomly swapping itself. */
.reel-ticks { display: flex; gap: 7px; margin-top: var(--s4); }
.reel-ticks i {
  width: 34px; height: 2px; background: var(--rule);
  position: relative; overflow: hidden; border-radius: 1px;
}
/* --brass, not --gold: the fill communicates position in the sequence, so it is a
   UI indicator and owes 3:1 under WCAG 1.4.11. The brand gold measures 1.98:1 on
   this paper and would fail; --brass is the same hue darkened to 4.9:1. The
   timeline dots and the mail underline keep the true gold — those are decorative
   and each sits beside its own text label. */
.reel-ticks i::after {
  content: ""; position: absolute; inset: 0; background: var(--brass);
  transform: scaleX(0); transform-origin: left center;
  animation: reel-tick 32s linear infinite;
}
.reel-ticks i:nth-child(1)::after { animation-delay:  0s; }
.reel-ticks i:nth-child(2)::after { animation-delay:  8s; }
.reel-ticks i:nth-child(3)::after { animation-delay: 16s; }
.reel-ticks i:nth-child(4)::after { animation-delay: 24s; }

@keyframes reel-tick {
  0%     { transform: scaleX(0); }
  25%    { transform: scaleX(1); }
  25.01% { transform: scaleX(0); }
  100%   { transform: scaleX(0); }
}

/* Stop on engagement. A reel takes away the reader's pace; hover and keyboard
   focus give it back, and it costs no JavaScript. Without this the reader who
   wants a second look has to wait a full cycle for the slide to come round. */
.reel:hover .reel-slide,
.reel:hover .reel-ticks i::after,
.reel:focus-within .reel-slide,
.reel:focus-within .reel-ticks i::after { animation-play-state: paused; }

/* External link marker, used for WhaleClues */
.out { color: inherit; text-decoration: none; border-bottom: 2px solid var(--gold); }
.out:hover { color: var(--moss); }

@media (prefers-reduced-motion: reduce) {
  .reel-stage { min-height: 0; }
  .reel-slide {
    position: static; opacity: 1; animation: none; transform: none;
    padding-block: var(--s3); border-top: 1px solid var(--rule);
  }
  .reel-slide:first-child { border-top: 0; }
  .reel-ticks { display: none; }
}

/* Hero subline + the differentiating claim. The subline is deliberately STATIC:
   an earlier version kept "who we build for" inside the rotating reel, so a
   visitor landing mid-cycle met a metaphor headline over jargon and could answer
   none of who/what/next. A qualifying statement must never rotate away. */
.hero-sub { margin-top: var(--s4); max-width: 54ch; color: var(--abyss); }
.hero-edge {
  margin-top: var(--s4); max-width: 50ch;
  padding-left: var(--s3); border-left: 2px solid var(--gold);
  color: var(--silt); font-size: var(--step-1);
}
.hero .cred { margin-top: var(--s5); }
.hero-act { margin-top: var(--s5); }

/* Sits with the client roster rather than in the contact block on purpose: the
   question it answers ("the one named client has no case study — why?") arises
   the moment the roster is read, so the answer belongs in the same glance. */
.cred-ref { display: block; margin-top: var(--s2); color: var(--silt); text-transform: none; letter-spacing: 0.02em; }


/* The reel is focusable so the pause actually reaches keyboard users. Without
   tabindex the :focus-within rule below was dead code — nothing inside the reel
   could take focus, so only mouse users could ever stop it. */
.reel:focus-visible { outline: 2px solid var(--brass); outline-offset: 6px; }
