/* ============================================================
   MILK TECHNOLOGY — site stylesheet
   Palette and direction inherited from the brand kit in
   OneDrive\Milk Technology\Departments\Marketing\Branding.
   ============================================================ */

:root {
  /* Core palette — from colors.css, unchanged */
  --milk-white: #ffffff;
  --milk-off: #f4f6f8;

  /* Sampled from hero-pool.png: the average tone of the pooled milk (#c8cfdf)
     and of the shaded left side where the drop lands (#b0bdd3). The falling
     drop is tinted to these rather than to pure white, so it reads as the same
     liquid at the same contrast instead of a bright highlight against navy. */
  --milk-pool: #c8cfdf;
  --milk-pool-shaded: #b0bdd3;
  --deep-navy: #0d2547;
  --navy-raised: #163457;

  /* Extra navy steps, added here so large surfaces can be given depth
     without introducing a colour that isn't already in the brand. */
  --navy-deep: #081a34;
  --navy-abyss: #050f21;

  --tech-blue: #4fc3f0;
  --tech-blue-dim: #1e4a5c;
  --tech-blue-glow: rgba(79, 195, 240, 0.35);

  --text-primary: #ffffff;

  /* Both dim tones are set by the worst case they appear against, which is not
     the page background but the top of a .card gradient — rgba(22,52,87,.72)
     composited over --deep-navy, i.e. about #142f52. Measured there:
       --text-secondary  4.97:1     --text-muted  4.66:1
     Both clear WCAG AA (4.5:1). The previous values were 4.38:1 and 2.59:1,
     so the muted tone failed outright and the secondary failed inside cards.
     They are now close to each other because AA leaves very little room below
     4.5:1 on this palette — separate the two by size and weight rather than by
     colour if more contrast between them is wanted. */
  --text-secondary: #949dad;
  --text-muted: #8f98a8;

  --border-subtle: rgba(255, 255, 255, 0.08);
  --border-mid: rgba(255, 255, 255, 0.14);
  --shadow-color: rgba(0, 0, 0, 0.5);

  --font: "Inter", "Segoe UI", -apple-system, BlinkMacSystemFont, Roboto, Arial, sans-serif;

  /* Used for buttons, navigation and eyebrows. A grotesque with tighter,
     more even letterforms than the body face holds up far better in the
     tracked uppercase those elements are set in. */
  --font-ui: "Inter", "Segoe UI Variable Display", "Segoe UI", "Helvetica Neue",
    Helvetica, Arial, sans-serif;

  --wrap: 1200px;
  --gutter: clamp(1.25rem, 4vw, 3rem);
  --radius: 14px;
  --nav-h: 76px;
}

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

/* Inter is the face this site is designed in. It is not a system font on any
   platform, so without these rules every visitor sees a fallback and the
   typography differs on Windows, macOS and Android.

   The woff2 is committed in assets/fonts/ — it has to be, because the Pages
   deploy runs no build step, so a git-ignored font would 404 in production.
   `node scripts/get-fonts.mjs` refetches it if a checkout is missing it.

   format() is plain "woff2", not "woff2-variations". The latter is early-draft
   syntax that some engines never recognised, and an unrecognised format string
   makes the browser skip the src entirely and fall back silently. The variable
   weight axis comes from the `font-weight: 400 700` range above, not from the
   format string, so nothing is lost by declaring it the portable way.

   Self-hosted rather than loaded from fonts.googleapis.com on purpose: a
   third-party font request transmits every visitor's IP address to Google,
   which is the practice that has drawn GDPR enforcement in the EU. */
@font-face {
  font-family: "Inter";
  font-style: normal;
  font-weight: 400 700;
  font-display: swap;
  src: url("../fonts/inter-latin-variable.woff2") format("woff2");
  unicode-range: U+0000-00FF, U+0131, U+0152-0153, U+02BB-02BC, U+02C6,
    U+02DA, U+02DC, U+2000-206F, U+2074, U+20AC, U+2122, U+2191, U+2193,
    U+2212, U+2215, U+FEFF, U+FFFD;
}

html {
  scroll-behavior: smooth;
  /* Anchored sections must clear the fixed navigation bar. */
  scroll-padding-top: calc(var(--nav-h) + 1rem);
}


body {
  margin: 0;
  font-family: var(--font);
  background: var(--deep-navy);
  color: var(--text-primary);
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img,
svg {
  max-width: 100%;
}

a {
  color: var(--tech-blue);
  text-decoration: none;
}

a:hover {
  text-decoration: underline;
}

:focus-visible {
  outline: 2px solid var(--tech-blue);
  outline-offset: 3px;
  border-radius: 4px;
}

h1,
h2,
h3,
h4 {
  line-height: 1.15;
  margin: 0 0 0.6em;
  font-weight: 700;
  letter-spacing: -0.01em;
}

h1 { font-size: clamp(2.4rem, 6vw, 4.2rem); }
h2 { font-size: clamp(1.9rem, 4vw, 2.9rem); }
h3 { font-size: clamp(1.25rem, 2.2vw, 1.6rem); }
h4 { font-size: 1.05rem; }

p { margin: 0 0 1.1em; }

.skip-link {
  position: absolute;
  left: -9999px;
  top: 0;
  z-index: 200;
  background: var(--tech-blue);
  color: var(--navy-abyss);
  padding: 0.75rem 1.25rem;
  font-weight: 700;
}

.skip-link:focus {
  left: 1rem;
  top: 1rem;
}

/* --------------------------------------------------------- layout */

.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--gutter);
}

.section {
  padding-block: clamp(4rem, 9vw, 7.5rem);
  position: relative;
}

.section--tint {
  background:
    radial-gradient(ellipse 1100px 620px at 15% 0%, rgba(79, 195, 240, 0.07), transparent 62%),
    var(--navy-deep);
  border-block: 1px solid var(--border-subtle);
}

.eyebrow {
  display: inline-flex;
  align-items: center;
  gap: 0.6rem;
  font-size: 0.78rem;
  font-weight: 600;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--tech-blue);
  margin-bottom: 1rem;
}

.eyebrow::before {
  content: "";
  width: 28px;
  height: 1px;
  background: var(--tech-blue);
  opacity: 0.7;
}

.lede {
  font-size: clamp(1.05rem, 1.6vw, 1.2rem);
  color: var(--text-secondary);
  max-width: 62ch;
}

.section-head {
  max-width: 70ch;
  margin-bottom: clamp(2.5rem, 5vw, 3.75rem);
}

/* --------------------------------------------------------- navigation */

.nav {
  position: fixed;
  inset: 0 0 auto 0;
  height: var(--nav-h);
  z-index: 100;
  display: flex;
  align-items: center;
  transition: background 0.35s ease, border-color 0.35s ease, backdrop-filter 0.35s ease;
  border-bottom: 1px solid transparent;
}

/* Transparent over the hero, solid once scrolled — so the wordmark never
   sits on a busy part of the background image. */
.nav.is-stuck {
  background: rgba(8, 26, 52, 0.86);
  backdrop-filter: blur(14px);
  border-bottom-color: var(--border-subtle);
}

.nav .wrap {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1.5rem;
}

.brand {
  display: flex;
  align-items: center;
  gap: 0.7rem;
  text-decoration: none;
  flex-shrink: 0;
}

.brand:hover { text-decoration: none; }

.brand img { width: 26px; height: 32px; }

.brand-text {
  display: flex;
  align-items: baseline;
  gap: 0.5rem;
  line-height: 1;
}

.brand-text .m {
  font-size: 1.15rem;
  font-weight: 800;
  letter-spacing: 0.04em;
  color: var(--milk-white);
}

.brand-text .t {
  font-size: 0.95rem;
  font-weight: 300;
  letter-spacing: 0.1em;
  color: var(--text-secondary);
}

.nav-links {
  display: flex;
  align-items: center;
  gap: clamp(1rem, 2.4vw, 2.4rem);
  list-style: none;
  margin: 0;
  padding: 0;
}

