/* ==========================================================================
   Bana Systems — landing page
   ========================================================================== */

:root {
  --background: #f9fafb;
  --foreground: #14171b;
  /* Taken from the logo artwork, which is drawn in this one navy and nothing
     else. On the deep navies below it still reads, but only just, so dark
     surfaces continue to invert their actions to white. */
  --primary: #1f3a5f;
  --primary-hover: var(--navy-deep);
  /* The brand sheet's muted navy, for the descriptor that sits under the
     tagline. It is a step down from --primary rather than a second accent,
     and it is only legible on light surfaces -- see 0004 for what dark ones
     do instead. */
  --primary-muted: #243b57;
  --primary-foreground: #f9fafb;
  --muted-foreground: #676c72;

  /* Dark surfaces are the brand hue at two further depths rather than a
     neutral. The charcoal these replace, #1c1e21, was picked before the
     identity existed and was never a brand colour -- the brand line sheet
     names only the two navies -- so a page built on it read as black with
     navy accents rather than as a navy page.

     One hue, three depths: --navy-deepest for chrome that sits over
     photography and has to stay behind it, --navy-deep for whole sections,
     and --primary itself for the one band that is a statement. */
  --navy-deepest: #0c1a2b;
  /* The same value as the primary hover, named for the second job it does.
     One colour, two roles, stated rather than duplicated. */
  --navy-deep: #16293f;

  --concrete: #d6d4d0;
  --border: #d6d9dd;

  --focus: var(--primary);

  --font-body: "Manrope", -apple-system, BlinkMacSystemFont, "Segoe UI", Roboto, sans-serif;

  --wrap: 80rem;      /* 1280px */
  --gutter: 2rem;
  --section-y: 8rem;  /* 128px */
}

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: 4.5rem;
}

body {
  font-family: var(--font-body);
  line-height: 1.5;
  color: var(--foreground);
  background: var(--background);
  -webkit-font-smoothing: antialiased;
}

img, svg { display: block; max-width: 100%; }
img { height: auto; }
a { color: inherit; text-decoration: none; }
ul, ol { list-style: none; }

/* Holds the <symbol> the brand lockup is drawn from. Not display:none —
   Safari will not render a <use> that points into a hidden subtree. */
.sprite { position: absolute; width: 0; height: 0; overflow: hidden; }

.skip-link {
  position: absolute;
  left: -9999px;
  z-index: 100;
  padding: 0.75rem 1.25rem;
  background: var(--primary);
  color: #fff;
  font-size: 0.8125rem;
  font-weight: 700;
  /* It lands over the dark header, where a navy ring on a navy chip is no
     ring at all. */
  --focus: #fff;
}
.skip-link:focus { left: 1rem; top: 1rem; }

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

/* The focus ring inherits its colour from the surface it lands on: navy on
   the light sections, white wherever the background is dark. */
