@charset "UTF-8";
/* ==========================================================================
   DAS Defence - site.css
   A licensed exporter's document, not a hardware brand's showreel.
   Order: tokens -> fonts -> reset -> type -> layout -> components -> motion.
   ========================================================================== */

/* --------------------------------------------------------------------------
   1. Tokens
   -------------------------------------------------------------------------- */
:root {
  /* Palette - the five from the brief, plus two derived inks.
     --khaki and --signal fail AA as text on --gypsum (2.14:1 and 2.83:1),
     so on light they are decoration/fill only. The -ink variants carry the
     same intent at 5.52:1 and 5.11:1, both AA for normal text. */
  --gypsum:      #EDEAE3;   /* page surface, dominant                       */
  --gypsum-sunk: #E4E0D6;   /* inset surfaces, form fields                  */
  --surface:     #FAF9F6;   /* raised cards on the gypsum page               */
  --basalt:      #10120F;   /* media bands, footer, primary type            */
  --gunmetal:    #2A2F2B;   /* secondary type                               */
  --khaki:       #A8A18F;   /* RULES + decoration on light; text on dark ok */
  --khaki-ink:   #625C4B;   /* muted/caption TEXT on light  - 5.52:1        */
  --signal:      #C8791A;   /* accent fill; text on dark ok - 5.54:1        */
  --signal-ink:  #8F5309;   /* accent TEXT on light         - 5.11:1        */
  --alert-ink:   #A32912;   /* validation errors                            */

  /* Navy, keyed to the logo. Used for the hero and solution plates so the
     mark has a home. Contrast on --navy-900: gypsum 14.5:1, khaki 6.8:1,
     signal 5.1:1 — all AA. */
  --navy-900:    #0B1A33;
  --navy-800:    #12294D;
  --navy-700:    #1E3A6E;
  --navy-ice:    #C8D3E6;   /* light type on navy — 11.9:1                   */

  --hairline: rgba(168, 161, 143, 0.6);

  /* Type */
  --f-display: "Archivo Expanded", "Archivo", "Helvetica Neue", Arial, system-ui, sans-serif;
  --f-body:    "IBM Plex Sans", system-ui, -apple-system, "Segoe UI", Roboto, Arial, sans-serif;
  --f-mono:    "IBM Plex Mono", ui-monospace, "Cascadia Mono", Consolas, "Courier New", monospace;

  --t-xs:   0.75rem;
  --t-sm:   0.8125rem;
  --t-base: 1rem;
  --t-md:   clamp(1.0625rem, 0.4vw + 0.98rem, 1.1875rem);
  --t-lg:   clamp(1.25rem,  0.8vw + 1.1rem,  1.5rem);
  --t-xl:   clamp(1.5rem,   1.6vw + 1.2rem,  2rem);
  --t-2xl:  clamp(1.9rem,   2.6vw + 1.3rem,  2.9rem);
  --t-3xl:  clamp(2.3rem,   4.4vw + 1.2rem,  4rem);

  /* Space */
  --s-1: 0.25rem;  --s-2: 0.5rem;   --s-3: 0.75rem;  --s-4: 1rem;
  --s-5: 1.5rem;   --s-6: 2rem;     --s-7: 3rem;     --s-8: 4rem;
  --s-9: 6rem;     --s-10: 8rem;

  --wrap: 1280px;
  --radius: 2px;
  --ease: cubic-bezier(0.2, 0, 0.15, 1);
}

/* --------------------------------------------------------------------------
   2. Fonts - self-hosted. Drop the four .woff2 into /assets/fonts/ and these
   activate. Until then the fallback stacks above carry the design.
   See assets/fonts/README.md.
   -------------------------------------------------------------------------- */
@font-face {
  font-family: "Archivo Expanded"; font-style: normal; font-weight: 700;
  font-display: swap; src: url("../fonts/archivo-expanded-700.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Sans"; font-style: normal; font-weight: 400;
  font-display: swap; src: url("../fonts/ibm-plex-sans-400.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Sans"; font-style: normal; font-weight: 600;
  font-display: swap; src: url("../fonts/ibm-plex-sans-600.woff2") format("woff2");
}
@font-face {
  font-family: "IBM Plex Mono"; font-style: normal; font-weight: 400;
  font-display: swap; src: url("../fonts/ibm-plex-mono-400.woff2") format("woff2");
}

/* --------------------------------------------------------------------------
   3. Reset
   -------------------------------------------------------------------------- */
*, *::before, *::after { box-sizing: border-box; }
* { margin: 0; }

html { -webkit-text-size-adjust: 100%; scroll-behavior: smooth; }

body {
  background: var(--gypsum);
  color: var(--basalt);
  font-family: var(--f-body);
  font-size: var(--t-base);
  line-height: 1.6;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

img, svg, video { display: block; max-width: 100%; }
img, video { height: auto; }
button, input, select, textarea { font: inherit; color: inherit; }
ul, ol { list-style: none; padding: 0; }

a { color: var(--signal-ink); text-decoration-thickness: 1px; text-underline-offset: 3px; }
a:hover { color: var(--basalt); }

:focus-visible {
  outline: 2px solid var(--signal-ink);
  outline-offset: 3px;
  border-radius: 1px;
}
.band :focus-visible, .foot :focus-visible, .rail :focus-visible, .hero :focus-visible {
  outline-color: var(--signal);
}

.skip {
  position: absolute; left: var(--s-4); top: -4rem; z-index: 100;
  background: var(--basalt); color: var(--gypsum);
  padding: var(--s-3) var(--s-5); text-decoration: none;
  font-family: var(--f-mono); font-size: var(--t-sm);
  transition: top 0.15s var(--ease);
}
.skip:focus { top: var(--s-4); color: var(--gypsum); }

/* --------------------------------------------------------------------------
   4. Type
   -------------------------------------------------------------------------- */
h1, h2, h3, h4 {
  font-family: var(--f-display);
  font-weight: 700;
  line-height: 1.08;
  letter-spacing: -0.015em;
  text-wrap: balance;
}
h1 { font-size: var(--t-3xl); }
h2 { font-size: var(--t-2xl); }
h3 { font-size: var(--t-lg);  letter-spacing: -0.005em; }
h4 { font-size: var(--t-md);  letter-spacing: 0; }
h1, h2 { font-stretch: expanded; }

p { text-wrap: pretty; }

.mono {
  font-family: var(--f-mono);
  font-size: var(--t-sm);
  letter-spacing: 0.01em;
}

.label {
  font-family: var(--f-mono);
  font-size: var(--t-xs);
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--khaki-ink);
}
.band .label, .foot .label, .hero .label { color: var(--khaki); }

.lede { font-size: var(--t-md); color: var(--gunmetal); max-width: 62ch; }
.band .lede { color: var(--khaki); }

.prose > * + * { margin-top: var(--s-4); }
.prose p, .prose li { max-width: 68ch; color: var(--gunmetal); }
.prose h2 { margin-top: var(--s-7); }
.prose h3 { margin-top: var(--s-6); }
.prose ul { list-style: none; }
.prose ul li { position: relative; padding-left: var(--s-5); }
.prose ul li + li { margin-top: var(--s-2); }
.prose ul li::before {
  content: ""; position: absolute; left: 0; top: 0.66em;
  width: 10px; height: 1px; background: var(--khaki-ink);
}
.band .prose p, .band .prose li { color: var(--khaki); }
.band .prose ul li::before { background: var(--khaki); }

/* --------------------------------------------------------------------------
   5. Layout
   -------------------------------------------------------------------------- */
.wrap {
  width: 100%;
  max-width: var(--wrap);
  margin-inline: auto;
  padding-inline: var(--s-5);
}
@media (min-width: 48em) { .wrap { padding-inline: var(--s-7); } }

.section { padding-block: var(--s-8); }
@media (min-width: 48em) { .section { padding-block: var(--s-9); } }
.section + .section { border-top: 1px solid var(--hairline); }

.band {
  background: var(--basalt);
  color: var(--gypsum);
}
.band a { color: var(--signal); }
.band a:hover { color: var(--gypsum); }
.band h2, .band h3 { color: var(--gypsum); }
.section + .band, .band + .section { border-top: 0; }

/* 12-column grid, collapsing to one on small screens. */
.grid { display: grid; gap: var(--s-6); grid-template-columns: repeat(12, 1fr); }
/* The span lives in grid-column-END, not the shorthand: a .start-N class sets
   grid-column-start, and the shorthand would put its span there and be
   overwritten, leaving the item one track wide. */
.col-4  { grid-column-end: span 4; }
.col-5  { grid-column-end: span 5; }
.col-6  { grid-column-end: span 6; }
.col-7  { grid-column-end: span 7; }
.col-8  { grid-column-end: span 8; }
.col-12 { grid-column-end: span 12; }
@media (max-width: 47.99em) {
  .grid { grid-template-columns: 1fr; gap: var(--s-5); }
  .col-4, .col-5, .col-6, .col-7, .col-8 { grid-column: 1 / -1; }
}

/* Explicit start lines, so a right-hand column can sit off the gutter.
   Scoped to the wide breakpoint only: applied unconditionally they would
   push content into implicit columns once the grid collapses to one, which
   overflows the viewport on a phone. */
@media (min-width: 48em) {
  .start-8 { grid-column-start: 8; }
  .start-9 { grid-column-start: 9; }
}

.sechead { margin-bottom: var(--s-6); }
.sechead .label { display: block; margin-bottom: var(--s-3); }

/* --------------------------------------------------------------------------
   6. Credential rail - the signature element
   -------------------------------------------------------------------------- */
.rail {
  background: var(--basalt);
  color: var(--khaki);          /* 7.33:1 on basalt */
  font-family: var(--f-mono);
  font-size: var(--t-xs);
  letter-spacing: 0.06em;
  text-transform: uppercase;
}
.rail__inner {
  display: flex; flex-wrap: wrap; gap: var(--s-2);
  align-items: center;
  padding-block: var(--s-2);
  min-height: 2.25rem;
}
.rail__entity { color: var(--gypsum); }
.rail__sep { color: var(--gunmetal); }
@media (max-width: 30em) {
  .rail { letter-spacing: 0.03em; }
  .rail__inner { font-size: 0.6875rem; }
}

/* --------------------------------------------------------------------------
   7. Header + navigation
   -------------------------------------------------------------------------- */
.head {
  position: sticky; top: 0; z-index: 50;
  background: rgba(237, 234, 227, 0.93);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--hairline);
}
.head__inner {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-5); min-height: 4.25rem;
}