/* Navigation items are pill buttons rather than bare text — they read as
   controls, and each one has a visible resting state, hover state and
   current-page state instead of relying on an underline alone. */
.nav-links a {
  color: #dce4ee;
  font-family: var(--font-ui);
  font-size: 0.74rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.1em;
  text-decoration: none;
  white-space: nowrap;
  padding: 0.62rem 1.05rem;
  border-radius: 999px;
  border: 1px solid transparent;
  background: rgba(255, 255, 255, 0.045);
  position: relative;
  overflow: hidden;
  transition: color 0.22s ease, background 0.22s ease, border-color 0.22s ease,
    transform 0.22s ease, box-shadow 0.22s ease;
}

.nav-links a::after {
  /* A soft blue wash that wipes upward on hover. */
  content: "";
  position: absolute;
  inset: 0;
  background: linear-gradient(180deg, rgba(79, 195, 240, 0.28), rgba(79, 195, 240, 0.1));
  opacity: 0;
  transition: opacity 0.22s ease;
  z-index: -1;
}

.nav-links a:hover {
  color: var(--milk-white);
  text-decoration: none;
  border-color: rgba(79, 195, 240, 0.55);
  background: rgba(79, 195, 240, 0.14);
  transform: translateY(-1px);
  box-shadow: 0 6px 18px -10px rgba(79, 195, 240, 0.9);
}

.nav-links a:hover::after { opacity: 1; }

.nav-links a[aria-current="page"] {
  color: var(--milk-white);
  border-color: rgba(79, 195, 240, 0.5);
  background: rgba(79, 195, 240, 0.16);
}