.site-header,
.hero,
.section-dark,
.section-horizon,
.final-cta,
.site-footer { --focus: #fff; }

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

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

.section { padding-block: var(--section-y); }
.section-light { background: var(--background); }
.section-dark { background: var(--navy-deep); }

/* The lightest of the three navies, and the only section that gets it. With
   the dark surfaces now navy too, this band is no longer the only navy on the
   page -- it is the top of the scale, which is what still separates a
   statement about direction from the sections either side of it. */
.section-horizon { background: var(--primary); }
.section-concrete {
  background: rgba(214, 212, 208, 0.6);
  --muted-foreground: #5c6167;
}

/* Typography -------------------------------------------------------------- */

.eyebrow {
  font-size: 0.6875rem;
  font-weight: 800;
  letter-spacing: 0.2em;
  text-transform: uppercase;
}
.eyebrow-primary { color: var(--primary); }
.eyebrow-light { color: rgba(255, 255, 255, 0.75); }
.eyebrow-muted-light { color: rgba(255, 255, 255, 0.5); }

.section-title {
  margin-top: 1rem;
  max-width: 44rem;
  font-size: clamp(1.9rem, 3.6vw, 2.9rem);
  font-weight: 800;
  line-height: 1.1;
  letter-spacing: -0.022em;
  color: var(--primary);
}
.section-title-light { color: #fff; }
.section-title-narrow { max-width: 38rem; }

/* Buttons ----------------------------------------------------------------- */

.cta-primary,
.cta-ghost,
.cta-invert {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  padding: 0.9rem 1.6rem;
  border-radius: 4px;
  font-size: 0.9rem;
  font-weight: 700;
  letter-spacing: 0.005em;
  white-space: nowrap;
  cursor: pointer;
  transition: transform 0.22s cubic-bezier(0.2, 0.8, 0.2, 1),
              box-shadow 0.22s, background-color 0.22s, color 0.22s;
}

/* The action on a light surface. Every call to action on this page happens to
   sit on a dark one, where .cta-invert is the readable form — but this is the
   brand button the other marketing surfaces cut from this file inherit. */
.cta-primary { background: var(--primary); color: var(--primary-foreground); }
.cta-primary:hover {
  background: var(--primary-hover);
  transform: translateY(-2px);
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.28);
}

.cta-ghost { color: #fff; box-shadow: inset 0 0 0 1px rgba(255, 255, 255, 0.45); }
.cta-ghost:hover {
  background: rgba(255, 255, 255, 0.1);
  transform: translateY(-2px);
  box-shadow: inset 0 0 0 1px #fff;
}

/* The action on a dark surface: navy on deep navy is barely a shape, so the
   button inverts and the brand colour moves to the label. */
.cta-invert { background: #fff; color: var(--primary); }
.cta-invert:hover {
  background: var(--background);
  transform: translateY(-2px);
  box-shadow: 0 10px 24px rgba(0, 0, 0, 0.3);
}

/* Header ------------------------------------------------------------------ */

.site-header {
  position: fixed;
  inset-inline: 0;
  top: 0;
  z-index: 50;
  background: rgba(12, 26, 43, 0.72);
  border-bottom: 1px solid rgba(255, 255, 255, 0.1);
  -webkit-backdrop-filter: blur(12px);
  backdrop-filter: blur(12px);
  transition: background-color 0.25s;
}
.site-header.is-scrolled { background: rgba(12, 26, 43, 0.95); }

.nav {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 1rem;
  max-width: var(--wrap);
  margin-inline: auto;
  padding: 0.75rem var(--gutter);
}

.brand { display: inline-flex; align-items: center; color: #fff; }
.brand-lockup {
  height: 2.25rem;
  width: auto;
  /* Declared as well as implied by the viewBox so the header cannot reflow
     between parse and paint. */
  aspect-ratio: 349.14 / 72.62;
  flex-shrink: 0;
}

.nav-right { display: flex; align-items: center; gap: 1.5rem; flex-shrink: 0; }
.nav-links { display: flex; align-items: center; gap: 1.6rem; }
.nav-links a {
  font-size: 0.9rem;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.78);
  transition: color 0.2s;
}
.nav-links a:hover { color: #fff; }

.nav-cta { padding: 0.55rem 1.1rem; font-size: 0.8125rem; }
/* The application's door, beside the enquiry button: quieter than Contact,
   because a visitor who already has an account is not the page's audience. */
.nav-login { margin-right: -0.5rem; white-space: nowrap; }

.nav-toggle {
  display: none;
  flex-direction: column;
  gap: 5px;
  padding: 0.5rem;
  background: none;
  border: 0;
  cursor: pointer;
}
.nav-toggle span {
  display: block;
  width: 22px;
  height: 2px;
  background: #fff;
  transition: transform 0.25s, opacity 0.25s;
}
.nav-toggle[aria-expanded="true"] span:nth-child(1) { transform: translateY(7px) rotate(45deg); }
.nav-toggle[aria-expanded="true"] span:nth-child(2) { opacity: 0; }
.nav-toggle[aria-expanded="true"] span:nth-child(3) { transform: translateY(-7px) rotate(-45deg); }

.mobile-menu {
  display: flex;
  flex-direction: column;
  border-top: 1px solid rgba(255, 255, 255, 0.1);
  background: rgba(12, 26, 43, 0.97);
}
.mobile-menu[hidden] { display: none; }
.mobile-menu a {
  padding: 0.9rem var(--gutter);
  font-size: 0.9rem;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.85);
  border-bottom: 1px solid rgba(255, 255, 255, 0.08);
}
.mobile-menu a:hover { color: #fff; background: rgba(255, 255, 255, 0.06); }

/* Hero -------------------------------------------------------------------- */

.hero {
  position: relative;
  display: flex;
  flex-direction: column;
  justify-content: flex-end;
  min-height: 100vh;
  min-height: 100svh;
  overflow: hidden;
  isolation: isolate;
}

.hero-media { position: absolute; inset: 0; z-index: -1; }

/* The photograph is a dark roof shot in shadow, and the scrim over it is what
   makes the hero text legible. Together they took the roof to a flat mass: the
   spread between its lightest and darkest fifth was 2.0:1, so the facets, hips
   and ridges the page is selling did not read at all.

   Lifting it here rather than in the JPEG keeps the original photograph
   untouched, so a future crop or regrade starts from the source, and leaves the
   amount arguable in a diff instead of buried in a binary. Measured against the
   painted composite: the roof's internal range goes to 3.3:1, 0.4% of pixels
   clip, and the least-contrasty text on the hero stays at 5.7:1 — above the
   4.5:1 floor for body text with the title, which is large, far above its own.

   The filter is static, so it is applied to the composited layer once; the Ken
   Burns transform animates on top of it without re-running it. */
.hero-media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  filter: brightness(1.5) contrast(1.15);
}

.hero-scrim {
  position: absolute;
  inset: 0;
  /* Four stops rather than three, so the scrim can do two jobs that pull in
     opposite directions. It stays heavy through the band the headline and lede
     occupy — 41% to 78% up — and releases above them, where nothing is written
     and the roof is all there is to see. A single mid stop had to compromise
     between the two and did neither well. */
  background: linear-gradient(
    to top,
    var(--navy-deepest) 0%,
    rgba(12, 26, 43, 0.74) 42%,
    rgba(12, 26, 43, 0.44) 80%,
    rgba(12, 26, 43, 0.20) 100%
  );
}

.ken-burns {
  animation: ken-burns 26s ease-in-out infinite alternate;
  transform-origin: 60% 45%;
}
@keyframes ken-burns {
  from { transform: scale(1); }
  to   { transform: scale(1.09); }
}

.hero-inner {
  position: relative;
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding: 8rem var(--gutter) 4rem;
}

.hero-title {
  margin-top: 1.25rem;
  max-width: 20ch;
  font-size: clamp(2.5rem, 6vw, 4.5rem);
  font-weight: 800;
  line-height: 1.02;
  letter-spacing: -0.03em;
  color: #fff;
}

.hero-lede {
  margin-top: 1.75rem;
  max-width: 40rem;
  font-size: 1.0625rem;
  line-height: 1.6;
  color: rgba(255, 255, 255, 0.82);
}

.hero-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem;
  margin-top: 2.25rem;
}

.hero-strip {
  position: relative;
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 2rem;
  width: 100%;
  padding-block: 1.5rem;
  padding-inline: max(var(--gutter), (100% - var(--wrap)) / 2 + var(--gutter));
  background: rgba(12, 26, 43, 0.82);
  border-top: 1px solid rgba(255, 255, 255, 0.14);
  -webkit-backdrop-filter: blur(8px);
  backdrop-filter: blur(8px);
}
.strip-title {
  display: block;
  font-size: 0.9rem;
  font-weight: 700;
  color: #fff;
}
.strip-body {
  display: block;
  margin-top: 0.3rem;
  font-size: 0.8125rem;
  line-height: 1.55;
  color: rgba(255, 255, 255, 0.65);
}

/* How it works ------------------------------------------------------------ */

.steps {
  display: grid;
  grid-template-columns: repeat(4, minmax(0, 1fr));
  gap: 3rem 2.5rem;
  margin-top: 4rem;
  padding-top: 2.5rem;
  border-top: 1px solid var(--border);
}
.step-num {
  display: block;
  font-size: 1.5rem;
  font-weight: 800;
  letter-spacing: -0.01em;
  color: var(--primary);
}
.steps h3 {
  margin-top: 0.875rem;
  font-size: 1.125rem;
  font-weight: 800;
  letter-spacing: -0.015em;
  color: var(--primary);
}
.steps p {
  margin-top: 0.625rem;
  font-size: 0.9rem;
  line-height: 1.65;
  color: var(--muted-foreground);
}

/* Split sections (Platform, White-label) ---------------------------------- */

.split {
  display: grid;
  grid-template-columns: minmax(0, 0.85fr) minmax(0, 1.15fr);
  gap: 5rem;
  align-items: start;
}
.split-even { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 5rem; }

.split-media {
  margin-top: 2.5rem;
  width: 100%;
  height: 18rem;
  object-fit: cover;
  border-radius: 4px;
}

.service-list li {
  display: grid;
  grid-template-columns: 1.5rem minmax(0, 1fr);
  gap: 0 1rem;
  padding-block: 1.5rem;
  border-top: 1px solid rgba(255, 255, 255, 0.12);
}
.service-list li:last-child { border-bottom: 1px solid rgba(255, 255, 255, 0.12); }
.service-num {
  font-size: 0.8125rem;
  font-weight: 700;
  line-height: 1.85;
  color: rgba(255, 255, 255, 0.5);
}
.service-list h3 {
  font-size: 1.125rem;
  font-weight: 800;
  letter-spacing: -0.015em;
  color: #fff;
}
.service-list p {
  margin-top: 0.4rem;
  font-size: 0.9rem;
  line-height: 1.65;
  color: rgba(255, 255, 255, 0.62);
}

.why-list { margin-top: 2.5rem; }
.why-list > div { padding-block: 1.5rem; border-top: 1px solid var(--border); }
.why-list > div:first-child { border-top: 0; padding-top: 0; }
.why-list dt {
  font-size: 1.125rem;
  font-weight: 800;
  letter-spacing: -0.015em;
  color: var(--primary);
}
.why-list dd {
  margin-top: 0.4rem;
  max-width: 30rem;
  font-size: 0.9rem;
  line-height: 1.65;
  color: var(--muted-foreground);
}

/* White-label illustration ------------------------------------------------ */

.wl-figure { align-self: center; }
.wl-grid { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.25rem; }

.wl-card {
  display: flex;
  flex-direction: column;
  padding: 1.25rem;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: 6px;
  box-shadow: 0 12px 30px rgba(12, 26, 43, 0.10);
}
.wl-card-a { --wl-accent: #8a010d; }
.wl-card-b { --wl-accent: #1f7a5f; }

.wl-head { display: flex; align-items: center; gap: 0.625rem; }
.wl-mark {
  display: grid;
  place-items: center;
  width: 2rem;
  height: 2rem;
  border-radius: 50%;
  background: var(--wl-accent);
  color: #fff;
  font-size: 0.8125rem;
  font-weight: 800;
}
/* Deliberately NOT --primary. These name two stand-in contractor brands in
   the illustration, and navy is ours -- 0001 moved card B off steel for
   exactly this reason. */
.wl-name { font-size: 0.8125rem; font-weight: 700; color: var(--foreground); }

.wl-body { display: grid; gap: 0.5rem; margin-block: 1.25rem; }
.wl-line { display: block; width: 80%; height: 0.5rem; border-radius: 2px; background: var(--concrete); }
.wl-line-wide { width: 100%; }
.wl-line-short { width: 55%; }

.wl-button {
  align-self: flex-start;
  padding: 0.4rem 0.9rem;
  border-radius: 3px;
  background: var(--wl-accent);
  color: #fff;
  font-size: 0.75rem;
  font-weight: 700;
}

.wl-figure figcaption {
  margin-top: 1.25rem;
  font-size: 0.8125rem;
  line-height: 1.6;
  color: var(--muted-foreground);
}

/* Product showcase -------------------------------------------------------- */

.platform-grid {
  display: grid;
  grid-template-columns: 1.45fr 1fr;
  gap: 1.5rem;
  margin-top: 3rem;
  align-items: start;
}

.map-frame {
  position: relative;
  aspect-ratio: 1400 / 1000;
  overflow: hidden;
  border-radius: 4px;
  background: var(--navy-deepest);
}
.map-frame > img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
}
.map-overlay { position: absolute; inset: 0; width: 100%; height: 100%; }

/* Overlay blues, local to this illustration: they read as measurement on
   satellite imagery and are not part of the page palette. */
.facet-fill polygon { fill: rgba(104, 142, 167, 0.28); }
.facet-edge polygon { fill: none; stroke: #c3d4e0; stroke-width: 3; }
.facet-edge line { stroke: #c3d4e0; stroke-width: 3; }
.facet-ridge { stroke: #fff; stroke-width: 5; }
.facet-vertex circle { fill: #fff; stroke: var(--navy-deepest); stroke-width: 3; }

.map-frame figcaption {
  position: absolute;
  bottom: 0;
  left: 0;
  display: flex;
  flex-wrap: wrap;
  gap: 1.5rem;
  padding: 0.75rem 1.25rem;
  background: rgba(12, 26, 43, 0.86);
  -webkit-backdrop-filter: blur(4px);
  backdrop-filter: blur(4px);
}
.stat-label {
  display: block;
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.55);
}
.stat-value {
  display: block;
  margin-top: 0.15rem;
  font-size: 1.0625rem;
  font-weight: 800;
  letter-spacing: -0.01em;
  color: #fff;
}

.report-card {
  display: flex;
  flex-direction: column;
  padding: 1.75rem;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: 4px;
}
.report-head {
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding-bottom: 1.25rem;
  border-bottom: 1px solid var(--border);
}
.report-title { font-size: 1.0625rem; font-weight: 800; letter-spacing: -0.015em; color: var(--primary); }
.report-tag {
  padding: 0.2rem 0.5rem;
  border-radius: 3px;
  background: rgba(31, 58, 95, 0.09);
  font-size: 0.65rem;
  font-weight: 800;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--primary);
}

.report-rows { margin-top: 1rem; }
.report-rows > div {
  display: flex;
  align-items: baseline;
  justify-content: space-between;
  gap: 1rem;
  padding-block: 0.625rem;
  border-bottom: 1px solid var(--border);
  font-size: 0.9rem;
}
.report-rows dt { color: var(--muted-foreground); }
.report-rows dd { font-weight: 700; color: var(--foreground); }

.estimate-box {
  margin-top: 1.75rem;
  padding: 1.25rem;
  border-radius: 4px;
  background: var(--navy-deep);
}
.estimate-label {
  display: block;
  font-size: 0.6rem;
  font-weight: 700;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.55);
}
.estimate-value {
  margin-top: 0.4rem;
  font-size: 2rem;
  font-weight: 800;
  letter-spacing: -0.025em;
  color: #fff;
}
.estimate-value span {
  font-size: 1.125rem;
  font-weight: 600;
  color: rgba(255, 255, 255, 0.6);
}

.showcase-note {
  margin-top: 1.5rem;
  font-size: 0.8125rem;
  color: var(--muted-foreground);
}

/* Plans ------------------------------------------------------------------- */

.plan-grid {
  display: grid;
  grid-template-columns: repeat(5, minmax(0, 1fr));
  gap: 1.25rem;
  margin-top: 3rem;
}
.plan {
  display: flex;
  flex-direction: column;
  padding: 1.5rem;
  background: #fff;
  border: 1px solid var(--border);
  border-radius: 6px;
}
.plan h3 {
  font-size: 1.0625rem;
  font-weight: 800;
  letter-spacing: -0.015em;
  color: var(--primary);
}
.plan-model {
  margin-top: 0.3rem;
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: var(--primary);
}
.plan-body {
  margin-top: 0.875rem;
  font-size: 0.9rem;
  line-height: 1.6;
  color: var(--muted-foreground);
}

.plan-note {
  margin-top: 1.75rem;
  font-size: 0.9rem;
  color: var(--muted-foreground);
}
.plan-note a { font-weight: 700; color: var(--primary); text-decoration: underline; text-underline-offset: 3px; }

/* Where we're headed ------------------------------------------------------ */

.horizon-arc {
  display: grid;
  grid-template-columns: repeat(3, minmax(0, 1fr));
  gap: 2.5rem;
  margin-top: 4rem;
  list-style: none;
}

.horizon-step { position: relative; padding-top: 2.5rem; }

/* The rail is drawn per step rather than as one line behind all three, so it
   stops at the last node instead of running off the edge, and so the stacked
   layout can turn it vertical without a second element to reposition. */
.horizon-step::before {
  content: "";
  position: absolute;
  top: 0.5rem;
  left: 0;
  right: -2.5rem;
  height: 1px;
  background: rgba(255, 255, 255, 0.25);
}
.horizon-step:last-child::before { right: auto; width: 0.75rem; }

/* Hollow by default; only the stage that exists is filled. That contrast is
   the graphic's whole argument. */
.horizon-step::after {
  content: "";
  position: absolute;
  top: 0;
  left: 0;
  width: 0.75rem;
  height: 0.75rem;
  background: var(--primary);
  border: 2px solid rgba(255, 255, 255, 0.55);
  border-radius: 50%;
}
.horizon-step.is-shipped::after {
  background: #fff;
  border-color: #fff;
  box-shadow: 0 0 0 4px rgba(255, 255, 255, 0.18);
}

.horizon-stage {
  display: block;
  font-size: 0.65rem;
  font-weight: 800;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.55);
}
.horizon-step h3 {
  margin-top: 0.6rem;
  font-size: 1.125rem;
  font-weight: 700;
  letter-spacing: -0.015em;
  color: #fff;
}
.horizon-step p {
  margin-top: 0.5rem;
  font-size: 0.9rem;
  line-height: 1.65;
  color: rgba(255, 255, 255, 0.72);
}

/* Says which of the three is real. Without it the arc reads as three things we
   have, which is the one claim this section must not make. */
.horizon-state {
  display: inline-block;
  margin-top: 1rem;
  padding: 0.2rem 0.55rem;
  font-size: 0.6875rem;
  font-weight: 700;
  letter-spacing: 0.04em;
  color: rgba(255, 255, 255, 0.7);
  border: 1px solid rgba(255, 255, 255, 0.28);
  border-radius: 3px;
}
.horizon-step.is-shipped .horizon-state {
  color: var(--primary);
  background: #fff;
  border-color: #fff;
}

/* Final CTA --------------------------------------------------------------- */

.final-cta {
  position: relative;
  overflow: hidden;
  isolation: isolate;
  text-align: center;
}
.final-media { position: absolute; inset: 0; z-index: -1; }
.final-media img { width: 100%; height: 100%; object-fit: cover; }
.final-scrim {
  position: absolute;
  inset: 0;
  background: linear-gradient(to right, rgba(31, 58, 95, 0.94), rgba(12, 26, 43, 0.93));
}

.final-title {
  font-size: clamp(2rem, 4.5vw, 3.25rem);
  font-weight: 800;
  line-height: 1.08;
  letter-spacing: -0.028em;
  color: #fff;
}
.final-lede {
  max-width: 40rem;
  margin: 1.25rem auto 0;
  font-size: 1.0625rem;
  line-height: 1.65;
  color: rgba(255, 255, 255, 0.82);
}
.final-actions {
  display: flex;
  flex-wrap: wrap;
  justify-content: center;
  gap: 0.75rem;
  margin-top: 2.25rem;
}

/* Enquiry form ------------------------------------------------------------
   The input primitive DESIGN.md specifies: 4px corner, 1px hairline border,
   white ground, Body-role text, and the system focus ring coloured by surface.

   This sits on the dark photographic surface, so --focus inverts to white the
   same way the footer's does. Without that the navy ring would be invisible
   against the scrim, and every field would appear to lose focus entirely. */

.final-cta { --focus: #fff; }

/* Says which fields are required before someone finds out by being bounced to
   one. The system marks the optional field rather than the required ones, which
   is only obvious once you have read the whole form — and a visitor in a hurry
   has not. */
.enquiry-required-note {
  max-width: 40rem;
  margin: 2.25rem auto 0;
  font-size: 0.8125rem;
  line-height: 1.55;
  color: rgba(255, 255, 255, 0.82);
  text-align: left;
}
.enquiry-required-note + .enquiry-form { margin-top: 0.625rem; }

/* The specific reason the endpoint gave, above the general advice. */
.enquiry-note-reason { font-weight: 700; color: var(--foreground); }
.enquiry-note-reason + .enquiry-note-body { margin-top: 0.4rem; }

.enquiry-form {
  display: grid;
  grid-template-columns: 1fr 1fr;
  gap: 1rem;
  max-width: 40rem;
  margin: 2.25rem auto 0;
  text-align: left;
}

.field { display: flex; flex-direction: column; gap: 0.4rem; }
.field-wide { grid-column: 1 / -1; }

.enquiry-form label {
  font-size: 0.75rem;
  font-weight: 700;
  letter-spacing: 0.06em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.82);
}
.field-optional {
  font-weight: 400;
  letter-spacing: normal;
  text-transform: none;
  color: rgba(255, 255, 255, 0.82);
}

.enquiry-form input,
.enquiry-form textarea {
  width: 100%;
  padding: 0.7rem 0.85rem;
  border: 1px solid var(--border);
  border-radius: 4px;
  background: #fff;
  color: var(--foreground);
  font-family: inherit;
  /* 16px, not the 0.9rem Body role, and the reason is mobile Safari: iOS
     auto-zooms the whole page when a focused input renders below 16px. On a
     phone that means every field tap jerks the layout and the visitor has to
     pinch back out to see the rest of the form. Most of this page's traffic is
     on a phone, so the Input role exists at 1rem precisely to sit on the right
     side of that threshold. Do not "tidy" it back to Body. */
  font-size: 1rem;
  line-height: 1.65;
}
.enquiry-form textarea { resize: vertical; min-height: 6.5rem; }

/* The placeholder is prompt copy, not content. Muted Ink keeps it from reading
   as a filled field, which is the usual failure of placeholder-as-label. */
.enquiry-form ::placeholder { color: var(--muted-foreground); opacity: 1; }

.enquiry-actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem 1rem;
  margin-top: 0.5rem;
}
.enquiry-form button { flex: 0 0 auto; }
.enquiry-privacy {
  font-size: 0.8125rem;
  line-height: 1.55;
  color: rgba(255, 255, 255, 0.82);
}

/* Off-screen rather than display:none. A bot reading the DOM sees an ordinary
   text input; hiding it with display:none is the one pattern naive scrapers
   are most likely to skip. */
.enquiry-hp {
  position: absolute;
  width: 1px;
  height: 1px;
  overflow: hidden;
  clip-path: inset(50%);
  white-space: nowrap;
}

/* Confirmation and error.
   :target covers the no-JavaScript path, where the handler answers a plain form
   POST with a 303 to /#enquiry-sent. .is-shown covers the fetch path, where
   nothing navigates. */
.enquiry-note {
  display: none;
  max-width: 40rem;
  margin: 2.25rem auto 0;
  padding: 1.5rem;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: #fff;
  text-align: left;
}
.enquiry-note:target,
.enquiry-note.is-shown { display: block; }

/* When the send succeeds there is nothing left to fill in, so the form is
   replaced by the confirmation rather than left sitting beneath it.
   Both paths have to do this, and the sibling selector is the one that matters:
   without it, a visitor with JavaScript off lands back on the page, reads the
   confirmation, and finds an empty form under it inviting them to send again.
   The error panel deliberately does NOT hide the form — there the whole point
   is to correct a field and retry. */
.enquiry-form.is-done,
#enquiry-sent:target ~ .enquiry-form { display: none; }

/* The note describes the form's required fields, so it goes with the form.
   Left behind it reads as an instruction about a form that is no longer
   there.

   The note sits BEFORE the form in the DOM, so the fetch path needs `:has()`
   to look forward at a later sibling — `~` only reaches the other way. The
   redirect path does not, because `#enquiry-sent` genuinely precedes it. A
   browser without `:has()` keeps the note, which is the harmless failure. */
.enquiry-required-note:has(~ .enquiry-form.is-done),
#enquiry-sent:target ~ .enquiry-required-note { display: none; }

.enquiry-note-title {
  font-size: 1.0625rem;
  font-weight: 800;
  line-height: 1.5;
  letter-spacing: -0.015em;
  color: var(--foreground);
}
.enquiry-note-body {
  margin-top: 0.4rem;
  font-size: 0.9rem;
  line-height: 1.65;
  color: var(--muted-foreground);
}
/* Text, not a mailto link. This is the fallback when the endpoint is
   unreachable, not a second conversion path competing with the form. */
.enquiry-fallback { font-weight: 700; color: var(--foreground); }

/* Brand line -------------------------------------------------------------- */

/* Tagline and descriptor as the brand sheet sets them: all-caps tagline
   subordinate to the wordmark, descriptor smaller and lighter below a thin
   rule. Written surface-agnostically so the same component can sit under any
   lockup the marketing template grows -- the two colour custom properties are
   what a surface overrides. */
.brand-tagline {
  margin-top: 1rem;
  font-size: 0.75rem;
  font-weight: 600;
  line-height: 1.5;
  letter-spacing: 0.16em;
  text-transform: uppercase;
  color: var(--brand-tagline-color, var(--primary));
}
/* The mark rides at cap height rather than sitting on the baseline, and keeps
   the tagline's tracking from opening a gap in front of it. */
.brand-tm {
  margin-left: 0.15em;
  font-size: 0.62em;
  letter-spacing: 0;
  vertical-align: 0.55em;
}

.brand-descriptor {
  margin-top: 0.55rem;
  padding-top: 0.55rem;
  max-width: 20rem;
  font-size: 0.75rem;
  font-weight: 500;
  line-height: 1.55;
  color: var(--brand-descriptor-color, var(--primary-muted));
  border-top: 1px solid var(--brand-rule-color, rgba(31, 58, 95, 0.22));
}

/* Footer ------------------------------------------------------------------ */

.site-footer {
  padding-block: 4rem 2.5rem;
  background: var(--navy-deep);
  color: #fff;
  /* Navy on deep navy is barely a shape. Dark surfaces invert, exactly as 0001
     settled for the accent colour and the buttons. */
  --brand-tagline-color: rgba(255, 255, 255, 0.92);
  --brand-descriptor-color: rgba(255, 255, 255, 0.55);
  --brand-rule-color: rgba(255, 255, 255, 0.16);
}
.footer-grid {
  display: grid;
  grid-template-columns: 1.5fr 1fr 1fr;
  gap: 2.5rem;
}
.footer-lockup { height: 2.5rem; }
.footer-blurb {
  margin-top: 1rem;
  max-width: 22rem;
  font-size: 0.9rem;
  line-height: 1.65;
  color: rgba(255, 255, 255, 0.55);
}
.footer-heading {
  font-size: 0.65rem;
  font-weight: 800;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, 0.6);
}
.footer-links { margin-top: 1rem; display: grid; gap: 0.5rem; }
.footer-links a {
  font-size: 0.9rem;
  color: rgba(255, 255, 255, 0.8);
  transition: color 0.2s;
}
.footer-links a:hover { color: #fff; }

.footer-rule {
  margin-top: 3rem;
  border: 0;
  border-top: 1px solid rgba(255, 255, 255, 0.12);
}
.footer-legal {
  margin-top: 1.5rem;
  font-size: 0.8125rem;
  color: rgba(255, 255, 255, 0.55);
}

/* Legal pages --------------------------------------------------------------
   Privacy policy and terms. One narrow column of running text on the light
   surface, under the same fixed header as the home page, which is why the
   header is given its scrolled state from the start: there is no hero for it
   to sit over. */
.legal-page .site-header { position: sticky; }
.legal {
  padding-block: 4rem 6rem;
  background: var(--background);
  color: var(--foreground);
}
.legal .section-title { margin-top: 0.75rem; }
.legal-updated {
  margin-top: 1rem;
  font-size: 0.8125rem;
  color: var(--muted-foreground);
}
/* Numeral and Title SM from the ramp, doing duty as running heads. */
.legal h2 {
  margin-top: 3rem;
  font-size: 1.5rem;
  font-weight: 800;
  letter-spacing: -0.01em;
  color: var(--primary);
}
.legal h3 {
  margin-top: 1.75rem;
  font-size: 1.0625rem;
  font-weight: 800;
  letter-spacing: -0.015em;
  color: var(--primary);
}
.legal p,
.legal li {
  margin-top: 1rem;
  max-width: 44rem;
  font-size: 1rem;
  line-height: 1.7;
}
.legal ul { padding-left: 1.25rem; list-style: disc; }
.legal li { margin-top: 0.5rem; }
.legal a { color: var(--primary); text-decoration: underline; text-underline-offset: 0.15em; }
.legal-note {
  margin-top: 3rem;
  padding-top: 1.5rem;
  border-top: 1px solid rgba(20, 23, 27, 0.12);
  font-size: 0.8125rem;
  color: var(--muted-foreground);
}
.enquiry-privacy a { color: inherit; text-decoration: underline; text-underline-offset: 0.15em; }

/* Text message opt-in page ------------------------------------------------
   A still of the estimate form's contact step, drawn rather than captured so
   it stays exact when the form's styling moves. Not a working form. */
.optin-mock {
  margin-top: 1.5rem;
  max-width: 32rem;
  padding: 1.5rem;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: #fff;
}
.legal .optin-mock p { margin-top: 0; font-size: 0.8125rem; line-height: 1.55; }
.legal .optin-mock .optin-mock-label { font-weight: 600; }
.legal .optin-mock .optin-mock-input {
  margin-top: 0.5rem;
  padding: 0.625rem 0.875rem;
  border: 1px solid var(--border);
  border-radius: 4px;
}
.legal .optin-mock .optin-mock-hint { margin-top: 0.25rem; font-size: 0.75rem; color: var(--muted-foreground); }
.legal .optin-mock .optin-mock-check { display: flex; gap: 0.75rem; margin-top: 1rem; }
.optin-mock-box {
  flex: none;
  width: 1rem;
  height: 1rem;
  margin-top: 0.15rem;
  border: 1px solid var(--muted-foreground);
  border-radius: 3px;
}
.optin-mock figcaption { margin-top: 1rem; font-size: 0.75rem; color: var(--muted-foreground); }
/* Set apart by a whole hairline border, not a thickened edge: the Uniform
   Border Rule in DESIGN.md. */
.optin-quote {
  margin-top: 1rem;
  max-width: 44rem;
  padding: 1rem 1.25rem;
  border: 1px solid var(--border);
  border-radius: 6px;
  background: #fff;
  line-height: 1.7;
}

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

@media (max-width: 1180px) {
  .plan-grid { grid-template-columns: repeat(3, minmax(0, 1fr)); }
}

@media (max-width: 1024px) {
  :root { --section-y: 6rem; }
  .platform-grid { grid-template-columns: minmax(0, 1fr); }
  .split,
  .split-even { grid-template-columns: minmax(0, 1fr); gap: 3rem; }
  .steps { grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 2.5rem 2rem; }
  .hero-strip { gap: 1.25rem; }
}

@media (max-width: 768px) {
  :root { --gutter: 1.25rem; --section-y: 4.5rem; }
  .nav-links,
  .nav-cta { display: none; }
  .nav-toggle { display: flex; }
  .hero { min-height: 92vh; }
  .hero-inner { padding-top: 7rem; padding-bottom: 3rem; }
  .hero-strip { grid-template-columns: minmax(0, 1fr); gap: 1rem; }
  .plan-grid { grid-template-columns: repeat(2, minmax(0, 1fr)); }
  .footer-grid { grid-template-columns: minmax(0, 1fr); gap: 2rem; }
  .steps { margin-top: 2.5rem; }
}

@media (max-width: 560px) {
  .steps { grid-template-columns: minmax(0, 1fr); }
  /* Two columns is wrong for three ordered stages: it orphans the third and
     breaks the arc's reading order. Straight to one column, rail turned
     vertical so the sequence still reads as a sequence. */
  .horizon-arc { grid-template-columns: minmax(0, 1fr); gap: 0; margin-top: 3rem; }
  .horizon-step { padding: 0 0 2.25rem 1.75rem; }
  .horizon-step:last-child { padding-bottom: 0; }
  .horizon-step::before {
    top: 0.5rem;
    bottom: -0.5rem;
    left: 0.34rem;
    right: auto;
    width: 1px;
    height: auto;
  }
  .horizon-step:last-child::before { display: none; }
  .horizon-step::after { top: 0.25rem; }
  .plan-grid,
  .wl-grid { grid-template-columns: minmax(0, 1fr); }
  .hero-actions .cta-invert,
  .hero-actions .cta-ghost,
  .final-actions .cta-invert { flex: 1 1 100%; }
  /* One column below the breakpoint: two fields side by side leaves each too
     narrow to type a company name into on a phone, which is where most of this
     page's traffic is. */
  .enquiry-form { grid-template-columns: 1fr; }
  .enquiry-form button { flex: 1 1 100%; }
  .map-frame figcaption { gap: 1rem; padding: 0.6rem 1rem; }
  .report-card { padding: 1.25rem; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  .ken-burns { animation: none; }
  * { transition-duration: 0.01ms !important; }
}