/* Wordmark. The mark is the supplied artwork as a transparent PNG, so it is
   not tinted by currentColor. See includes/logo.php. */
.logo {
  display: inline-flex; align-items: center; gap: var(--s-3);
  text-decoration: none; flex-shrink: 0; color: var(--basalt);
}
.logo__name {
  font-family: var(--f-display); font-weight: 700;
  font-size: 0.875rem; letter-spacing: 0.02em; text-transform: uppercase;
  white-space: nowrap; padding-left: var(--s-3);
  border-left: 1px solid var(--hairline);
}
/* "Defence Advance System" is twice the width of the old wordmark, so it is
   dropped earlier: below this the mark and the Menu button take the bar. */
@media (max-width: 40em) { .logo__name { display: none; } }

.logo__mark { width: auto; height: 34px; display: block; }
@media (max-width: 30em) { .logo__mark { height: 28px; } }
.foot__brand .logo__mark { height: 40px; }
/* The navy reads about 1.6:1 on basalt, so dark surfaces get a mono
   knockout; 0.92 lands it near gypsum rather than a pure white that would
   out-glare the type around it. */
.logo__mark--light { filter: brightness(0) invert(1); opacity: 0.92; }

.navtoggle {
  display: inline-flex; align-items: center; gap: var(--s-2);
  background: none; border: 1px solid var(--khaki);
  padding: var(--s-2) var(--s-3); border-radius: var(--radius);
  cursor: pointer; color: var(--basalt);
  font-family: var(--f-mono); font-size: var(--t-xs);
  letter-spacing: 0.1em; text-transform: uppercase;
}
.navtoggle__bars { display: grid; gap: 3px; width: 14px; }
.navtoggle__bars i { display: block; height: 1.5px; background: currentColor; }
@media (min-width: 60em) { .navtoggle { display: none; } }

.nav__list {
  display: flex; align-items: center; gap: var(--s-5);
  font-size: var(--t-sm); letter-spacing: 0.02em;
}
.nav__list a {
  color: var(--gunmetal); text-decoration: none;
  padding-block: var(--s-2); display: inline-block;
  border-bottom: 1px solid transparent;
}
.nav__list a:hover { color: var(--basalt); border-bottom-color: var(--khaki); }
.nav__list a[aria-current="page"] {
  color: var(--basalt); border-bottom-color: var(--signal);
}
.nav__cta a { border-bottom: 0; }

@media (max-width: 59.99em) {
  .nav {
    position: absolute; inset: 100% 0 auto 0;
    background: var(--gypsum);
    border-bottom: 1px solid var(--hairline);
    display: none;
  }
  .nav[data-open="true"] { display: block; }
  .nav__list {
    flex-direction: column; align-items: stretch;
    gap: 0; padding: var(--s-3) var(--s-5) var(--s-5);
  }
  .nav__list li + li { border-top: 1px solid var(--hairline); }
  .nav__list a { padding-block: var(--s-4); font-size: var(--t-md); }
  .nav__cta { padding-top: var(--s-4); border-top: 0 !important; }
  .nav__cta a { display: inline-block; }
}

/* --------------------------------------------------------------------------
   8. Buttons
   -------------------------------------------------------------------------- */
.btn {
  display: inline-flex; align-items: center; gap: var(--s-3);
  background: var(--signal); color: var(--basalt);   /* 5.54:1 */
  font-family: var(--f-mono); font-size: var(--t-sm);
  letter-spacing: 0.06em; text-transform: uppercase;
  padding: var(--s-4) var(--s-5);
  border: 1px solid var(--signal); border-radius: var(--radius);
  text-decoration: none; cursor: pointer;
  transition: background 0.15s var(--ease), color 0.15s var(--ease),
              border-color 0.15s var(--ease);
}
.btn:hover { background: var(--basalt); border-color: var(--basalt); color: var(--gypsum); }
.btn--sm { padding: var(--s-2) var(--s-4); font-size: var(--t-xs); }
.btn--ghost { background: none; color: var(--basalt); border-color: var(--khaki); }
.btn--ghost:hover { background: var(--basalt); border-color: var(--basalt); color: var(--gypsum); }
/* .band a would otherwise repaint the label signal-on-signal. */
.band .btn { color: var(--basalt); }
.band .btn--ghost, .hero .btn--ghost { color: var(--gypsum); border-color: var(--gunmetal); }
.band .btn--ghost:hover, .hero .btn--ghost:hover {
  background: var(--gypsum); color: var(--basalt); border-color: var(--gypsum);
}
.btn[disabled] { opacity: 0.55; cursor: not-allowed; }

.arrowlink {
  display: inline-flex; align-items: center; gap: var(--s-2);
  font-family: var(--f-mono); font-size: var(--t-sm);
  letter-spacing: 0.04em; text-decoration: none;
  border-bottom: 1px solid var(--hairline); padding-bottom: 2px;
}
.arrowlink::after { content: "\2192"; transition: transform 0.15s var(--ease); }
.arrowlink:hover::after { transform: translateX(3px); }

/* --------------------------------------------------------------------------
   9. Hero
   -------------------------------------------------------------------------- */
.hero { position: relative; background: var(--basalt); color: var(--gypsum); overflow: hidden; }
.hero__plate { position: absolute; inset: 0; }
.hero__plate picture { display: block; width: 100%; height: 100%; }
.hero__plate svg, .hero__plate img, .hero__plate video {
  width: 100%; height: 100%; object-fit: cover;
}
.hero__plate::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(105deg,
    rgba(16,18,15,0.95) 0%, rgba(16,18,15,0.84) 46%, rgba(16,18,15,0.45) 100%);
}
.hero__inner {
  position: relative;
  padding-block: var(--s-9);
  max-width: 46rem;
}
@media (min-width: 48em) { .hero__inner { padding-block: var(--s-10); } }
.hero h1 { color: var(--gypsum); margin-bottom: var(--s-5); }
.hero .label { display: block; margin-bottom: var(--s-5); }
.hero .lede { color: var(--khaki); margin-bottom: var(--s-6); font-size: var(--t-lg); }
.hero__actions { display: flex; flex-wrap: wrap; gap: var(--s-4); align-items: center; }