.nav-cta {
  background: linear-gradient(180deg, #6fd2f7, var(--tech-blue)) !important;
  color: var(--navy-abyss) !important;
  border-color: transparent !important;
  font-weight: 800 !important;
  box-shadow: 0 8px 20px -10px rgba(79, 195, 240, 0.95);
}

.nav-cta::after { display: none; }

.nav-cta:hover {
  background: linear-gradient(180deg, #8adcfa, #5cc9f3) !important;
  box-shadow: 0 12px 26px -10px rgba(79, 195, 240, 1);
}

.nav-toggle {
  display: none;
  background: none;
  border: 1px solid var(--border-mid);
  border-radius: 8px;
  width: 44px;
  height: 44px;
  cursor: pointer;
  padding: 0;
  align-items: center;
  justify-content: center;
}

.nav-toggle span,
.nav-toggle span::before,
.nav-toggle span::after {
  display: block;
  width: 20px;
  height: 2px;
  background: var(--milk-white);
  position: relative;
  transition: transform 0.25s ease, opacity 0.2s ease;
}

.nav-toggle span::before,
.nav-toggle span::after {
  content: "";
  position: absolute;
}

.nav-toggle span::before { top: -6px; }
.nav-toggle span::after { top: 6px; }

.nav-toggle[aria-expanded="true"] span { background: transparent; }
.nav-toggle[aria-expanded="true"] span::before { transform: translateY(6px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span::after { transform: translateY(-6px) rotate(-45deg); }

@media (max-width: 900px) {
  .nav-toggle { display: flex; }

  .nav-links {
    position: fixed;
    inset: var(--nav-h) 0 auto 0;
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    background: rgba(5, 15, 33, 0.97);
    backdrop-filter: blur(16px);
    border-bottom: 1px solid var(--border-subtle);
    padding: 0.5rem var(--gutter) 1.5rem;
    transform: translateY(-130%);
    transition: transform 0.3s ease;
    max-height: calc(100vh - var(--nav-h));
    overflow-y: auto;
  }

  .nav-links.is-open { transform: translateY(0); }

  .nav-links li { margin-top: 0.55rem; }

  .nav-links a {
    display: block;
    padding: 0.9rem 1.15rem;
    font-size: 1rem;
    border-radius: 11px;
    border-color: var(--border-subtle);
  }

  .nav-cta {
    margin-top: 0.9rem;
    text-align: center;
  }
}

/* --------------------------------------------------------- buttons */

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.55rem;
  padding: 1.05rem 2.1rem;
  border-radius: 8px;
  /* Set in tracked uppercase rather than sentence case. It is the single
     biggest thing that separates a button that looks like a styled link from
     one that looks like it belongs on a professional site. */
  font-family: var(--font-ui);
  font-size: 0.79rem;
  font-weight: 700;
  text-transform: uppercase;
  letter-spacing: 0.11em;
  line-height: 1;
  border: 1px solid transparent;
  cursor: pointer;
  position: relative;
  overflow: hidden;
  isolation: isolate;
  text-decoration: none;
  transition: transform 0.2s ease, box-shadow 0.25s ease, background 0.22s ease,
    border-color 0.22s ease, color 0.22s ease;
}

/* A highlight that sweeps across on hover — the detail that separates a
   styled link from something that reads as a real control. */
.btn::after {
  content: "";
  position: absolute;
  top: 0;
  bottom: 0;
  left: -60%;
  width: 45%;
  z-index: -1;
  background: linear-gradient(100deg, transparent, rgba(255, 255, 255, 0.4), transparent);
  transform: skewX(-18deg);
  transition: left 0.55s cubic-bezier(0.2, 0.7, 0.3, 1);
}

.btn:hover::after { left: 125%; }

.btn:hover {
  transform: translateY(-2px);
  text-decoration: none;
}

.btn:active { transform: translateY(0); }

.btn--primary {
  background: linear-gradient(180deg, #6fd2f7 0%, var(--tech-blue) 55%, #35b2e4 100%);
  color: var(--navy-abyss);
  box-shadow: 0 10px 26px -12px rgba(79, 195, 240, 0.95), inset 0 1px 0 rgba(255, 255, 255, 0.45);
}

.btn--primary:hover {
  background: linear-gradient(180deg, #8adcfa 0%, #5cc9f3 55%, #3cb9ea 100%);
  box-shadow: 0 16px 34px -12px rgba(79, 195, 240, 1), inset 0 1px 0 rgba(255, 255, 255, 0.55);
}

.btn--ghost {
  border-color: rgba(255, 255, 255, 0.24);
  color: var(--milk-white);
  background: rgba(255, 255, 255, 0.06);
  backdrop-filter: blur(6px);
  box-shadow: inset 0 1px 0 rgba(255, 255, 255, 0.1);
}

.btn--ghost::after { background: linear-gradient(100deg, transparent, rgba(255, 255, 255, 0.16), transparent); }

.btn--ghost:hover {
  border-color: rgba(79, 195, 240, 0.7);
  background: rgba(79, 195, 240, 0.14);
  box-shadow: 0 12px 26px -14px rgba(79, 195, 240, 0.9);
}

.btn-row {
  display: flex;
  flex-wrap: wrap;
  gap: 0.9rem;
  margin-top: 2rem;
}

/* --------------------------------------------------------- hero */

.hero {
  position: relative;
  min-height: 100svh;
  display: flex;
  align-items: center;
  overflow: hidden;
  isolation: isolate;

  /* Geometry shared by the pool image and every motion layer over it. */
  --pool-h: 48%;

  /* Measured from hero-pool.png: the horizontal position of the splash column,
     which is the centre of the ripples already present in the photograph. */
  --splash-x: 31.9%;
}

@media (max-width: 700px) {
  .hero {
    --pool-h: 34%;
  }
}

/* The sky is drawn entirely in CSS rather than taken from the source image.
   The image shipped with a permanent drip hanging in it, which fought with an
   animated drop; cropping the image down to the pool removes it, and lets the
   whole upper half be built from the brand navy with real depth in it. */
.hero__sky {
  position: absolute;
  inset: 0;
  z-index: 0;
  background:
    radial-gradient(ellipse 1500px 820px at 50% -12%, rgba(79, 195, 240, 0.13), transparent 62%),
    radial-gradient(ellipse 1000px 620px at 84% 16%, rgba(31, 77, 120, 0.5), transparent 68%),
    radial-gradient(ellipse 900px 560px at 8% 42%, rgba(9, 30, 60, 0.85), transparent 72%),
    linear-gradient(180deg, #071b37 0%, var(--deep-navy) 30%, #0a1d39 58%, #041129 80%, #010c30 100%);
}

/* The pool. Stretched to the element rather than cropped by `cover`, so a
   given x% always lands on the same feature of the image — which is what
   keeps the falling drop lined up with the splash column at every width. */
.hero__pool {
  position: absolute;
  left: 0;
  right: 0;
  bottom: 0;
  height: var(--pool-h);
  z-index: 1;
  /* The PNG first, as the fallback any browser understands; image-set then
     overrides it where AVIF or WebP is supported. Same 1536x359 image in all
     three — 628 KB as PNG, 22 KB as WebP, 19 KB as AVIF. This is the largest
     asset on the most important page load on the site, so the saving lands
     directly on Largest Contentful Paint.

     A browser that does not understand image-set ignores the whole
     declaration and keeps the PNG from the line above, which is why that line
     must stay. */
  background-image: url("../img/hero-pool.png");
  background-image: image-set(
    url("../img/hero-pool.avif") type("image/avif"),
    url("../img/hero-pool.webp") type("image/webp"),
    url("../img/hero-pool.png") type("image/png")
  );
  background-size: 100% 100%;
  background-position: center bottom;
  background-repeat: no-repeat;
  /* Feathers the top edge into the CSS sky so there is no visible seam. */
  -webkit-mask-image: linear-gradient(to bottom, transparent 0, #000 9%);
  mask-image: linear-gradient(to bottom, transparent 0, #000 9%);
  overflow: visible;
}

/* Keeps the headline legible over the brightest part of the pool. */
.hero__scrim {
  position: absolute;
  inset: 0;
  z-index: 2;
  background:
    linear-gradient(100deg, rgba(4, 12, 28, 0.62) 0%, rgba(4, 12, 28, 0.34) 46%, rgba(4, 12, 28, 0) 76%),
    linear-gradient(180deg, rgba(4, 12, 28, 0.55) 0%, transparent 24%);
  pointer-events: none;
}

/* On a short window the scroll cue and the buttons compete for the same
   strip of pixels; the cue is the one that can go. */
@media (max-height: 760px) {
  .hero__scroll { display: none; }
}

/* Deliberately not given its own max-width: it keeps .wrap's, so the hero
   copy starts on the same left edge as every section below it. The
   individual text elements carry their own measure instead. */
.hero__inner {
  position: relative;
  z-index: 6;
  padding-block: calc(var(--nav-h) + 2.5rem) 5rem;
}

/* The consent sheet is anchored to the bottom of a phone screen, which is
   exactly where the hero's two buttons fall. `consent-pending` is on <html>
   only for as long as the sheet is showing, so the hero lifts its content
   clear while the visitor decides and settles back once they have. */
@media (max-width: 640px) {
  .hero__inner {
    transition: padding 0.45s cubic-bezier(0.2, 0.7, 0.3, 1);
  }

  /* `padding-block-end`, not `padding-bottom`: the base rule above sets the
     logical `padding-block`, and a physical longhand does not reliably win
     against it whatever its specificity. Same property, so this just wins. */
  .consent-pending .hero__inner { padding-block-end: 15rem; }
}

/* The copy runs down over the pool, which is almost white and would swallow
   it. This is a soft pool of shade behind the text — full-bleed rather than
   sized to the text block, because a gradient confined to the content box
   gets clipped at its edges and reads as a rectangular panel. It sits under
   the ripples and sparks so those stay bright through it. */
.hero__shade {
  position: absolute;
  inset: 0;
  z-index: 3;
  pointer-events: none;
  background: radial-gradient(
    ellipse 62% 80% at 25% 46%,
    rgba(3, 11, 28, 0.94) 0%,
    rgba(3, 11, 28, 0.78) 38%,
    rgba(3, 11, 28, 0.36) 64%,
    rgba(3, 11, 28, 0) 84%
  );
}

/* The wordmark is the hero's headline. It carries the brand at full size,
   which pushes the proposition line below it down to a supporting weight. */
.hero__lockup {
  display: flex;
  align-items: baseline;
  gap: clamp(0.7rem, 1.6vw, 1.4rem);
  margin: 0 0 0.45rem;
  line-height: 1;
}

.hero__lockup .milk {
  font-size: clamp(3.1rem, 8.6vw, 6.4rem);
  font-weight: 800;
  letter-spacing: 0.01em;
  color: var(--milk-white);
  text-shadow: 0 6px 34px rgba(0, 0, 0, 0.55);
}

.hero__lockup .bar {
  width: 3px;
  align-self: stretch;
  margin-block: 0.6rem 0.35rem;
  background: linear-gradient(180deg, var(--tech-blue), rgba(79, 195, 240, 0.15));
  border-radius: 2px;
  flex-shrink: 0;
}

.hero__lockup .technology {
  font-size: clamp(1.5rem, 4.2vw, 3.1rem);
  font-weight: 200;
  letter-spacing: 0.09em;
  color: #b9c6d6;
}

.hero__tagline {
  font-size: clamp(0.68rem, 1.15vw, 0.84rem);
  letter-spacing: 0.42em;
  text-transform: uppercase;
  color: var(--tech-blue);
  margin-bottom: 1.9rem;
}

/* Sits under the wordmark, so it is deliberately smaller than it would be
   as a standalone headline. */
.hero__prop {
  font-size: clamp(1.35rem, 2.7vw, 2.05rem);
  font-weight: 600;
  line-height: 1.25;
  letter-spacing: -0.01em;
  color: var(--milk-white);
  margin-bottom: 0.9rem;
  max-width: 22ch;
}

.hero__prop .accent { color: var(--tech-blue); }

.hero__lede {
  font-size: clamp(0.98rem, 1.5vw, 1.12rem);
  color: #bcc7d6;
  max-width: 38rem;
}

.hero__scroll {
  position: absolute;
  left: 50%;
  bottom: 1.5rem;
  transform: translateX(-50%);
  z-index: 7;
  color: #dfe8f2;
  font-size: 0.7rem;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  font-weight: 600;
  display: flex;
  flex-direction: column;
  align-items: center;
  gap: 0.45rem;
  text-decoration: none;
  /* The pool behind this is near-white, so the cue needs its own surface
     to stay readable rather than relying on the background. */
  background: rgba(4, 14, 32, 0.72);
  backdrop-filter: blur(8px);
  border: 1px solid rgba(255, 255, 255, 0.18);
  border-radius: 999px;
  padding: 0.7rem 1.05rem 0.6rem;
  transition: background 0.25s ease, border-color 0.25s ease, color 0.25s ease;
}

.hero__scroll:hover {
  text-decoration: none;
  color: var(--milk-white);
  background: rgba(4, 14, 32, 0.9);
  border-color: rgba(79, 195, 240, 0.6);
}

.hero__scroll .chev {
  width: 18px;
  height: 18px;
  border-right: 2px solid currentColor;
  border-bottom: 2px solid currentColor;
  transform: rotate(45deg);
  animation: nudge 2.2s ease-in-out infinite;
}

@keyframes nudge {
  0%, 100% { transform: rotate(45deg) translate(0, 0); opacity: 0.5; }
  50%      { transform: rotate(45deg) translate(3px, 3px); opacity: 1; }
}

/* ---- hero motion layer ------------------------------------------------
   .hero__surface exactly overlays .hero__pool. Because the pool image is
   painted with `background-size: 100% 100%`, any percentage inside this box
   lands on the same feature of the image at every viewport width — which is
   how the ripples stay centred on the splash column at 31.9%.

   The looping layers keep running until site.js adds `.is-settled`, which
   eases them to rest rather than cutting them dead. */

.hero__surface {
  position: absolute;
  pointer-events: none;
  left: 0;
  right: 0;
  bottom: 0;
  height: var(--pool-h);
  z-index: 4;
}

/* ---- ripples on the surface ---- */

/* Centred on the splash column in the photograph, so the rings spread from the
   same point as the concentric rings already in the image rather than from an
   arbitrary spot on the surface. */
.ring {
  position: absolute;
  left: var(--splash-x);
  top: 41.8%;              /* where the splash column meets the pool */
  width: 54%;
  height: 42%;
  translate: -50% -50%;
  /* Tinted to the pool rather than pure white — a white ring on #b0bdd3 milk
     reads as a drawn-on circle. */
  border: 1.5px solid rgba(200, 207, 223, 0.42);
  border-radius: 50%;
  opacity: 0;
  transform: scale(0.08);
  animation-name: ringIdle;
  animation-timing-function: cubic-bezier(0.2, 0.55, 0.3, 1);
  animation-duration: 7.5s;
  animation-iteration-count: infinite;
  transition: opacity 1.8s ease;
}

/* Staggered so a ring is always somewhere mid-spread and the pool never
   reads as still. */
.ring.o1 { animation-delay: 0s; }
.ring.o2 { animation-delay: 1.5s; }
.ring.o3 { animation-delay: 3s; }
.ring.o4 { animation-delay: 4.5s; }
.ring.o5 { animation-delay: 6s; }

@keyframes ringIdle {
  0%   { transform: scale(0.1); opacity: 0; }
  14%  { opacity: 0.4; }
  100% { transform: scale(1.15); opacity: 0; }
}

/* ---- circuit overlay ---- */

/* preserveAspectRatio is "none" so the traces track the stretched pool image;
   non-scaling-stroke then keeps the line weight even when they do. */
.hero__circuit {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
}

.circuit-line {
  fill: none;
  stroke: #a8e4ff;
  stroke-width: 1.3;
  stroke-linecap: round;
  stroke-linejoin: round;
  vector-effect: non-scaling-stroke;
  opacity: 0.5;
  filter: drop-shadow(0 0 3px rgba(79, 195, 240, 0.9));
  animation: circuitFlicker 8.4s steps(1, end) infinite;
  transition: opacity 1.8s ease;
}

.circuit-line.f2 { animation-duration: 10.8s; animation-delay: 0.8s; }
.circuit-line.f3 { animation-duration: 7.4s;  animation-delay: 1.7s; }
.circuit-line.f4 { animation-duration: 11.7s; animation-delay: 0.3s; }
.circuit-line.f5 { animation-duration: 9.5s;  animation-delay: 2.4s; }
.circuit-line.f6 { animation-duration: 10s;   animation-delay: 3.1s; }
.circuit-line.f7 { animation-duration: 7.9s;  animation-delay: 1.2s; }

/* Deliberately irregular — an even pulse reads as a loading indicator,
   whereas uneven steps read as current arcing along the trace. */
@keyframes circuitFlicker {
  0%   { opacity: 0.22; }
  3%   { opacity: 0.95; }
  5%   { opacity: 0.3; }
  8%   { opacity: 0.85; }
  11%  { opacity: 0.25; }
  30%  { opacity: 0.3; }
  32%  { opacity: 1; }
  34%  { opacity: 0.35; }
  37%  { opacity: 0.7; }
  40%  { opacity: 0.28; }
  64%  { opacity: 0.26; }
  66%  { opacity: 0.9; }
  68%  { opacity: 0.32; }
  71%  { opacity: 0.98; }
  74%  { opacity: 0.3; }
  100% { opacity: 0.24; }
}

/* A bright head of current running the length of each trace. */
.circuit-pulse {
  fill: none;
  stroke: #ffffff;
  stroke-width: 2.4;
  stroke-linecap: round;
  vector-effect: non-scaling-stroke;
  stroke-dasharray: 26 2000;
  filter: drop-shadow(0 0 6px rgba(140, 220, 255, 1));
  opacity: 0.9;
  animation: circuitPulse 5.2s linear infinite;
  transition: opacity 1.8s ease;
}

.circuit-pulse.f2 { animation-duration: 6.7s; animation-delay: 0.8s; }
.circuit-pulse.f3 { animation-duration: 4.6s; animation-delay: 1.7s; }
.circuit-pulse.f4 { animation-duration: 7.3s; animation-delay: 0.3s; }
.circuit-pulse.f5 { animation-duration: 5.9s; animation-delay: 2.4s; }
.circuit-pulse.f6 { animation-duration: 6.2s; animation-delay: 3.1s; }
.circuit-pulse.f7 { animation-duration: 4.9s; animation-delay: 1.2s; }

@keyframes circuitPulse {
  0%   { stroke-dashoffset: 2026; opacity: 0; }
  6%   { opacity: 0.95; }
  55%  { opacity: 0.95; }
  62%  { stroke-dashoffset: 0; opacity: 0; }
  100% { stroke-dashoffset: 0; opacity: 0; }
}

/* ---- sparks ---- */

/* Positioned on circuit nodes measured from the pool image itself, so each
   spark sits on a marking that is actually there. */
.spark {
  position: absolute;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  translate: -50% -50%;
  background: #e8f8ff;
  box-shadow:
    0 0 6px 2px rgba(120, 214, 255, 0.95),
    0 0 16px 6px rgba(79, 195, 240, 0.5);
  opacity: 0;
  transform: scale(0.35);
  animation: sparkFire 4.8s steps(1, end) infinite;
  transition: opacity 1.8s ease;
}

@keyframes sparkFire {
  0%,  100% { opacity: 0;    transform: scale(0.35); }
  2%        { opacity: 1;    transform: scale(1.55); }
  4%        { opacity: 0.25; transform: scale(0.85); }
  6%        { opacity: 0.95; transform: scale(1.2); }
  9%        { opacity: 0;    transform: scale(0.4); }
  46%       { opacity: 0;    transform: scale(0.4); }
  48%       { opacity: 1;    transform: scale(1.4); }
  50%       { opacity: 0.3;  transform: scale(0.9); }
  53%       { opacity: 0.85; transform: scale(1.15); }
  57%       { opacity: 0;    transform: scale(0.4); }
}

/* ---- gentle swirl ---- */

.hero__swirl {
  position: absolute;
  left: -6%;
  right: -6%;
  bottom: -8%;
  height: calc(var(--pool-h) + 10%);
  z-index: 2;
  background: radial-gradient(ellipse at 40% 62%, rgba(255, 255, 255, 0.06), transparent 60%);
  animation: swirl 24s ease-in-out infinite;
  transition: opacity 1.8s ease;
  pointer-events: none;
}

@keyframes swirl {
  0%, 100% { transform: translate3d(0, 0, 0) scale(1); }
  33%      { transform: translate3d(1.5%, -0.6%, 0) scale(1.01); }
  66%      { transform: translate3d(-1.3%, 0.5%, 0) scale(0.993); }
}

/* ---- settled ---- */

.hero.is-settled .circuit-line { opacity: 0.3; }
.hero.is-settled .circuit-pulse,
.hero.is-settled .spark,
.hero.is-settled .ring { opacity: 0; }
.hero.is-settled .hero__swirl { opacity: 0.5; }

.hero.is-settled .ring,
.hero.is-settled .circuit-line,
.hero.is-settled .circuit-pulse,
.hero.is-settled .spark,
.hero.is-settled .hero__swirl {
  animation-play-state: paused;
}

/* Headline entrance — deliberately early, so the proposition is readable
   almost immediately rather than waiting on anything else. */
.reveal {
  opacity: 0;
  transform: translateY(18px);
  animation: reveal 0.85s cubic-bezier(0.2, 0.7, 0.3, 1) forwards;
}

.reveal.r-1 { animation-delay: 0.15s; }
.reveal.r-2 { animation-delay: 0.32s; }
.reveal.r-3 { animation-delay: 0.5s; }
.reveal.r-4 { animation-delay: 0.68s; }

@keyframes reveal {
  to { opacity: 1; transform: translateY(0); }
}

/* --------------------------------------------------------- cards */

.grid {
  display: grid;
  gap: 1.5rem;
}

.grid--2 { grid-template-columns: repeat(auto-fit, minmax(320px, 1fr)); }
.grid--3 { grid-template-columns: repeat(auto-fit, minmax(290px, 1fr)); }
.grid--4 { grid-template-columns: repeat(auto-fit, minmax(230px, 1fr)); }

/* Five service cards. A narrower minimum and a tighter gutter so all five sit
   on one row at desktop — at the grid--4 sizing the fifth wrapped onto a line
   of its own and read as an afterthought. */
.grid--5 {
  grid-template-columns: repeat(auto-fit, minmax(198px, 1fr));
  gap: 1.15rem;
}

/* Between roughly 950 and 1180px only four fit, leaving a single stranded
   card. Three-then-two reads as a deliberate pair rather than an orphan. */
@media (min-width: 950px) and (max-width: 1180px) {
  .grid--5 { grid-template-columns: repeat(3, 1fr); }
}

.grid--5 .card { padding: 1.6rem 1.35rem; }
.grid--5 .card h3 { font-size: 1.12rem; }
.grid--5 .card p { font-size: 0.9rem; }

.card {
  background: linear-gradient(160deg, rgba(22, 52, 87, 0.72), rgba(8, 26, 52, 0.72));
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius);
  padding: 1.9rem 1.75rem;
  position: relative;
  overflow: hidden;
  transition: transform 0.3s ease, border-color 0.3s ease, box-shadow 0.3s ease;
  display: flex;
  flex-direction: column;
}

.card::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 2px;
  background: linear-gradient(90deg, transparent, var(--tech-blue), transparent);
  opacity: 0;
  transition: opacity 0.3s ease;
}

.card:hover {
  transform: translateY(-5px);
  border-color: rgba(79, 195, 240, 0.4);
  box-shadow: 0 18px 40px -20px rgba(0, 0, 0, 0.8);
}

.card:hover::before { opacity: 1; }

.card h3 { margin-bottom: 0.55rem; }

.card p {
  color: var(--text-secondary);
  font-size: 0.96rem;
  margin-bottom: 0;
}

.card__icon {
  width: 46px;
  height: 46px;
  border-radius: 11px;
  background: rgba(79, 195, 240, 0.12);
  border: 1px solid rgba(79, 195, 240, 0.26);
  display: grid;
  place-items: center;
  margin-bottom: 1.15rem;
  color: var(--tech-blue);
  flex-shrink: 0;
}

.card__icon svg { width: 22px; height: 22px; }

.card__link {
  margin-top: 1.25rem;
  font-weight: 600;
  font-size: 0.92rem;
  display: inline-flex;
  align-items: center;
  gap: 0.4rem;
}

.card--link { text-decoration: none; color: inherit; }
.card--link:hover { text-decoration: none; }

/* --------------------------------------------------------- service detail */

.service-block {
  scroll-margin-top: calc(var(--nav-h) + 2rem);
  padding-block: clamp(2.75rem, 5vw, 4rem);
  border-top: 1px solid var(--border-subtle);
}

.service-block:first-of-type { border-top: 0; }

.service-block > h3 {
  color: var(--milk-white);
  font-size: clamp(1.5rem, 2.8vw, 2rem);
}

.service-block p { color: var(--text-secondary); }

.sub-service {
  margin-top: 2.25rem;
  padding-left: 1.5rem;
  border-left: 2px solid rgba(79, 195, 240, 0.28);
}

.sub-service h4 {
  color: var(--milk-white);
  font-size: 1.18rem;
  margin-bottom: 0.5rem;
}

.tick-list {
  list-style: none;
  padding: 0;
  margin: 1.1rem 0 0;
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(255px, 1fr));
  gap: 0.6rem 1.75rem;
}

.tick-list li {
  position: relative;
  padding-left: 1.7rem;
  color: var(--text-secondary);
  font-size: 0.95rem;
}

.tick-list li::before {
  content: "";
  position: absolute;
  left: 0;
  top: 0.55em;
  width: 7px;
  height: 7px;
  border-radius: 50%;
  background: var(--tech-blue);
  box-shadow: 0 0 0 3px rgba(79, 195, 240, 0.15);
}

/* Sticky in-page contents for the long services page. */
.toc {
  position: sticky;
  top: calc(var(--nav-h) + 1.5rem);
  align-self: start;
  border-left: 1px solid var(--border-subtle);
  padding-left: 1.25rem;
}

.toc h2 {
  font-size: 0.74rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 0.9rem;
}

.toc ul { list-style: none; margin: 0; padding: 0; }

.toc a {
  display: block;
  padding: 0.38rem 0;
  color: var(--text-secondary);
  font-size: 0.92rem;
  text-decoration: none;
  border-left: 2px solid transparent;
  margin-left: -1.25rem;
  padding-left: 1.25rem;
  transition: color 0.2s ease, border-color 0.2s ease;
}

.toc a:hover,
.toc a.is-active {
  color: var(--milk-white);
  border-left-color: var(--tech-blue);
  text-decoration: none;
}

.layout-sidebar {
  display: grid;
  grid-template-columns: 230px 1fr;
  gap: clamp(2rem, 5vw, 4rem);
  align-items: start;
}

/* Below the sidebar breakpoint the contents list used to be dropped
   altogether, which left the services page as roughly fifteen phone screens of
   continuous scroll with no way to skip ahead — the readers most in need of it
   were the ones losing it. It becomes a scrollable strip of chips pinned under
   the nav instead, driven by the same active-link observer as the sidebar. */
@media (max-width: 940px) {
  /* Block rather than a single-column grid: a grid item's sticky travel is
     limited to its own grid area, which for a one-column layout is the height
     of the strip itself, so it would never actually stick. */
  .layout-sidebar { display: block; }

  .toc {
    top: var(--nav-h);
    z-index: 40;
    border-left: 0;
    padding: 0.55rem 0;
    margin-bottom: 1.5rem;
    background: rgba(8, 26, 52, 0.92);
    backdrop-filter: blur(12px);
    /* Pulled out to the screen edges and padded back in, so the strip spans
       the full width and the last chip scrolls past the edge rather than
       stopping short of it. */
    margin-inline: calc(var(--gutter) * -1);
    padding-inline: var(--gutter);
  }

  /* "On this page" is redundant next to a row of page links, but the nav still
     needs a name for anyone listening to it rather than looking at it. */
  .toc h2 {
    position: absolute;
    width: 1px;
    height: 1px;
    overflow: hidden;
    clip-path: inset(50%);
    white-space: nowrap;
  }

  .toc ul {
    display: flex;
    gap: 0.5rem;
    overflow-x: auto;
    scrollbar-width: none;
    -webkit-overflow-scrolling: touch;
  }

  .toc ul::-webkit-scrollbar { display: none; }

  .toc li { flex: 0 0 auto; }

  .toc a {
    display: flex;
    align-items: center;
    /* A thumb needs 44px; the chip's text alone gives 42. */
    min-height: 44px;
    margin-left: 0;
    padding: 0 0.9rem;
    border: 1px solid var(--border-subtle);
    border-radius: 999px;
    font-size: 0.85rem;
    white-space: nowrap;
  }

  .toc a:hover,
  .toc a.is-active {
    border-color: var(--tech-blue);
    background: rgba(79, 195, 240, 0.14);
  }

  /* Jumping to a service has to clear the nav and the strip, not just the nav. */
  .service-block { scroll-margin-top: calc(var(--nav-h) + 4.5rem); }
}

/* --------------------------------------------------------- sectors */

/* A <button> may only contain phrasing content, so everything inside the
   trigger is a <span>. Putting an <h3> or a <p> in there makes the parser
   hoist it out of the button, which silently breaks both the chevron state
   and the sibling relationship the panel depends on. The heading semantics
   come from the <h3> wrapping the button instead. */
.sector-h {
  margin: 0;
  font-size: inherit;
  font-weight: inherit;
}

.sector {
  cursor: pointer;
  text-align: left;
  font: inherit;
  color: inherit;
  display: block;
  width: 100%;
  background: linear-gradient(160deg, rgba(22, 52, 87, 0.72), rgba(8, 26, 52, 0.72));
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius);
  padding: 1.75rem;
  transition: transform 0.3s ease, border-color 0.3s ease;
}

.sector:hover {
  transform: translateY(-4px);
  border-color: rgba(79, 195, 240, 0.4);
}

.sector[aria-expanded="true"] {
  border-color: var(--tech-blue);
  border-bottom-left-radius: 0;
  border-bottom-right-radius: 0;
}

.sector__head {
  display: flex;
  align-items: center;
  gap: 0.9rem;
}

.sector__name {
  font-size: 1.2rem;
  font-weight: 700;
  line-height: 1.2;
  color: var(--milk-white);
}

.sector__blurb {
  display: block;
  color: var(--text-secondary);
  font-size: 0.95rem;
  margin-top: 0.7rem;
}

.sector__chev {
  margin-left: auto;
  width: 10px;
  height: 10px;
  border-right: 2px solid var(--tech-blue);
  border-bottom: 2px solid var(--tech-blue);
  transform: rotate(45deg);
  transition: transform 0.3s ease;
  flex-shrink: 0;
}

.sector[aria-expanded="true"] .sector__chev { transform: rotate(-135deg); }

/* Height is animated from JS using the measured content height. The tidier
   `grid-template-rows: 0fr -> 1fr` technique does not work here: the inner
   element needs `overflow: hidden` while it collapses, and that zeroes its
   contribution to the flexible track, so the row resolves to the padding
   alone and the panel never opens. */
.sector__panel {
  overflow: hidden;
  height: 0;
  transition: height 0.4s cubic-bezier(0.2, 0.7, 0.3, 1);
  background: linear-gradient(160deg, rgba(22, 52, 87, 0.5), rgba(8, 26, 52, 0.5));
  border: 1px solid transparent;
  border-top: 0;
  border-radius: 0 0 var(--radius) var(--radius);
}

.sector__panel.is-open {
  border-left-color: var(--tech-blue);
  border-right-color: var(--tech-blue);
  border-bottom-color: var(--tech-blue);
}

.sector__panel > div { padding: 0.25rem 1.75rem 1.6rem; }

.sector__panel p {
  color: var(--text-secondary);
  font-size: 0.95rem;
  margin: 1rem 0 0;
}

/* --------------------------------------------------------- article / news */

.article-card {
  display: flex;
  flex-direction: column;
  background: linear-gradient(160deg, rgba(22, 52, 87, 0.72), rgba(8, 26, 52, 0.72));
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius);
  overflow: hidden;
  transition: transform 0.3s ease, border-color 0.3s ease;
}

.article-card:hover {
  transform: translateY(-4px);
  border-color: rgba(79, 195, 240, 0.4);
}

.article-card__media {
  aspect-ratio: 16 / 9;
  background: var(--navy-abyss);
  position: relative;
  overflow: hidden;
  display: grid;
  place-items: center;
}

.article-card__media svg { width: 100%; height: 100%; display: block; }

.article-card__body {
  padding: 1.5rem 1.6rem 1.75rem;
  display: flex;
  flex-direction: column;
  flex: 1;
}

.tag {
  display: inline-block;
  font-size: 0.7rem;
  font-weight: 700;
  letter-spacing: 0.12em;
  text-transform: uppercase;
  color: var(--tech-blue);
  background: rgba(79, 195, 240, 0.1);
  border: 1px solid rgba(79, 195, 240, 0.22);
  border-radius: 999px;
  padding: 0.25rem 0.7rem;
  margin-bottom: 0.9rem;
  align-self: flex-start;
}

.article-card h3 { font-size: 1.15rem; }

.article-card p {
  color: var(--text-secondary);
  font-size: 0.94rem;
  flex: 1;
}

.meta {
  font-size: 0.8rem;
  color: var(--text-muted);
  margin-top: 1rem;
}

/* --------------------------------------------------------- notice */

.notice {
  border: 1px dashed rgba(79, 195, 240, 0.45);
  background: rgba(79, 195, 240, 0.06);
  border-radius: var(--radius);
  padding: 1.5rem 1.75rem;
}

.notice h2 { color: var(--tech-blue); font-size: 1.1rem; }
.notice p { color: var(--text-secondary); margin-bottom: 0; }

.placeholder-flag {
  background: rgba(255, 196, 61, 0.12);
  border: 1px solid rgba(255, 196, 61, 0.45);
  color: #ffc43d;
  border-radius: 6px;
  padding: 0.1rem 0.5rem;
  font-size: 0.78rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  white-space: nowrap;
}

/* --------------------------------------------------------- headshot */

/* Frame reserves the space a founder photograph will occupy. The frame and a
   real <img> share the same 4:5 ratio and max width, so swapping one for the
   other does not move anything else on the page. */
.headshot {
  margin: 0;
  max-width: 320px;
  margin-inline: auto;
  width: 100%;
}

.headshot__frame,
.headshot img {
  aspect-ratio: 4 / 5;
  border-radius: var(--radius);
  width: 100%;
}

.headshot__frame {
  border: 1px dashed rgba(79, 195, 240, 0.45);
  background: linear-gradient(160deg, rgba(22, 52, 87, 0.55), rgba(8, 26, 52, 0.55));
  display: grid;
  align-content: center;
  justify-items: center;
  gap: 1rem;
  padding: 1.5rem;
}

.headshot__frame svg {
  width: 58px;
  height: 58px;
  color: var(--tech-blue);
  opacity: 0.65;
}

.headshot img {
  display: block;
  object-fit: cover;
  border: 1px solid var(--border-subtle);
}

.headshot figcaption {
  color: var(--text-muted);
  font-size: 0.85rem;
  text-align: center;
  margin-top: 0.8rem;
}

/* --------------------------------------------------------- why milk */

/* The brand story sits full width above the two statements, centred on a
   narrower measure so the lines stay readable at that width. */
.why-milk {
  max-width: 60rem;
  margin-inline: auto;
  text-align: center;
}

.why-milk .lede { margin-inline: auto; }

.why-milk__body {
  color: var(--text-secondary);
  max-width: 52rem;
  margin-inline: auto;
}

/* ------------------------------------------------- mission / brand promise */

.statement {
  display: flex;
  flex-direction: column;
  background: linear-gradient(160deg, rgba(22, 52, 87, 0.6), rgba(8, 26, 52, 0.6));
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius);
  padding: clamp(1.6rem, 3vw, 2.5rem);
  position: relative;
  overflow: hidden;
}

.statement::before {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: 2px;
  background: linear-gradient(90deg, transparent, var(--tech-blue), transparent);
  opacity: 0.55;
}

.statement h3 {
  font-size: clamp(1.1rem, 1.8vw, 1.4rem);
  line-height: 1.35;
  color: var(--milk-white);
  margin-bottom: 1.2rem;
}

.statement p {
  color: var(--text-secondary);
  font-size: 0.95rem;
}

/* `margin-top: auto` pushes the closing line to the bottom of each panel, so
   the two sit level with each other however unevenly the copy falls. */
.statement__close {
  margin-top: auto;
  margin-bottom: 0;
  padding-top: 1.2rem;
  border-top: 1px solid var(--border-subtle);
  color: var(--milk-white) !important;
  font-weight: 600;
}

/* --------------------------------------------------------- values */

.value {
  display: flex;
  gap: 1.1rem;
  align-items: flex-start;
}

.value__num {
  font-size: 0.8rem;
  font-weight: 800;
  color: var(--tech-blue);
  border: 1px solid rgba(79, 195, 240, 0.35);
  border-radius: 8px;
  width: 34px;
  height: 34px;
  display: grid;
  place-items: center;
  flex-shrink: 0;
  margin-top: 0.2rem;
}

/* These are h3 for document structure — they sit directly under an h2 and
   using h4 left a gap that screen-reader heading navigation reports as a
   missing level. They keep h4's size, which is what the design wants. */
.value h3 { font-size: 1.05rem; margin-bottom: 0.3rem; }
.value p { color: var(--text-secondary); font-size: 0.95rem; margin: 0; }

/* --------------------------------------------------------- core values */

/* Five headings across the top, one panel of copy beneath. Showing a single
   statement at a time holds the section to roughly a third of the height that
   five full paragraphs need, without cutting a word of the copy. */
.values-tabs__list {
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 0.7rem;
}

/* Five across needs about 200px a column before the longer names wrap to
   three lines and the row looks ragged. Below that, three then two. */
@media (max-width: 1040px) { .values-tabs__list { grid-template-columns: repeat(3, 1fr); } }

/* Two columns are kept all the way down rather than dropping to one: a single
   stacked column pushes the strip past the height the five open paragraphs
   would have taken, which defeats the point of collapsing them. */
@media (max-width: 640px)  { .values-tabs__list { grid-template-columns: repeat(2, 1fr); } }

.values-tab {
  font: inherit;
  color: inherit;
  cursor: pointer;
  text-align: left;
  display: flex;
  flex-direction: column;
  gap: 0.65rem;
  padding: 1rem 1.05rem 1.1rem;
  background: linear-gradient(160deg, rgba(22, 52, 87, 0.72), rgba(8, 26, 52, 0.72));
  border: 1px solid var(--border-subtle);
  border-radius: var(--radius);
  transition: transform 0.3s ease, border-color 0.3s ease, background 0.3s ease;
}

.values-tab:hover {
  transform: translateY(-3px);
  border-color: rgba(79, 195, 240, 0.4);
}

.values-tab[aria-selected="true"] {
  border-color: var(--tech-blue);
  background: linear-gradient(160deg, rgba(79, 195, 240, 0.16), rgba(8, 26, 52, 0.72));
}

.values-tab__num {
  font-size: 0.75rem;
  font-weight: 800;
  color: var(--tech-blue);
  border: 1px solid rgba(79, 195, 240, 0.35);
  border-radius: 7px;
  width: 30px;
  height: 30px;
  display: grid;
  place-items: center;
}

.values-tab[aria-selected="true"] .values-tab__num {
  background: rgba(79, 195, 240, 0.18);
  border-color: rgba(79, 195, 240, 0.6);
}

.values-tab__name {
  font-size: 1rem;
  font-weight: 700;
  line-height: 1.25;
  color: var(--milk-white);
}

/* `min-height` reserves room for the longest statement, so moving along the
   headings does not bounce the rest of the page up and down. */
.values-panel {
  margin-top: 1.6rem;
  padding-left: 1.4rem;
  border-left: 2px solid var(--tech-blue);
  min-height: 5.5rem;
}

.values-panel p {
  color: var(--text-secondary);
  font-size: 1.02rem;
  max-width: 62rem;
  margin-bottom: 0;
}

/* The heading repeats the selected tab, so it is kept for assistive tech and
   for the no-JS fallback below, where every panel is on screen at once. */
.js .values-panel h3 {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

.values-panel.is-in { animation: value-in 0.35s ease both; }

@keyframes value-in {
  from { opacity: 0; transform: translateY(6px); }
  to   { opacity: 1; transform: none; }
}

/* Without JS nothing is ever hidden, so the five panels stack under the
   headings and read as an ordinary list. */
html:not(.js) .values-panel { margin-top: 1.2rem; min-height: 0; }

/* --------------------------------------------------------- contact */

.contact-item {
  display: flex;
  gap: 0.9rem;
  align-items: flex-start;
  margin-bottom: 1.4rem;
}

.contact-item svg {
  width: 20px;
  height: 20px;
  color: var(--tech-blue);
  flex-shrink: 0;
  margin-top: 0.25rem;
}

.contact-item h3 { margin-bottom: 0.15rem; font-size: 0.95rem; }
.contact-item p { color: var(--text-secondary); margin: 0; font-size: 0.95rem; }

/* --------------------------------------------------------- CTA band */

.cta-band {
  background:
    radial-gradient(ellipse 800px 400px at 50% 120%, rgba(79, 195, 240, 0.16), transparent 65%),
    var(--navy-abyss);
  border-top: 1px solid var(--border-subtle);
  text-align: center;
}

.cta-band .btn-row { justify-content: center; }

/* --------------------------------------------------------- footer */

.footer {
  background: var(--navy-abyss);
  border-top: 1px solid var(--border-subtle);
  padding-block: 3.5rem 2rem;
  font-size: 0.92rem;
}

.footer__grid {
  display: grid;
  grid-template-columns: 1.6fr repeat(3, 1fr);
  gap: 2.5rem;
  margin-bottom: 3rem;
}

@media (max-width: 800px) {
  .footer__grid { grid-template-columns: 1fr 1fr; gap: 2rem; }
}

@media (max-width: 480px) {
  .footer__grid { grid-template-columns: 1fr; }
}

.footer h2 {
  font-size: 0.76rem;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--text-muted);
  margin-bottom: 1rem;
}

.footer ul { list-style: none; margin: 0; padding: 0; }
.footer li { margin-bottom: 0.6rem; }

.footer a { color: var(--text-secondary); text-decoration: none; }
.footer a:hover { color: var(--milk-white); }

.footer__tagline {
  color: var(--text-secondary);
  max-width: 30ch;
  margin-top: 1rem;
}

/* Company identification. Required on a UK limited company's website by the
   Companies Act 2006 s.82 and the Electronic Commerce (EC Directive)
   Regulations 2002 — registered name, number, place of registration,
   registered office, and the VAT number where registered. */
.footer__legal {
  border-top: 1px solid var(--border-subtle);
  margin-top: 2.5rem;
  padding-top: 1.5rem;
  color: var(--text-muted);
  font-size: 0.82rem;
  line-height: 1.7;
  max-width: 70ch;
}

.footer__legal p { margin: 0 0 0.4em; }

.footer__links {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.25rem;
  list-style: none;
  margin: 0;
  padding: 0;
}

.footer__links a { font-size: 0.85rem; }

.footer__bottom {
  border-top: 1px solid var(--border-subtle);
  margin-top: 1.5rem;
  padding-top: 1.5rem;
  display: flex;
  flex-wrap: wrap;
  gap: 1rem;
  justify-content: space-between;
  align-items: center;
  color: var(--text-muted);
  font-size: 0.85rem;
}

/* --------------------------------------------------------- cookie banner */

/* Anchored bottom-right: bottom-left sat directly over the hero's primary
   call to action, which is the one thing on the page it must not cover. */
.cookie {
  position: fixed;
  z-index: 150;
  right: 1rem;
  bottom: 1rem;
  left: auto;
  width: min(440px, calc(100vw - 2rem));
  background: rgba(8, 26, 52, 0.97);
  backdrop-filter: blur(16px);
  border: 1px solid var(--border-mid);
  border-radius: var(--radius);
  padding: 1.5rem;
  box-shadow: 0 24px 60px -20px rgba(0, 0, 0, 0.9);
  transform: translateY(140%);
  transition: transform 0.45s cubic-bezier(0.2, 0.7, 0.3, 1);
}

.cookie.is-visible { transform: translateY(0); }

.cookie h3 { font-size: 1.05rem; margin-bottom: 0.5rem; }

.cookie p {
  font-size: 0.9rem;
  color: var(--text-secondary);
  margin-bottom: 1.1rem;
}

.cookie__actions {
  display: flex;
  flex-wrap: wrap;
  gap: 0.6rem;
}

.cookie__actions .btn {
  padding: 0.65rem 1.15rem;
  font-size: 0.9rem;
  flex: 1;
  min-width: 120px;
}

.cookie__prefs {
  border-top: 1px solid var(--border-subtle);
  margin-top: 1.1rem;
  padding-top: 1.1rem;
  display: none;
}

.cookie__prefs.is-open { display: block; }

.cookie-option {
  display: flex;
  gap: 0.75rem;
  align-items: flex-start;
  margin-bottom: 1rem;
}

.cookie-option input { margin-top: 0.35rem; accent-color: var(--tech-blue); }

.cookie-option label { font-size: 0.88rem; font-weight: 600; }

.cookie-option p { font-size: 0.82rem; margin: 0.15rem 0 0; }

.cookie__link {
  background: none;
  border: 0;
  color: var(--tech-blue);
  cursor: pointer;
  font: inherit;
  font-size: 0.85rem;
  text-decoration: underline;
  padding: 0;
  margin-top: 0.85rem;
}

/* On a phone the floating card becomes a bottom sheet. As a card it measured
   279px on a 390px screen — a third of the viewport — floating over the hero
   with a gap of wasted space around it. None of the consent wording is cut:
   the saving comes from the padding, the type scale and losing the inset. */
@media (max-width: 640px) {
  .cookie {
    right: 0;
    bottom: 0;
    left: 0;
    width: auto;
    border-width: 1px 0 0;
    border-radius: var(--radius) var(--radius) 0 0;
    padding: 1.05rem var(--gutter);
    /* Clears the home indicator on a gesture-navigation phone. */
    padding-bottom: max(1.05rem, env(safe-area-inset-bottom));
    /* Landscape, or a long preferences panel, must never push the buttons off
       the bottom of the screen where they cannot be reached. */
    max-height: 80vh;
    overflow-y: auto;
  }

  .cookie h3 { font-size: 0.98rem; margin-bottom: 0.35rem; }

  .cookie p { font-size: 0.85rem; line-height: 1.45; margin-bottom: 0.85rem; }

  /* Two equal columns rather than `flex: 1` with a minimum width. The ICO's
     position is that refusing must be exactly as easy as accepting, and a
     flexible minimum is what lets the two buttons drift to different sizes
     once the text no longer fits. */
  .cookie__actions {
    display: grid;
    grid-template-columns: 1fr 1fr;
  }

  /* `min-height` rather than padding alone: the label is uppercase and tracked,
     so at this width it wraps to two lines on some devices and one on others,
     and padding alone would give a 37px target on the single-line case — below
     the 44px a thumb needs. */
  .cookie__actions .btn {
    min-width: 0;
    min-height: 44px;
    padding: 0.6rem 0.4rem;
    font-size: 0.76rem;
    letter-spacing: 0.06em;
  }

  .cookie__link { margin-top: 0.7rem; }
}

/* --------------------------------------------------------- page header */

.page-head {
  padding-block: calc(var(--nav-h) + 4rem) 3.5rem;
  background:
    radial-gradient(ellipse 900px 480px at 12% 0%, rgba(79, 195, 240, 0.12), transparent 60%),
    linear-gradient(180deg, var(--navy-abyss), var(--deep-navy));
  border-bottom: 1px solid var(--border-subtle);
}

/* Paused while scrolled out of sight — see the IntersectionObserver in
   site.js. Nothing is visible at this point, so pausing rather than easing
   is correct; it resumes mid-cycle when the hero comes back. */
.hero.is-offscreen *,
.hero.is-offscreen::before,
.hero.is-offscreen::after {
  animation-play-state: paused !important;
}

/* --------------------------------------------------------- contact form */

.form { display: grid; gap: 1.1rem; }

.field { display: grid; gap: 0.4rem; }

.field label {
  font-family: var(--font-ui);
  font-size: 0.8rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--text-secondary);
}

.field .req { color: var(--tech-blue); }

.field input,
.field select,
.field textarea {
  font: inherit;
  color: var(--text-primary);
  background: rgba(5, 15, 33, 0.6);
  border: 1px solid var(--border-mid);
  border-radius: 10px;
  padding: 0.8rem 0.95rem;
  width: 100%;
  transition: border-color 0.2s ease, box-shadow 0.2s ease;
}

.field textarea { min-height: 8rem; resize: vertical; }

.field input:focus,
.field select:focus,
.field textarea:focus {
  outline: none;
  border-color: var(--tech-blue);
  box-shadow: 0 0 0 3px var(--tech-blue-glow);
}

/* The browser's own invalid styling fires while the visitor is still typing.
   .is-submitted is set on first submit, so errors only appear once they have
   actually tried to send something. */
.form.is-submitted .field input:invalid,
.form.is-submitted .field textarea:invalid {
  border-color: #ff8f8f;
}

.field__error {
  color: #ffb3b3;
  font-size: 0.82rem;
  min-height: 1.2em;
}

/* Honeypot. Off-screen rather than display:none — some bots skip hidden
   fields but will happily fill one that is merely positioned away. */
.form__trap {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.form__consent {
  display: grid;
  grid-template-columns: auto 1fr;
  gap: 0.75rem;
  align-items: start;
  color: var(--text-secondary);
  font-size: 0.88rem;
}

.form__consent input { margin-top: 0.3rem; width: 1.05rem; height: 1.05rem; }

.form__status {
  border-radius: 10px;
  padding: 0.9rem 1.1rem;
  font-size: 0.92rem;
  display: none;
}

.form__status.is-ok {
  display: block;
  background: rgba(79, 195, 240, 0.12);
  border: 1px solid var(--tech-blue-dim);
  color: var(--milk-white);
}

.form__status.is-error {
  display: block;
  background: rgba(255, 143, 143, 0.1);
  border: 1px solid rgba(255, 143, 143, 0.4);
  color: #ffd7d7;
}

.form button[disabled] { opacity: 0.6; cursor: progress; }

/* --------------------------------------------------------- prose pages */

/* Long-form legal and policy copy. Deliberately narrower than the grid: the
   policy pages are read, not scanned, and 72ch is the readable limit. */
.prose { max-width: 72ch; }

.prose h2 {
  font-size: clamp(1.4rem, 2.6vw, 1.9rem);
  margin-top: 2.75rem;
}

.prose h3 {
  font-size: 1.15rem;
  margin-top: 2rem;
}

.prose p,
.prose li { color: var(--text-secondary); }

.prose ul,
.prose ol { padding-left: 1.25rem; margin-bottom: 1.4em; }

.prose li { margin-bottom: 0.5em; }

.prose table {
  width: 100%;
  border-collapse: collapse;
  margin-bottom: 1.6em;
  font-size: 0.92rem;
}

.prose th,
.prose td {
  text-align: left;
  padding: 0.7rem 0.9rem;
  border-bottom: 1px solid var(--border-subtle);
  color: var(--text-secondary);
  vertical-align: top;
}

.prose th { color: var(--text-primary); font-weight: 600; }

/* Tables of cookies and retention periods are the one thing on these pages
   that cannot always wrap, so it scrolls in its own box rather than pushing
   the page sideways. */
.prose__scroll { overflow-x: auto; }

.prose__updated {
  font-size: 0.85rem;
  color: var(--text-muted);
  border-left: 2px solid var(--tech-blue-dim);
  padding-left: 1rem;
}

/* --------------------------------------------------------- error page */

.error-page {
  min-height: 70svh;
  display: grid;
  place-items: center;
  text-align: center;
  padding-block: calc(var(--nav-h) + 4rem) 4rem;
}

.error-page__code {
  font-family: var(--font-ui);
  font-size: clamp(4rem, 14vw, 9rem);
  font-weight: 800;
  line-height: 1;
  letter-spacing: -0.03em;
  background: linear-gradient(180deg, var(--milk-pool), var(--tech-blue));
  -webkit-background-clip: text;
  background-clip: text;
  color: transparent;
  margin-bottom: 1rem;
}

/* --------------------------------------------------------- scroll reveal */

.js .on-scroll {
  opacity: 0;
  transform: translateY(26px);
  transition: opacity 0.7s ease, transform 0.7s cubic-bezier(0.2, 0.7, 0.3, 1);
}

.js .on-scroll.is-in {
  opacity: 1;
  transform: none;
}

/* --------------------------------------------------------- reduced motion */

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }

  *,
  *::before,
  *::after {
    animation-duration: 0.001ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 0.001ms !important;
  }

  .reveal { opacity: 1; transform: none; }
  .js .on-scroll { opacity: 1; transform: none; }

  /* Everything that exists only to move is removed outright, rather than left
     frozen mid-animation in whatever state it happened to be in. */
  .ring,
  .spark,
  .circuit-pulse,
  .hero__swirl { display: none; }

  .circuit-line { opacity: 0.32; }
}