/* Page header for interior pages */
.pagehead { background: var(--basalt); color: var(--gypsum); }
.pagehead__inner { padding-block: var(--s-8); max-width: 52rem; }
.pagehead h1 { color: var(--gypsum); margin-bottom: var(--s-4); }
.pagehead .label { display: block; margin-bottom: var(--s-4); }
.pagehead .lede { color: var(--khaki); }

/* ---- Photographic ground, capability pages only --------------------------
   The line's own card photograph sunk behind the band. Everything here is
   gated behind --photo, which only capability pages carry: /contact/, /privacy/
   and 404 share .pagehead and must keep the flat basalt band.
   -------------------------------------------------------------------------- */
.pagehead--photo { position: relative; overflow: clip; }
.pagehead--photo .pagehead__inner { padding-block: var(--s-9); }

.pagehead__ground { position: absolute; inset: 0; pointer-events: none; }
.pagehead__ground picture { display: block; height: 100%; }
.pagehead__ground img {
  width: 100%; height: 100%; object-fit: cover; object-position: center;
  filter: grayscale(1) brightness(0.5) contrast(0.95);
}
/* Scrim. Directional, as on .caphero: the type side stays near-solid basalt
   while the far side keeps enough photograph to read as imagery rather than
   as texture. It hangs off the ground rather than off .pagehead so that a
   band with no photograph paints no gradient. */
.pagehead__ground::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(104deg,
    rgba(16,18,15,0.96) 0%, rgba(16,18,15,0.90) 42%,
    rgba(16,18,15,0.70) 74%, rgba(16,18,15,0.52) 100%);
}
.pagehead--photo > .wrap { position: relative; z-index: 1; }

@media (max-width: 59.99em) {
  /* Stacked, the h1 runs the full width and would cross the open end of the
     gradient, so the ground sits back and the scrim closes up. */
  .pagehead--photo .pagehead__inner { padding-block: var(--s-8); }
  .pagehead__ground::after {
    background: linear-gradient(168deg,
      rgba(16,18,15,0.96) 0%, rgba(16,18,15,0.92) 46%,
      rgba(16,18,15,0.82) 78%, rgba(16,18,15,0.72) 100%);
  }
}

/* --------------------------------------------------------------------------
   10. Capability sub-navigation
   -------------------------------------------------------------------------- */
/* Sibling rail on capability child pages */
.subnav { border-bottom: 1px solid var(--hairline); background: var(--gypsum); }
.subnav ul { display: flex; flex-wrap: wrap; gap: var(--s-5); padding-block: var(--s-4); }
.subnav a {
  font-family: var(--f-mono); font-size: var(--t-xs);
  letter-spacing: 0.1em; text-transform: uppercase;
  text-decoration: none; color: var(--khaki-ink);
  padding-bottom: 3px; border-bottom: 1px solid transparent;
}
.subnav a:hover { color: var(--basalt); }
.subnav a[aria-current="page"] { color: var(--basalt); border-bottom-color: var(--signal); }

/* --------------------------------------------------------------------------
   11. Fact lists, proof band, callouts
   -------------------------------------------------------------------------- */
.facts { border-top: 1px solid var(--hairline); }
.facts > div {
  display: grid; gap: var(--s-1);
  padding-block: var(--s-4);
  border-bottom: 1px solid var(--hairline);
}
@media (min-width: 40em) {
  .facts > div { grid-template-columns: 14rem 1fr; gap: var(--s-5); align-items: baseline; }
}
.facts dt {
  font-family: var(--f-mono); font-size: var(--t-xs);
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--khaki-ink);
}
.facts dd { color: var(--gunmetal); }
.band .facts, .band .facts > div { border-color: var(--gunmetal); }
.band .facts dt { color: var(--khaki); }
.band .facts dd { color: var(--gypsum); }

/* Compliance callout */
.note {
  border-left: 2px solid var(--signal);
  padding: var(--s-4) 0 var(--s-4) var(--s-5);
}
.note p { color: var(--gunmetal); font-size: var(--t-sm); max-width: 66ch; }
.note .label { display: block; margin-bottom: var(--s-2); }
.band .note p { color: var(--khaki); }

/* Numbered process steps */
.steps { counter-reset: step; display: grid; gap: var(--s-5); }
@media (min-width: 48em) { .steps { grid-template-columns: repeat(2, 1fr); gap: var(--s-6); } }
.steps > li {
  counter-increment: step;
  border-top: 1px solid var(--hairline);
  padding-top: var(--s-4);
}
.band .steps > li { border-color: var(--gunmetal); }
.steps > li::before {
  content: counter(step, decimal-leading-zero);
  display: block; margin-bottom: var(--s-2);
  font-family: var(--f-mono); font-size: var(--t-xs);
  letter-spacing: 0.1em; color: var(--signal-ink);
}
.band .steps > li::before { color: var(--signal); }
.steps h3 { font-size: var(--t-md); margin-bottom: var(--s-2); }
.steps p { color: var(--gunmetal); font-size: var(--t-sm); max-width: 46ch; }
.band .steps p { color: var(--khaki); }

/* --------------------------------------------------------------------------
   12. Forms
   -------------------------------------------------------------------------- */
.form { display: grid; gap: var(--s-5); }
@media (min-width: 40em) { .form { grid-template-columns: 1fr 1fr; } }
.field { display: grid; gap: var(--s-2); align-content: start; }
.field--full { grid-column: 1 / -1; }

.field > label {
  font-family: var(--f-mono); font-size: var(--t-xs);
  letter-spacing: 0.1em; text-transform: uppercase; color: var(--khaki-ink);
}
.field .req { color: var(--signal-ink); }

.field input, .field select, .field textarea {
  background: var(--gypsum-sunk);
  border: 1px solid var(--khaki);
  border-radius: var(--radius);
  padding: var(--s-3) var(--s-4);
  width: 100%;
  color: var(--basalt);
  font-size: var(--t-base);
}
.field textarea { min-height: 9rem; resize: vertical; }
.field input:hover, .field select:hover, .field textarea:hover { border-color: var(--gunmetal); }
.field .hint { font-family: var(--f-mono); font-size: var(--t-xs); color: var(--khaki-ink); }
.field .err { font-family: var(--f-mono); font-size: var(--t-xs); color: var(--alert-ink); }
.field .err:empty { display: none; }
.field[data-invalid="true"] input,
.field[data-invalid="true"] select,
.field[data-invalid="true"] textarea { border-color: var(--alert-ink); }

.consent { display: flex; gap: var(--s-3); align-items: flex-start; }
.consent input {
  width: 1.1rem; height: 1.1rem; margin-top: 0.3rem;
  flex-shrink: 0; accent-color: var(--signal-ink);
}
.consent label {
  text-transform: none; letter-spacing: 0; font-family: var(--f-body);
  font-size: var(--t-sm); color: var(--gunmetal);
}

/* Honeypot - moved off-screen rather than display:none, which bots skip. */
.hp { position: absolute; left: -9999px; width: 1px; height: 1px; overflow: hidden; }

.formstatus { grid-column: 1 / -1; }
.formstatus:empty { display: none; }
.formstatus p { font-size: var(--t-sm); }
.formstatus[data-state="ok"]  { border-left: 2px solid var(--signal); padding-left: var(--s-4); }
.formstatus[data-state="bad"] { border-left: 2px solid var(--alert-ink); padding-left: var(--s-4); }

/* --------------------------------------------------------------------------
   13. Footer
   -------------------------------------------------------------------------- */
.foot { background: var(--basalt); color: var(--khaki); padding-block: var(--s-8) var(--s-6); }
.foot__grid { display: grid; gap: var(--s-6); }
@media (min-width: 40em) { .foot__grid { grid-template-columns: repeat(2, 1fr); } }
@media (min-width: 64em) { .foot__grid { grid-template-columns: 1.6fr 1.2fr 1fr 1fr; gap: var(--s-7); } }
.foot__brand { margin-bottom: var(--s-4); }
.foot__legal { color: var(--khaki); line-height: 1.7; }
.foot__h {
  font-family: var(--f-mono); font-size: var(--t-xs); font-weight: 400;
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--gypsum); margin-bottom: var(--s-4);
}
.foot__list li + li { margin-top: var(--s-2); }
.foot__list a, .foot__addr a { color: var(--khaki); text-decoration: none; font-size: var(--t-sm); }
.foot__list a:hover, .foot__addr a:hover { color: var(--signal); text-decoration: underline; }
.foot__addr { font-style: normal; font-size: var(--t-sm); line-height: 1.8; }
.foot__base {
  margin-top: var(--s-7); padding-top: var(--s-5);
  border-top: 1px solid var(--gunmetal);
  display: grid; gap: var(--s-3);
}
@media (min-width: 64em) { .foot__base { grid-template-columns: 1fr auto; align-items: baseline; } }
.foot__base p { color: var(--khaki); font-size: var(--t-xs); }
.foot__note { max-width: 60ch; }
.foot__note a { color: var(--signal); }

/* --------------------------------------------------------------------------
   14. Motion - four uses only, all opt-out
   -------------------------------------------------------------------------- */
.js .reveal { opacity: 0; transform: translateY(14px); }
.reveal.is-in {
  opacity: 1; transform: none;
  transition: opacity 0.55s var(--ease), transform 0.55s var(--ease);
}
.hero__inner { animation: fadeup 0.7s var(--ease) both; }
@keyframes fadeup { from { opacity: 0; transform: translateY(18px); } }

@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; }
  .hero__inner { animation: none; }
  /* scroll-behavior does not inherit, so the html rule above misses the
     slider's own scroll container. site.js also passes behavior:'auto'. */
  .solslider__viewport { scroll-behavior: auto; }
}

/* --------------------------------------------------------------------------
   15. Utilities
   -------------------------------------------------------------------------- */
.vh {
  position: absolute; width: 1px; height: 1px; padding: 0;
  margin: -1px; overflow: hidden; clip: rect(0 0 0 0); white-space: nowrap; border: 0;
}
/* Spacing utilities, so page markup carries no inline styles. */
.mt-4 { margin-top: var(--s-4); }
.mt-5 { margin-top: var(--s-5); }
.mt-6 { margin-top: var(--s-6); }
.mt-7 { margin-top: var(--s-7); }
.stack-4 > * + * { margin-top: var(--s-4); }
.stack-5 > * + * { margin-top: var(--s-5); }
.stack-6 > * + * { margin-top: var(--s-6); }
.rule { border: 0; border-top: 1px solid var(--hairline); margin-block: var(--s-6); }
.band .rule { border-color: var(--gunmetal); }
.center-narrow { max-width: 46rem; }
.todo { font-style: normal; }

/* --------------------------------------------------------------------------
   17. Hero — defence-tech treatment
   -------------------------------------------------------------------------- */
.hero--tech { background: var(--navy-900); }
.hero--tech .hero__plate::after {
  background: linear-gradient(100deg,
    rgba(11,26,51,0.97) 0%, rgba(11,26,51,0.90) 38%,
    rgba(11,26,51,0.55) 68%, rgba(11,26,51,0.30) 100%);
}
.hero--tech .hero__inner { max-width: 44rem; padding-block: var(--s-9); }
@media (min-width: 60em) { .hero--tech .hero__inner { padding-block: var(--s-10); } }

.hero--tech h1 {
  font-size: clamp(2.4rem, 5.2vw + 1rem, 4.4rem);
  line-height: 1.02; letter-spacing: -0.02em;
  text-transform: uppercase; margin-bottom: var(--s-5);
}
.hero__accent { color: var(--signal); display: block; }

.hero--tech .lede {
  color: var(--navy-ice); font-size: var(--t-md);
  max-width: 44ch; margin-bottom: var(--s-7);
}

/* --------------------------------------------------------------------------
   18. Solution cards — scroll-snap slider
   --------------------------------------------------------------------------
   The track scrolls natively; site.js only adds the arrows and dots. See
   includes/solslider.php. */
.solslider__viewport {
  overflow-x: auto; overflow-y: hidden;
  scroll-snap-type: x mandatory;
  /* The scrollbar would sit under the cards and fight the controls row. */
  scrollbar-width: none; -ms-overflow-style: none;
  /* Room for the focus ring a card draws on itself, which a scroll container
     would otherwise clip. */
  padding: 3px; margin: -3px;
}
.solslider__viewport::-webkit-scrollbar { display: none; }

/* Stretch keeps the equal card heights the grid gave for free, which is what
   makes .solcard__lede's margin-top:auto land every lede on one baseline. */
.solutions { display: flex; align-items: stretch; gap: var(--s-5); }

.solslider__controls {
  display: flex; align-items: center; justify-content: space-between;
  gap: var(--s-5);
  margin-top: var(--s-5); padding-top: var(--s-5);
  border-top: 1px solid var(--hairline);
}
.solslider__arrows { display: flex; gap: var(--s-3); }
.solslider__arrow { padding: var(--s-3); line-height: 0; }

/* Bars rather than circles, echoing .solcard__rule. */
.solslider__dots { display: flex; gap: var(--s-2); }
.solslider__dot {
  background: none; border: 0; cursor: pointer;
  width: 28px; padding: var(--s-3) 0; line-height: 0;
}
.solslider__dot::before {
  content: ""; display: block; height: 3px;
  background: var(--khaki);
  transition: background 0.2s var(--ease);
}
.solslider__dot:hover::before { background: var(--signal-ink); }
.solslider__dot[aria-current="true"]::before { background: var(--signal); }

.solcard {
  display: flex; flex-direction: column;
  flex: 0 0 82%;            /* a sliver of the next card signals the scroll */
  scroll-snap-align: start;
  background: var(--surface);
  border: 1px solid transparent;
  padding: var(--s-6) var(--s-5) var(--s-5);
  text-decoration: none; color: inherit;
  border-radius: var(--radius);
  transition: border-color 0.2s var(--ease), background 0.2s var(--ease);
}
/* Same breakpoints as the grid this replaced: 1-up, 2-up, 3-up. */
@media (min-width: 34em) { .solcard { flex-basis: calc((100% - var(--s-5)) / 2); } }
@media (min-width: 60em) { .solcard { flex-basis: calc((100% - 2 * var(--s-5)) / 3); } }
.solcard:hover { border-color: var(--khaki); background: #FFFDFA; color: inherit; }

.solcard__title {
  font-family: var(--f-display); font-weight: 700;
  font-size: var(--t-sm); letter-spacing: 0.09em;
  text-transform: uppercase; color: var(--basalt);
  line-height: 1.35;
}
.solcard__rule {
  display: block; width: 30px; height: 4px;
  background: var(--signal); margin-top: var(--s-3);
}

.solcard__art {
  display: flex; align-items: center; justify-content: center;
  padding-block: var(--s-6);
  margin-top: var(--s-4);
}
.solcard__art .wf { width: 100%; height: auto; max-width: 17rem; overflow: visible; }

/* Photo variant: the image becomes a banded plate rather than line art. */
.solcard--photo .solcard__art {
  padding-block: 0; margin-top: var(--s-5);
  border-radius: var(--radius); overflow: hidden;
}
.solcard--photo .solcard__art picture { display: block; width: 100%; }
.solcard--photo .solcard__art img {
  width: 100%; height: 11rem; object-fit: cover; display: block;
  filter: saturate(0.85);
  transition: transform 0.35s var(--ease), filter 0.35s var(--ease);
}
.solcard--photo:hover .solcard__art img { transform: scale(1.03); filter: saturate(1); }
.solcard--photo .solcard__lede { margin-top: var(--s-5); }
.solcard .wf [stroke] { transition: opacity 0.25s var(--ease); }
.solcard:hover .wf { opacity: 1; }

.solcard__lede {
  margin-top: auto;
  font-size: var(--t-sm); color: var(--khaki-ink); max-width: 34ch;
}
.solcard:focus-visible { outline-color: var(--signal-ink); }

/* --------------------------------------------------------------------------
   19. Licensed activities
   -------------------------------------------------------------------------- */
.activities { border-top: 1px solid var(--hairline); }
.activities__row {
  display: flex; align-items: baseline; justify-content: space-between;
  gap: var(--s-5); padding-block: var(--s-4);
  border-bottom: 1px solid var(--hairline);
}
.activities__name { color: var(--basalt); font-weight: 500; }
.activities__state {
  flex-shrink: 0;
  font-family: var(--f-mono); font-size: var(--t-xs);
  letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--signal-ink);
}
.activities__state::before {
  content: ""; display: inline-block;
  width: 6px; height: 6px; margin-right: var(--s-2);
  background: var(--signal); border-radius: 50%;
  vertical-align: 0.1em;
}
.band .activities, .band .activities__row { border-color: var(--gunmetal); }
.band .activities__name { color: var(--gypsum); }
.band .activities__state { color: var(--signal); }

@media (min-width: 48em) { .start-6 { grid-column-start: 6; } }

/* --------------------------------------------------------------------------
   20. Capabilities overview - /capabilities/ only

   The one page on the site built to be scrolled rather than read in place: a
   mosaic hero, a sticky index, and nine alternating panels that flip surface
   and direction as they go past.

   Two rules govern the motion here.

   First, everything scroll-scrubbed lives in the @supports block at the end.
   Firefox has no animation-timeline yet and the page must not depend on it -
   without it the panels still reveal, they simply do not drift.

   Second, nothing uses overflow: hidden. A hidden overflow makes the element a
   scroll container, which would capture the view() timelines of everything
   inside it. overflow: clip crops identically without doing that.
   -------------------------------------------------------------------------- */

/* Shared word-stagger index. Longest heading across the site is nine words. */
.capword { display: inline-block; }
.capword:nth-child(1) { --i: 1; }
.capword:nth-child(2) { --i: 2; }
.capword:nth-child(3) { --i: 3; }
.capword:nth-child(4) { --i: 4; }
.capword:nth-child(5) { --i: 5; }
.capword:nth-child(6) { --i: 6; }
.capword:nth-child(7) { --i: 7; }
.capword:nth-child(8) { --i: 8; }
.capword:nth-child(9) { --i: 9; }
.capword:nth-child(10) { --i: 10; }
.capword:nth-child(11) { --i: 11; }
.capword:nth-child(12) { --i: 12; }

/* ---- Hero ---------------------------------------------------------------- */
.caphero {
  position: relative;
  background: var(--navy-900);
  overflow: clip;
}

/* Nine photographs, one per line, on a 3x3 with a hairline gutter. */
.caphero__mosaic {
  position: absolute; inset: 0;
  display: grid;
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: repeat(3, 1fr);
  gap: 1px;
  background: rgba(200, 211, 230, 0.12);
}
.caphero__tile {
  overflow: clip;
  background: var(--navy-800);
  animation: capTile 1.2s var(--ease) both;
  animation-delay: calc(var(--i, 1) * 70ms);
}
.caphero__tile:nth-child(1) { --i: 1; }
.caphero__tile:nth-child(2) { --i: 2; }
.caphero__tile:nth-child(3) { --i: 3; }
.caphero__tile:nth-child(4) { --i: 4; }
.caphero__tile:nth-child(5) { --i: 5; }
.caphero__tile:nth-child(6) { --i: 6; }
.caphero__tile:nth-child(7) { --i: 7; }
.caphero__tile:nth-child(8) { --i: 8; }
.caphero__tile:nth-child(9) { --i: 9; }
.caphero__tile picture { display: block; height: 100%; }
.caphero__tile img {
  width: 100%; height: 100%; object-fit: cover;
  filter: grayscale(1) brightness(0.5) contrast(0.95);
}
@keyframes capTile {
  from { opacity: 0; transform: scale(1.12); }
  to   { opacity: 1; transform: scale(1); }
}

/* Scrim. Directional, so the type side stays near-solid while the far side
   keeps enough of the photography to read as imagery rather than texture. */
.caphero::after {
  content: ""; position: absolute; inset: 0;
  background: linear-gradient(104deg,
    rgba(11,26,51,0.96) 0%, rgba(11,26,51,0.90) 42%,
    rgba(11,26,51,0.70) 74%, rgba(11,26,51,0.52) 100%);
}
.caphero > .wrap, .caphero__statsbar { position: relative; z-index: 1; }

.caphero__inner { padding-block: var(--s-9); max-width: 62rem; }
@media (min-width: 60em) { .caphero__inner { padding-block: var(--s-10) var(--s-9); } }

.caphero__eyebrow {
  display: block; margin-bottom: var(--s-5); color: var(--khaki);
  animation: capUp 0.8s var(--ease) 0.15s both;
}
.caphero__h {
  color: var(--gypsum);
  font-size: clamp(2.2rem, 4.4vw + 1rem, 4.15rem);
  line-height: 1.03; letter-spacing: -0.02em;
  text-transform: uppercase;
  margin-bottom: var(--s-5);
}
.caphero__h .capword {
  animation: capUp 0.9s var(--ease) both;
  animation-delay: calc(0.28s + var(--i, 1) * 70ms);
}
.caphero__lede {
  color: var(--navy-ice); font-size: var(--t-lg); max-width: 46rem;
  animation: capUp 0.9s var(--ease) 0.78s both;
}
@keyframes capUp { from { opacity: 0; transform: translateY(0.5em); } }

.caphero__cue {
  display: inline-flex; align-items: center; gap: var(--s-4);
  margin-top: var(--s-7);
  font-family: var(--f-mono); font-size: var(--t-xs);
  letter-spacing: 0.14em; text-transform: uppercase;
  color: var(--khaki); text-decoration: none;
  animation: capUp 0.9s var(--ease) 0.95s both;
}
.caphero__cue:hover { color: var(--gypsum); }
.caphero__cueline {
  width: 64px; height: 1px; background: rgba(200,211,230,0.3);
  position: relative; overflow: clip;
}
.caphero__cueline::after {
  content: ""; position: absolute; inset: 0;
  background: var(--signal); transform-origin: left;
  animation: capCue 2.6s var(--ease) infinite;
}
@keyframes capCue {
  0%   { transform: scaleX(0); opacity: 1; }
  55%  { transform: scaleX(1); opacity: 1; }
  100% { transform: scaleX(1); opacity: 0; }
}

/* Instrument strip welded to the bottom edge of the hero. */
.caphero__statsbar {
  border-top: 1px solid rgba(200,211,230,0.18);
  background: rgba(6,15,29,0.55);
  backdrop-filter: blur(6px);
}
.caphero__stats { display: grid; }
/* Column flow rather than a fixed count: the bar carries two stats on /about/
   and three on /capabilities/. A hardcoded repeat(3) leaves a dead third of
   the bar on whichever page has fewer, and silently wraps one that has more. */
@media (min-width: 48em) {
  .caphero__stats { grid-auto-flow: column; grid-auto-columns: 1fr; }
}
.caphero__stats > div {
  display: flex; align-items: baseline; gap: var(--s-4);
  padding-block: var(--s-5);
}
@media (max-width: 47.99em) {
  .caphero__stats > div { padding-block: var(--s-4); }
  .caphero__stats > div + div { border-top: 1px solid rgba(200,211,230,0.14); }
}
@media (min-width: 48em) {
  .caphero__stats > div + div {
    border-left: 1px solid rgba(200,211,230,0.14);
    padding-left: var(--s-5);
  }
}
.caphero__stats dt {
  font-family: var(--f-display); font-weight: 700;
  font-size: var(--t-xl); line-height: 1; color: var(--signal);
  flex-shrink: 0;
}
.caphero__stats dd {
  font-family: var(--f-mono); font-size: var(--t-xs);
  letter-spacing: 0.1em; text-transform: uppercase;
  color: var(--khaki); line-height: 1.5;
}

/* ---- Sticky index -------------------------------------------------------- */
/* Sits directly under the sticky header, whose inner min-height is 4.25rem. */
.capindex {
  position: sticky; top: 4.25rem; z-index: 40;
  background: rgba(237, 234, 227, 0.94);
  backdrop-filter: blur(8px);
  border-bottom: 1px solid var(--hairline);
}
.capindex__progress {
  position: absolute; left: 0; bottom: -1px;
  width: 100%; height: 2px;
  background: var(--signal);
  transform: scaleX(0); transform-origin: left;
}
.capindex__list {
  display: flex;
  overflow-x: auto;
  scrollbar-width: none;
  scroll-snap-type: x proximity;
  margin-inline: calc(var(--s-5) * -1);
}
@media (min-width: 48em) { .capindex__list { margin-inline: calc(var(--s-7) * -1); } }
.capindex__list::-webkit-scrollbar { display: none; }
.capindex__list > li { flex: 0 0 auto; scroll-snap-align: center; }
.capindex a {
  display: flex; align-items: baseline; gap: var(--s-2);
  padding: var(--s-4);
  font-family: var(--f-mono); font-size: var(--t-xs);
  letter-spacing: 0.12em; text-transform: uppercase;
  color: var(--khaki-ink); text-decoration: none;
  white-space: nowrap;
  border-bottom: 2px solid transparent;
  transition: color 0.2s var(--ease), border-color 0.2s var(--ease);
}
.capindex a:hover { color: var(--basalt); }
.capindex__n { color: var(--signal-ink); }
.capindex a[aria-current="true"] { color: var(--basalt); border-bottom-color: var(--signal); }

/* ---- Zig-zag panels ------------------------------------------------------ */
.capzig {
  position: relative;
  overflow: clip;
  padding-block: var(--s-8);
  /* Clear the sticky header plus the sticky rail when an anchor lands. */
  scroll-margin-top: 7.75rem;
}
@media (min-width: 48em) { .capzig { padding-block: var(--s-9); } }
@media (min-width: 60em) {
  .capzig { display: flex; align-items: center; min-height: 80vh; }
}
/* Two light panels never meet - the alternation supplies the separation - and
   the first panel follows the index rail, which carries its own hairline. */

.capzig__grid { display: grid; gap: var(--s-6); }
@media (min-width: 60em) {
  .capzig__grid {
    grid-template-columns: repeat(12, 1fr);
    gap: var(--s-8); align-items: center;
  }
  /* Both pinned to row 1: with only a column start declared, sparse
     auto-placement drops the body to a second row on the flipped panels,
     where its start line sits before the media's. */
  .capzig__mediacol, .capzig__body { grid-row: 1; }
  .capzig__mediacol { grid-column: 1 / span 6; }
  .capzig__body     { grid-column: 8 / span 5; }
  .capzig--flip .capzig__mediacol { grid-column: 7 / span 6; }
  .capzig--flip .capzig__body     { grid-column: 1 / span 5; }
}

.capzig__mediacol { position: relative; }

/* The media carries .reveal so the shared observer drives it, but it opts out
   of the fade-and-rise: the wipe below is the reveal. */
.capzig__media {
  display: block; position: relative;
  aspect-ratio: 3 / 2;
  overflow: clip;
  border-radius: var(--radius);
  background: var(--navy-900);
}
/* Taller once the panel goes two-column: a 3:2 frame beside a full text
   column leaves a conspicuous void. Must sit after the base rule - a media
   query adds no specificity, so this is decided on source order. */
@media (min-width: 60em) { .capzig__media { aspect-ratio: 4 / 3; } }
.capzig__media.reveal { opacity: 1; transform: none; }
.capzig__media > picture { display: block; width: 100%; height: 100%; }

/* The wipe clips the picture, never .capzig__media itself.
   clip-path collapses an element's intersection rectangle to 0x0, and the
   reveal observer in site.js fires at threshold 0.1 - so clipping the observed
   element deadlocks it: it stays hidden because it never intersects, and it
   never intersects because it is hidden. Clipping a child leaves the observed
   box its full area and the wipe still reads identically. */
.js .capzig__media.reveal > picture,
.js .capzig__media.reveal > svg { clip-path: inset(0 100% 0 0); }
.js .capzig--flip .capzig__media.reveal > picture,
.js .capzig--flip .capzig__media.reveal > svg { clip-path: inset(0 0 0 100%); }
/* Two openers, not one: the flip closer above is a class heavier than a shared
   opener would be, and would keep winning. */
.js .capzig__media.reveal.is-in > picture,
.js .capzig__media.reveal.is-in > svg,
.js .capzig--flip .capzig__media.reveal.is-in > picture,
.js .capzig--flip .capzig__media.reveal.is-in > svg {
  clip-path: inset(0 0 0 0);
  transition: clip-path 0.95s var(--ease);
}
.capzig__media img, .capzig__media svg {
  width: 100%; height: 100%; object-fit: cover;
  transform: scale(1.06);
  filter: saturate(0.72) contrast(1.04);
  transition: filter 0.5s var(--ease);
}
.capzig__media:hover img { filter: saturate(1) contrast(1); }
/* Inset hairline that only exists on hover - a frame, not a border. */
.capzig__frame {
  position: absolute; inset: 10px;
  border: 1px solid transparent;
  transition: border-color 0.25s var(--ease);
  pointer-events: none;
}
.capzig__media:hover .capzig__frame { border-color: var(--signal); }

/* Outlined index numeral, bleeding off the top corner beside the media. */
.capzig__num {
  position: absolute; z-index: 2;
  top: -0.34em; left: -0.06em;
  font-family: var(--f-display); font-weight: 700;
  font-size: clamp(3.5rem, 10vw, 8.5rem);
  line-height: 1; letter-spacing: -0.04em;
  color: transparent;
  -webkit-text-stroke: 1.5px rgba(98, 92, 75, 0.45);
  pointer-events: none;
}
.capzig--flip .capzig__num {
  left: auto; right: -0.06em;
  -webkit-text-stroke-color: rgba(168, 161, 143, 0.34);
}

.capzig__body { position: relative; z-index: 1; }

.capzig__domain { display: flex; align-items: center; gap: var(--s-3); margin-bottom: var(--s-5); }
.capzig__dash {
  display: block; height: 1px; width: 40px;
  background: var(--signal);
}
.js .capzig__dash { width: 0; transition: width 0.7s var(--ease) 0.15s; }
.capzig__domain.is-in .capzig__dash { width: 40px; }

.capzig__title { font-size: clamp(1.9rem, 2.6vw + 1rem, 3rem); margin-bottom: var(--s-4); }
.capzig__title.reveal, .capcta__h.reveal { opacity: 1; transform: none; }
.js .capzig__title.reveal .capword, .js .capcta__h.reveal .capword {
  opacity: 0; transform: translateY(0.42em);
  transition: opacity 0.6s var(--ease), transform 0.6s var(--ease);
  transition-delay: calc(var(--i, 1) * 65ms);
}
.capzig__title.reveal.is-in .capword, .capcta__h.reveal.is-in .capword {
  opacity: 1; transform: none;
}

.capzig__claim {
  font-family: var(--f-display); font-weight: 700;
  font-size: var(--t-lg); line-height: 1.2; letter-spacing: -0.01em;
  color: var(--signal-ink); max-width: 26ch;
  margin-bottom: var(--s-5);
}
.band .capzig__claim { color: var(--signal); }
.capzig__lede { margin-bottom: var(--s-6); }
.capzig__go { margin-top: var(--s-6); }

/* ---- Scope list ---------------------------------------------------------- */
/* Also used by "What you will not find here", which is why it is not scoped to
   .capzig. Deliberately outside .prose so the dash can animate. */
.capscope { display: grid; gap: var(--s-3); }
.capscope.reveal { opacity: 1; transform: none; }
.capscope > li {
  position: relative; padding-left: var(--s-5);
  font-size: var(--t-sm); color: var(--gunmetal); max-width: 52ch;
}
.js .capscope > li {
  opacity: 0; transform: translateY(10px);
  transition: opacity 0.5s var(--ease), transform 0.5s var(--ease);
  transition-delay: calc(var(--i, 1) * 85ms);
}
.band .capscope > li { color: var(--khaki); }
.capscope > li::before {
  content: ""; position: absolute; left: 0; top: 0.66em;
  width: 12px; height: 2px; background: var(--signal);
}
.js .capscope > li::before {
  width: 0;
  transition: width 0.5s var(--ease);
  transition-delay: calc(var(--i, 1) * 85ms + 140ms);
}
.capscope.is-in > li { opacity: 1; transform: none; }
.capscope.is-in > li::before { width: 12px; }
.capscope > li:nth-child(1) { --i: 1; }
.capscope > li:nth-child(2) { --i: 2; }
.capscope > li:nth-child(3) { --i: 3; }
.capscope > li:nth-child(4) { --i: 4; }
.capscope > li:nth-child(5) { --i: 5; }
.capscope > li:nth-child(6) { --i: 6; }

/* ---- Engagement sequence ------------------------------------------------- */
.capflow__steps {
  position: relative;
  display: grid; gap: var(--s-6);
  margin-top: var(--s-8); padding-top: var(--s-6);
}
@media (min-width: 60em) {
  .capflow__steps { grid-template-columns: repeat(4, 1fr); gap: var(--s-7); }
}
/* The rule that runs through all four steps, drawn on entry. */
.capflow__steps.reveal { opacity: 1; transform: none; }
.capflow__steps::before {
  content: ""; position: absolute; inset-inline: 0; top: 0; height: 1px;
  background: var(--gunmetal); transform-origin: left;
}
.js .capflow__steps::before {
  transform: scaleX(0);
  transition: transform 1.2s var(--ease);
}
.capflow__steps.is-in::before { transform: scaleX(1); }
.capflow__steps > li.reveal.is-in { transition-delay: calc(var(--i, 1) * 110ms); }
.capflow__steps > li:nth-child(1) { --i: 1; }
.capflow__steps > li:nth-child(2) { --i: 2; }
.capflow__steps > li:nth-child(3) { --i: 3; }
.capflow__steps > li:nth-child(4) { --i: 4; }
.capflow__n {
  display: block;
  font-family: var(--f-display); font-weight: 700;
  font-size: var(--t-2xl); line-height: 1; color: var(--signal);
  margin-bottom: var(--s-4);
}
.capflow__steps h3 { font-size: var(--t-md); margin-bottom: var(--s-3); }
.capflow__steps p { color: var(--khaki); font-size: var(--t-sm); max-width: 44ch; }

/* ---- Closing statement --------------------------------------------------- */
.capcta__h { font-size: clamp(2rem, 3.4vw + 1rem, 3.4rem); }

/* ---- Scroll-scrubbed motion ----------------------------------------------
   Progressive enhancement only. Where animation-timeline is unsupported these
   declarations never apply and the panels keep the observer reveals.
   -------------------------------------------------------------------------- */
@supports (animation-timeline: view()) {
  /* Named so the progress bar, which is not a descendant of the panels, can
     still read their combined range. */
  #main { timeline-scope: --caplines; }
  .capzigs { view-timeline-name: --caplines; }

  .capindex__progress {
    animation: capProgress linear both;
    animation-timeline: --caplines;
    animation-range: entry 100% exit 0%;
  }
  @keyframes capProgress { from { transform: scaleX(0); } to { transform: scaleX(1); } }

  /* Slow drift across the whole pass. The scale keeps the frame covered at
     both ends of the translate. */
  .capzig__media img {
    animation: capDrift linear both;
    animation-timeline: view();
    animation-range: entry 0% exit 100%;
  }
  @keyframes capDrift {
    from { transform: scale(1.14) translateY(-2.4%); }
    to   { transform: scale(1.14) translateY(2.4%); }
  }

  /* The numeral moves against the image, which is what sells the depth. */
  .capzig__num {
    animation: capNum linear both;
    animation-timeline: view();
    animation-range: entry 0% exit 100%;
  }
  @keyframes capNum {
    from { transform: translateY(2rem); }
    to   { transform: translateY(-2rem); }
  }
}

/* ---- Reduced motion -------------------------------------------------------
   Section 14 flattens durations globally, but these effects hide content in
   their resting state, so each one needs its end state asserted here.
   -------------------------------------------------------------------------- */
@media (prefers-reduced-motion: reduce) {
  /* Every selector here mirrors the .js prefix of the rule it overrides.
     Without it the guard would outrank these and the resting states would
     stay hidden for exactly the visitors who asked for less motion. */
  .caphero__tile,
  .caphero__eyebrow, .caphero__lede, .caphero__cue,
  .caphero__h .capword {
    animation: none; opacity: 1; transform: none;
  }
  .caphero__cueline::after { animation: none; transform: scaleX(1); }
  .capindex__progress { animation: none; transform: scaleX(0); }
  .js .capzig__media.reveal > picture, .js .capzig__media.reveal > svg,
  .js .capzig--flip .capzig__media.reveal > picture,
  .js .capzig--flip .capzig__media.reveal > svg,
  .js .capzig__media.reveal.is-in > picture,
  .js .capzig__media.reveal.is-in > svg,
  .js .capzig--flip .capzig__media.reveal.is-in > picture,
  .js .capzig--flip .capzig__media.reveal.is-in > svg { clip-path: none; }
  .capzig__media img, .capzig__num { animation: none; transform: none; }
  .js .capzig__dash, .capzig__domain.is-in .capzig__dash { width: 40px; }
  .js .capzig__title.reveal .capword, .js .capcta__h.reveal .capword {
    opacity: 1; transform: none;
  }
  .js .capscope > li { opacity: 1; transform: none; }
  .js .capscope > li::before, .capscope.is-in > li::before { width: 12px; }
  .js .capflow__steps::before { transform: scaleX(1); }
  .capflow__steps > li.reveal.is-in { transition-delay: 0s; }
}

/* --------------------------------------------------------------------------
   21. About - /about/ only

   Built from section 20's parts rather than beside them: the panel shell, the
   domain label, the title stagger, the claim and the scope lists are all the
   same components, so the two pages read as one system. Three things here are
   new - the hero's three-frame variant, the story panel that replaces the
   zig-zag's media card, and the supply schematic.
   -------------------------------------------------------------------------- */

/* Hero variant: three tall frames instead of the nine-tile mosaic. */
.caphero--strip .caphero__mosaic {
  grid-template-columns: repeat(3, 1fr);
  grid-template-rows: 1fr;
}
@media (max-width: 47.99em) {
  /* Two frames is enough at this width; a third would be a sliver. */
  .caphero--strip .caphero__mosaic { grid-template-columns: repeat(2, 1fr); }
  .caphero--strip .caphero__tile:nth-child(3) { display: none; }
}

/* ---- Hero strapline ------------------------------------------------------
   Three words under the headline. Set in signal at display weight so it reads
   as a claim rather than as a second deck of heading, and staggered a beat
   behind the headline words it follows.
   -------------------------------------------------------------------------- */
.caphero__strap {
  margin-bottom: var(--s-5);
  font-family: var(--f-display); font-weight: 700;
  font-size: clamp(1.1rem, 1.2vw + 0.8rem, 1.65rem);
  line-height: 1.2; letter-spacing: 0.01em;
  color: var(--signal);
}
.caphero__strap .capword {
  animation: capUp 0.9s var(--ease) both;
  animation-delay: calc(0.66s + var(--i, 1) * 90ms);
}

/* The headline is five words here rather than the nine it used to be, but the
   strapline now sits between it and the lede - so this hero alone runs its
   last two elements later. Scoped to --strip: /capabilities/ has no strapline
   and its timings are still correct. */
.caphero--strip .caphero__lede { animation-delay: 1.02s; }
.caphero--strip .caphero__cue  { animation-delay: 1.18s; }

@media (prefers-reduced-motion: reduce) {
  .caphero__strap .capword { animation: none; }
}

/* Reach opens with no top border of its own: the story panels above it are
   wrapped in the #experience div, so Reach's previous *sibling* is that div and
   no adjacent-sibling rule reaches it - not `.band + .band`, not
   `.section + .section`. Against the dark panel above, the two would run
   together into one slab without this. */
.section.reach.band { border-top: 1px solid var(--gunmetal); }

/* ---- Story panels --------------------------------------------------------
   /about/ has two panels, not nine, so it drops the media card and the index
   numerals and splits the panel head from its body instead. Both columns then
   carry content - the card layout left one of them conspicuously empty.
   -------------------------------------------------------------------------- */
.capstory > .wrap { position: relative; z-index: 1; }

.capstory__grid { display: grid; gap: var(--s-6); }
@media (min-width: 60em) {
  .capstory__grid {
    grid-template-columns: repeat(12, 1fr);
    gap: var(--s-8);
    align-items: start;
  }
  /* Pinned to row 1 for the same reason the zig-zag panels are: with only a
     start column declared, sparse auto-placement drops the second block onto a
     new row wherever its start line precedes the first block's. */
  .capstory__head, .capstory__body { grid-row: 1; }
  .capstory__head { grid-column: 1 / span 5; }
  .capstory__body { grid-column: 7 / span 6; }
  .capstory--flip .capstory__head { grid-column: 8 / span 5; }
  .capstory--flip .capstory__body { grid-column: 1 / span 6; }
}
/* The label lives inside the head, directly over its own title, so that on a
   flipped panel it travels to the right with the title instead of stranding
   itself across the page from it. */
.capstory .capzig__domain { margin-bottom: var(--s-5); }
.capstory .capzig__title  { margin-bottom: var(--s-5); }
/* Narrower than on /capabilities/, and last in its column, so it sets the
   head's ragged edge rather than running to meet the body. */
.capstory .capzig__claim  { max-width: 23ch; margin-bottom: 0; }

/* ---- Photographic ground -------------------------------------------------
   A whole photograph, sunk far enough back to read as ground rather than as a
   picture. Stored greyscale already (see assets/img/about/README.md), so the
   filter is insurance against a replacement file that is not.
   -------------------------------------------------------------------------- */
.capstory__ground {
  position: absolute; inset: 0; z-index: -2;
  pointer-events: none;
  -webkit-mask-image: linear-gradient(to right, #000 0%, rgba(0,0,0,0.42) 38%, transparent 66%);
          mask-image: linear-gradient(to right, #000 0%, rgba(0,0,0,0.42) 38%, transparent 66%);
}
.capstory--flip .capstory__ground {
  -webkit-mask-image: linear-gradient(to left, #000 0%, rgba(0,0,0,0.42) 38%, transparent 66%);
          mask-image: linear-gradient(to left, #000 0%, rgba(0,0,0,0.42) 38%, transparent 66%);
}
.capstory__ground picture { display: block; height: 100%; }
.capstory__ground img {
  width: 100%; height: 100%; object-fit: cover; object-position: center;
  filter: grayscale(1) contrast(1.05);
  opacity: 0.13;
}
/* Basalt swallows a ghost this faint. */
.band .capstory__ground img { opacity: 0.22; filter: grayscale(1) brightness(0.9); }

@media (max-width: 59.99em) {
  /* Stacked, the type crosses the full width, so the ground fades downward
     rather than sideways and sits back a little further. */
  .capstory__ground,
  .capstory--flip .capstory__ground {
    -webkit-mask-image: linear-gradient(to bottom, rgba(0,0,0,0.35) 0%, #000 100%);
            mask-image: linear-gradient(to bottom, rgba(0,0,0,0.35) 0%, #000 100%);
  }
  .capstory__ground img { opacity: 0.10; }
  .band .capstory__ground img { opacity: 0.18; }
}

/* The measured grid sits between the photograph and the type. Its one-sided
   radial mask was drawn for an empty column that no longer exists, so here it
   becomes an even wash the ground can show through. */
.capstory.tex::before {
  opacity: 0.22;
  -webkit-mask-image: linear-gradient(to bottom, #000 0%, #000 68%, transparent 97%);
          mask-image: linear-gradient(to bottom, #000 0%, #000 68%, transparent 97%);
}

@supports (animation-timeline: view()) {
  /* Slow counter-drift at a fraction of the capabilities panels' amplitude.
     This is a background; anything more reads as the page slipping. */
  .capstory__ground img {
    animation: capGround linear both;
    animation-timeline: view();
    animation-range: entry 0% exit 100%;
  }
  @keyframes capGround {
    from { transform: scale(1.08) translateY(-1.6%); }
    to   { transform: scale(1.08) translateY(1.6%); }
  }
}
@media (prefers-reduced-motion: reduce) {
  .capstory__ground img { animation: none; transform: none; }
}

/* ---- Supply schematic ---------------------------------------------------- */
.reach__figure {
  margin-block: var(--s-8);
  border-block: 1px solid var(--gunmetal);
  padding-block: var(--s-6);
  overflow-x: auto;
}
.reach__figure svg { width: 100%; min-width: 40rem; height: auto; }

.reach__arc { stroke: var(--khaki); stroke-width: 1.25; opacity: 0.55; }
.reach__ring { stroke: var(--navy-700); stroke-width: 1; }
.reach__node { fill: none; stroke: var(--khaki); stroke-width: 1.25; }
.reach__hub, .reach__tip { fill: var(--signal); }

.reach__label {
  fill: var(--khaki);
  font-family: var(--f-mono); font-size: 15px;
  letter-spacing: 0.14em;
}
.reach__label--hub {
  fill: var(--gypsum); text-anchor: middle;
  font-size: 20px; letter-spacing: 0.2em;
}
.reach__label--end { text-anchor: end; }
/* Column headings and the hub caption. Dimmer and wider-tracked than the nodes
   they sit over, so they read as a key rather than as another label. Colour,
   not opacity - the reveal transition ends by asserting `opacity: 1` on every
   .reach__label, which would override any opacity set here.
   Not --khaki-ink: at 2.8:1 on basalt it failed AA outright. This is the
   dimmest value that still clears 4.5:1 while staying clearly quieter than the
   --khaki node labels beside it. */
.reach__label--head { fill: #868070; font-size: 12px; letter-spacing: 0.26em; }

.reach__notes { margin-top: var(--s-7); }
@media (min-width: 60em) {
  .reach__notes { grid-template-columns: repeat(2, 1fr); column-gap: var(--s-7); }
}

/* The corridors draw themselves in as the figure arrives. Dash lengths are
   generous enough to cover the longest path; the exact value does not matter
   provided it exceeds it. */
.js .reach__arc path {
  stroke-dasharray: 700;
  stroke-dashoffset: 700;
}
.js .reveal.is-in .reach__arc path {
  stroke-dashoffset: 0;
  transition: stroke-dashoffset 1.5s var(--ease) 0.15s;
}
.js .reach__hub, .js .reach__tip, .js .reach__node, .js .reach__label { opacity: 0; }
.js .reveal.is-in .reach__hub,
.js .reveal.is-in .reach__tip,
.js .reveal.is-in .reach__node,
.js .reveal.is-in .reach__label {
  opacity: 1;
  transition: opacity 0.6s var(--ease) 0.9s;
}
.js .reach__ring circle { transform-origin: 660px 170px; transform: scale(0.6); opacity: 0; }
.js .reveal.is-in .reach__ring circle {
  transform: scale(1); opacity: 1;
  transition: transform 0.9s var(--ease) 0.8s, opacity 0.9s var(--ease) 0.8s;
}

@media (prefers-reduced-motion: reduce) {
  .js .reach__arc path { stroke-dashoffset: 0; }
  .js .reach__hub, .js .reach__tip, .js .reach__node, .js .reach__label { opacity: 1; }
  .js .reach__ring circle { transform: none; opacity: 1; }
}

/* ---- Ground texture ------------------------------------------------------
   A faint measured grid, drawn in gradients so it costs no request. It gives
   the open half of a panel something to be without competing with the type:
   at these opacities it reads as paper, not as pattern.

   The radial mask concentrates it behind the media column and fades it out
   under the text, so nothing ever sits on a grid line it has to fight.
   -------------------------------------------------------------------------- */
.tex { position: relative; isolation: isolate; }
.tex::before {
  content: ""; position: absolute; inset: 0; z-index: -1;
  pointer-events: none;
  background-image:
    linear-gradient(to right, var(--hairline) 1px, transparent 1px),
    linear-gradient(to bottom, var(--hairline) 1px, transparent 1px);
  background-size: 88px 88px;
  opacity: 0.34;
  -webkit-mask-image: radial-gradient(72% 80% at 26% 50%, #000 18%, transparent 76%);
          mask-image: radial-gradient(72% 80% at 26% 50%, #000 18%, transparent 76%);
}
/* Flipped panels keep their open space on the other side. */
.capzig--flip.tex::before {
  -webkit-mask-image: radial-gradient(72% 80% at 74% 50%, #000 18%, transparent 76%);
          mask-image: radial-gradient(72% 80% at 74% 50%, #000 18%, transparent 76%);
}
/* On basalt the same grid needs to be quieter, and it uses the darker rule. */
.band.tex::before {
  background-image:
    linear-gradient(to right, var(--gunmetal) 1px, transparent 1px),
    linear-gradient(to bottom, var(--gunmetal) 1px, transparent 1px);
  opacity: 0.55;
}

/* A single soft wash keyed to the logo navy, sat behind the grid. Enough to
   stop a large light section reading as blank paper. */
.tex--wash::after {
  content: ""; position: absolute; inset: 0; z-index: -2;
  pointer-events: none;
  background: radial-gradient(58% 70% at 22% 42%,
    rgba(30, 58, 110, 0.055), transparent 70%);
}
.band.tex--wash::after {
  background: radial-gradient(58% 70% at 22% 42%,
    rgba(30, 58, 110, 0.30), transparent 70%);
}
.capzig--flip.tex--wash::after {
  background: radial-gradient(58% 70% at 78% 42%,
    rgba(30, 58, 110, 0.30), transparent 70%);
}

@media (prefers-reduced-motion: reduce) {
  /* nothing here animates, but keep the block honest if that changes */
  .tex::before, .tex--wash::after { animation: none; }
}
