/* Cornercase Solutions LLC
   Tokens and rationale: see DESIGN.md at the repo root.
   Contrast ratios are computed by tools/contrast.mjs, not eyeballed. */

/* ---------------------------------------------------------------- tokens */

:root {
  color-scheme: dark;

  /* Every neutral is tinted toward the logo charcoal (hue 262). No #000, no
     #fff. Each of the logo's colours has exactly one job on the page; see the
     colour section of DESIGN.md for the roles and the measured ratios.

     The three grounds sit as deep as the no-pure-black rule allows, so the star
     field has something to be seen against. They drop together, keeping the
     +0.040 and +0.087 steps between them, so the alternating band and the inset
     wells still read as lifts off the ground rather than as panels laid on it.
     L=0.130 is the floor: at 0.090 the ground renders #010205, which is black
     by any measure a reader can see. */
  --ground:        oklch(0.130 0.013 262);
  --ground-2:      oklch(0.170 0.015 262);
  --ground-3:      oklch(0.217 0.017 262);
  --ink:           oklch(0.965 0.005 262);
  --ink-2:         oklch(0.795 0.013 262);
  --ink-3:         oklch(0.685 0.016 262);
  --rule:          oklch(0.325 0.016 262);
  /* Held at L=0.550 on purpose. It has to clear 3.0 against all three grounds,
     and since the grounds went deep the tightest of them is --ground-3 rather
     than --ground-2: L=0.505 measures 2.97 there, missing by the same hair it
     used to miss --ground-2 by. Bounds every control. */
  --rule-strong:   oklch(0.550 0.020 262);
  --signal:        oklch(0.720 0.128 248);
  --signal-hover:  oklch(0.825 0.098 248);
  --signal-mark:   oklch(0.620 0.150 252);
  /* The logo's centre cube, and the one thing on the page allowed to wear it:
     the lit core of the hero motif. Nothing else is yellow. */
  --core:          oklch(0.860 0.160 88);
  --array:         oklch(0.620 0.014 262);
  /* The logo's red, lifted off the near-black ground far enough to clear 4.5:1
     at header size. It has exactly two jobs, and they share a value because
     they are the same colour doing the same thing in two places: the CASE half
     of the wordmark, and a field that failed validation. */
  --brand-red:     oklch(0.680 0.185 27);
  --danger:        var(--brand-red);

  --sans: "Geist", ui-sans-serif, system-ui, -apple-system, "Segoe UI", Roboto,
          "Helvetica Neue", Arial, sans-serif;
  --mono: "Geist Mono", ui-monospace, "SF Mono", "Cascadia Mono", Menlo,
          Consolas, "Liberation Mono", monospace;

  --measure: 68ch;
  --gutter: 1.25rem;
  --shell: min(100% - (var(--gutter) * 2), 76rem);
  --shell-narrow: min(100% - (var(--gutter) * 2), 62rem);

  --step--1: clamp(0.8125rem, 0.79rem + 0.1vw, 0.875rem);
  --step-0:  clamp(1rem, 0.97rem + 0.15vw, 1.0625rem);
  --step-1:  clamp(1.1875rem, 1.12rem + 0.32vw, 1.375rem);
  --step-2:  clamp(1.4375rem, 1.32rem + 0.58vw, 1.8125rem);
  --step-3:  clamp(1.75rem, 1.55rem + 1vw, 2.4375rem);
  --step-4:  clamp(2rem, 1.35rem + 2.6vw, 3.4rem);

  --ease: cubic-bezier(0.16, 1, 0.3, 1);
  --dur: 480ms;
  --header-h: 3.75rem;
}

/* ------------------------------------------------------------ foundation */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
  scroll-padding-top: calc(var(--header-h) + 1.5rem);
}

body {
  margin: 0;
  background: var(--ground);
  color: var(--ink);
  font-family: var(--sans);
  font-size: var(--step-0);
  /* Light type on a dark ground needs more room between lines than dark type on
     a light one; 1.6 read tight once the palette inverted. */
  line-height: 1.65;
  font-synthesis-weight: none;
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
  overflow-wrap: break-word;
}

/* The ambient ground. Two very wide, very low-contrast lifts give the page
   depth without an image, a starfield, or a glow: the room is dim, not lit.
   The grain on top is not decoration, it is dither. A gradient this wide from
   --ground-3 to --ground bands visibly on an 8-bit display, and 3% noise is
   what kills the steps. One fixed, pointer-events-none layer, so it never
   repaints while the page scrolls. */
body::before {
  content: "";
  position: fixed;
  inset: 0;
  z-index: -1;
  pointer-events: none;
  background:
    url("data:image/svg+xml,%3Csvg xmlns='http://www.w3.org/2000/svg' width='160' height='160'%3E%3Cfilter id='n'%3E%3CfeTurbulence type='fractalNoise' baseFrequency='0.85' numOctaves='3' stitchTiles='stitch'/%3E%3C/filter%3E%3Crect width='160' height='160' filter='url(%23n)' opacity='0.032'/%3E%3C/svg%3E") repeat,
    radial-gradient(115rem 64rem at 8% -16%, var(--ground-3), transparent 58%),
    radial-gradient(78rem 58rem at 92% 4%, var(--ground-2), transparent 62%);
  background-size: 160px 160px, auto, auto;
}

h1, h2, h3, h4 {
  margin: 0;
  /* One step lighter than the light theme carried. Light strokes on dark bloom
     optically, so 550 read as bold; hierarchy leans on size and colour instead. */
  font-weight: 500;
  line-height: 1.15;
  letter-spacing: -0.022em;
  text-wrap: balance;
}

p { margin: 0; max-width: var(--measure); text-wrap: pretty; }
ul, ol { margin: 0; padding: 0; list-style: none; }
img, svg { display: block; max-width: 100%; }

a {
  color: var(--signal);
  text-decoration-thickness: 1px;
  text-underline-offset: 0.22em;
  transition: color 180ms var(--ease);
}
a:hover { color: var(--signal-hover); }

:where(a, button, input, select, textarea, summary):focus-visible {
  outline: 2px solid var(--signal);
  outline-offset: 2px;
  border-radius: 2px;
}

::selection { background: var(--signal-mark); color: var(--ink); }

.shell { width: var(--shell); margin-inline: auto; }
.shell-narrow { width: var(--shell-narrow); margin-inline: auto; }

.visually-hidden {
  position: absolute; width: 1px; height: 1px; padding: 0;
  margin: -1px; overflow: hidden; clip-path: inset(50%); white-space: nowrap;
}

.skip-link {
  position: absolute; top: 0; left: var(--gutter);
  z-index: 100;
  transform: translateY(-120%);
  padding: 0.7rem 1.1rem;
  background: var(--ink); color: var(--ground);
  font: 500 var(--step--1)/1 var(--sans);
  text-decoration: none;
  transition: transform 220ms var(--ease);
}
.skip-link:focus-visible { transform: translateY(0.5rem); }

/* ------------------------------------------------------- shared elements */

.eyebrow {
  margin: 0;
  font-family: var(--mono);
  font-size: var(--step--1);
  font-weight: 450;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-3);
}

.eyebrow--marked {
  display: flex;
  align-items: center;
  gap: 0.7rem;
}
.eyebrow--marked::before {
  content: "";
  width: 0.4375rem; height: 0.4375rem;
  flex: none;
  background: var(--signal-mark);
}

.lede {
  font-size: var(--step-1);
  line-height: 1.45;
  color: var(--ink-2);
  letter-spacing: -0.008em;
}

.rule { border: 0; border-top: 1px solid var(--rule); margin: 0; }

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

.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: 0.6rem;
  min-height: 2.875rem;
  padding: 0.75rem 1.4rem;
  border: 1px solid transparent;
  border-radius: 2px;
  font: 500 var(--step-0)/1 var(--sans);
  letter-spacing: -0.005em;
  text-decoration: none;
  cursor: pointer;
  transition: background-color 180ms var(--ease), color 180ms var(--ease),
              border-color 180ms var(--ease), transform 120ms var(--ease);
}
.btn:active { transform: translateY(1px); }

.btn--primary { background: var(--signal); color: var(--ground); }
.btn--primary:hover { background: var(--signal-hover); color: var(--ground); }

.btn--ghost {
  background: transparent;
  color: var(--ink);
  border-color: var(--rule-strong);
}
.btn--ghost:hover { color: var(--ink); border-color: var(--ink); background: var(--ground-2); }

.btn--sm { min-height: 2.375rem; padding: 0.5rem 1rem; font-size: var(--step--1); }

.btn[disabled] { opacity: 0.55; cursor: not-allowed; transform: none; }

/* Section scaffolding ---------------------------------------------------- */

.section { padding-block: clamp(3.5rem, 7vw, 6rem); }
.section--roomy { padding-block: clamp(4.5rem, 9vw, 8rem); }
.section--band { background: var(--ground-2); }
.section + .section:not(.section--band) { border-top: 1px solid var(--rule); }

.section-head {
  display: grid;
  gap: 0.9rem;
  margin-bottom: clamp(2.25rem, 4vw, 3.25rem);
}
.section-head h2 { font-size: var(--step-3); }
.section-head p { color: var(--ink-2); }

/* ------------------------------------------------------------ site header */

.site-header {
  position: sticky;
  top: 0;
  z-index: 50;
  background: color-mix(in oklab, var(--ground) 88%, transparent);
  backdrop-filter: saturate(1.4) blur(10px);
  border-bottom: 1px solid var(--rule);
}

.site-header__inner {
  width: var(--shell);
  margin-inline: auto;
  min-height: var(--header-h);
  display: flex;
  align-items: center;
  gap: 1.5rem;
}

.brand {
  display: inline-flex;
  align-items: center;
  gap: 0.6875rem;
  text-decoration: none;
  color: var(--ink);
  margin-right: auto;
  padding-block: 0.5rem;
}
.brand img { width: 1.875rem; height: 1.875rem; flex: none; }

.brand__name {
  display: grid;
  line-height: 1.05;
}
.brand__name b {
  font-weight: 600;
  font-size: 0.9375rem;
  letter-spacing: 0.1em;
  text-transform: uppercase;
}
/* The wordmark splits CORNER / CASE / SOLUTIONS LLC across three colours, as
   the logo does. The em carries colour, not italics. Both accents clear 4.5:1
   against --ground at this size. */
.brand__name em { font-style: normal; color: var(--brand-red); }
.brand__name span {
  font-family: var(--mono);
  font-size: 0.625rem;
  font-weight: 450;
  letter-spacing: 0.19em;
  text-transform: uppercase;
  color: var(--signal);
}

.nav__list { display: flex; align-items: center; gap: 1.75rem; }
.nav__list a {
  color: var(--ink-2);
  text-decoration: none;
  font-size: var(--step--1);
  font-weight: 450;
  letter-spacing: -0.003em;
  padding-block: 0.5rem;
  transition: color 180ms var(--ease);
}
.nav__list a:hover { color: var(--ink); }
.nav__list .btn--primary { color: var(--ground); padding-block: 0.5rem; }
.nav__list .btn--primary:hover { color: var(--ground); }
.nav__list .btn--ghost { color: var(--ink); }

.nav-toggle { display: none; }

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

.hero {
  padding-block: clamp(2.75rem, 5.5vw, 4.5rem) clamp(2.75rem, 5vw, 4.25rem);
  position: relative;
}

.hero__grid {
  display: grid;
  grid-template-columns: minmax(0, 1.05fr) minmax(0, 0.95fr);
  gap: clamp(2.5rem, 5vw, 4rem);
  align-items: center;
  position: relative;
  z-index: 1;
}

/* The scene's canvas. Created by hero-scene.js and kept, because where WebGL
   runs the scene is the hero's resting state rather than an intro. It spans the
   hero band and sits under the grid above: the field it draws passes behind the
   headline rather than over it, and the copy is readable and clickable from the
   first frame. The model itself is framed on .hero__figure, not on this box. */
.hero__stage {
  position: absolute;
  inset: 0;
  /* Explicit, and not redundant with inset. A canvas is a replaced element with
     an intrinsic size, so an absolutely positioned one resolves `width: auto`
     to its own width attribute and ignores the opposing offset. The attribute
     carries the device-pixel backing store, so on a 2x display the element laid
     itself out at twice the hero's width and pushed the page into horizontal
     overflow. */
  width: 100%;
  height: 100%;
  z-index: 0;
  pointer-events: none;
  opacity: 1;
}

.hero__copy { display: grid; gap: 1.5rem; justify-items: start; }
.hero h1 { font-size: var(--step-4); max-width: 21ch; }
.hero .lede { max-width: 46ch; }

.hero__actions { display: flex; flex-wrap: wrap; gap: 0.75rem; margin-top: 0.25rem; }

.hero__note {
  font-family: var(--mono);
  font-size: var(--step--1);
  color: var(--ink-3);
  line-height: 1.5;
  max-width: 42ch;
}

/* Detector-grid motif ---------------------------------------------------- */

.detector { width: 100%; height: auto; }
.detector rect.cell { opacity: var(--o, 1); }
.detector.is-lit rect.cell {
  animation: cell-in 620ms var(--ease) both;
  /* Per-cell, so it is the cell count that sets the tail: 323 x 4 + 620 = 1.9s,
     which is where this has sat since it stopped being the only motion on the
     page and became the end of a sequence. The array still develops visibly
     outward and lands inside the budget, on the intro's path and on the
     fallback path both. Re-derive this if the generator's grid changes. */
  animation-delay: calc(var(--i, 0) * 4ms);
}
@keyframes cell-in { from { opacity: 0; } to { opacity: var(--o, 1); } }

/* The stand-in. While `scene-armed` is set the canvas owns the figure and the
   schematic waits at zero; script removes the class if the scene cannot come up,
   and the schematic is simply there again. The class lives on <html> and is set
   by script, so with JS blocked it never exists and the schematic just renders,
   which is the same reasoning as the .reveal gate further down.

   opacity, not visibility or display: the module measures this element's box
   every frame to frame the model on it, and it has to keep a box to be
   measured. */
.scene-armed .detector { opacity: 0; }

/* --------------------------------------------------------- programs strip */

.strip {
  border-block: 1px solid var(--rule);
  background: var(--ground-2);
  padding-block: 1.5rem;
}
.strip__inner { width: var(--shell); margin-inline: auto; display: grid; gap: 1rem; }
.strip__label { white-space: nowrap; }
.strip__list {
  display: flex;
  flex-wrap: wrap;
  gap: 0.5rem 0;
}
.strip__list li {
  font-family: var(--mono);
  font-size: var(--step--1);
  color: var(--ink);
  letter-spacing: -0.01em;
  padding-inline: 0.9rem;
  border-left: 1px solid var(--rule);
}
.strip__list li:first-child { padding-left: 0; border-left: 0; }

/* ------------------------------------------------------------ capabilities */

.split {
  display: grid;
  grid-template-columns: minmax(0, 0.8fr) minmax(0, 1.2fr);
  gap: clamp(2rem, 5vw, 4.5rem);
  align-items: start;
}

.split__aside { display: grid; gap: 1.25rem; position: sticky; top: calc(var(--header-h) + 2rem); }
.split__aside h2 { font-size: var(--step-3); }
.split__aside p { color: var(--ink-2); font-size: var(--step-0); }

.service-area {
  display: grid;
  gap: 0.35rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--rule);
  font-family: var(--mono);
  font-size: var(--step--1);
  color: var(--ink-3);
}
.service-area dt { letter-spacing: 0.09em; text-transform: uppercase; }
.service-area dd { margin: 0; color: var(--ink); }

.capabilities { display: grid; }
.capability:nth-child(1) { --i: 0; }
.capability:nth-child(2) { --i: 1; }
.capability:nth-child(3) { --i: 2; }
.capability:nth-child(4) { --i: 3; }
.capability:nth-child(5) { --i: 4; }
.capability {
  display: grid;
  grid-template-columns: 3rem minmax(0, 1fr);
  gap: 0 1.5rem;
  padding-block: 1.75rem;
  border-top: 1px solid var(--rule);
}
.capability:last-child { border-bottom: 1px solid var(--rule); }
.capability__idx {
  font-family: var(--mono);
  font-size: var(--step--1);
  color: var(--ink-3);
  padding-top: 0.3rem;
  font-variant-numeric: tabular-nums;
}
.capability h3 { font-size: var(--step-1); margin-bottom: 0.6rem; }
.capability p { color: var(--ink-2); font-size: var(--step-0); }
.capability p + p { margin-top: 0.75rem; }
.capability__tags {
  display: flex;
  flex-wrap: wrap;
  gap: 0.4rem 1.1rem;
  margin-top: 0.85rem;
  font-family: var(--mono);
  font-size: 0.8125rem;
  color: var(--ink-3);
}

/* ------------------------------------------------------------------ plates */

/* A plate is a photograph of flight hardware named in the entry above it. It is
   evidence, not ornament, and it is the only photography the site allows; see
   the Plates section of DESIGN.md for the rule that admits it. No card, no
   shadow, no lightbox, no hover trick. It gets a hairline and negative space,
   the same grouping the rest of the page uses. */
.plate {
  margin: 1.5rem 0 0;
  /* Held well inside the text column. At the full 76rem shell the entry runs to
     about 39rem, so the plate stops short of the measure and reads as an
     attachment to the claim rather than as a banner across it. */
  max-width: min(100%, 30rem);
}
.plate img {
  width: 100%;
  height: auto;
  border: 1px solid var(--rule);
  border-radius: 2px;
  /* Holds the box in ground-3 until the bytes arrive, so a slow load never
     flashes white on a page this dark. */
  background: var(--ground-3);
  /* One step back from the ink. Clean rooms are lit, and at full strength a pale
     photograph on a 0.175-lightness ground punches a hole in a page whose whole
     premise is a dim room. Uniform across plates rather than tuned per image,
     because two photographs sitting at different strengths would read as one of
     them being wrong. This dims the plate against its surroundings; the image
     content itself is never altered, and 0.94 stays far above the point where a
     photograph reads as disabled. */
  opacity: 0.94;
}
/* No max-width of its own: the caption takes the plate's width, so its ragged
   right sits under the photograph's edge instead of cutting across it. */
.plate figcaption {
  margin-top: 0.7rem;
  font-family: var(--mono);
  font-size: var(--step--1);
  line-height: 1.5;
  color: var(--ink-2);
}
/* Credit and archive reference. Its own line, one step quieter than the
   caption, in the same mono register the entry's tags already use. */
.plate figcaption small {
  display: block;
  margin-top: 0.3rem;
  font-size: inherit;
  color: var(--ink-3);
}
/* body sets overflow-wrap: break-word, and a hyphen is a break opportunity on
   top of that. Neither may touch an archive id: half of KSC-08PD2456 is not
   something a procurement reader can check. */
.plate figcaption small span { white-space: nowrap; }

/* ------------------------------------------------------- attribution note */

.disclosure {
  margin-top: clamp(2.5rem, 5vw, 3.5rem);
  padding: 1rem 1.25rem;
  background: var(--ground-3);
  border: 1px solid var(--rule);
  border-radius: 2px;
  font-size: var(--step--1);
  color: var(--ink-2);
  max-width: 78ch;
}
.disclosure b { color: var(--ink); font-weight: 550; }

/* --------------------------------------------------------------- skills */

.facts {
  display: grid;
  grid-template-columns: repeat(auto-fit, minmax(min(100%, 17rem), 1fr));
  gap: 0;
  border-top: 1px solid var(--rule);
}
.fact {
  padding: 1.75rem clamp(1rem, 2.5vw, 2rem) 1.75rem 0;
  border-bottom: 1px solid var(--rule);
}
.fact h3 {
  font-family: var(--mono);
  font-size: var(--step--1);
  font-weight: 450;
  letter-spacing: 0.09em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-bottom: 1rem;
}
.fact li { padding-block: 0.35rem; font-size: var(--step-0); }
.fact li span {
  display: block;
  font-family: var(--mono);
  font-size: 0.8125rem;
  color: var(--ink-3);
}

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

.contact__grid {
  display: grid;
  grid-template-columns: minmax(0, 0.75fr) minmax(0, 1.25fr);
  gap: clamp(2rem, 5vw, 4.5rem);
  align-items: start;
}
.contact__aside { display: grid; gap: 1.25rem; }
.contact__aside h2 { font-size: var(--step-3); }
.contact__aside p { color: var(--ink-2); }

.checklist { display: grid; gap: 0.55rem; margin-top: 0.25rem; }
.checklist li {
  display: grid;
  grid-template-columns: 1.1rem minmax(0, 1fr);
  gap: 0.6rem;
  font-size: var(--step--1);
  color: var(--ink-2);
}
.checklist li::before {
  content: "";
  width: 0.375rem; height: 0.375rem;
  margin-top: 0.5rem;
  background: var(--signal-mark);
}

/* Form ------------------------------------------------------------------- */

.form { display: grid; gap: 1.25rem; }
.form__row { display: grid; grid-template-columns: repeat(2, minmax(0, 1fr)); gap: 1.25rem; }

.field { display: grid; gap: 0.5rem; }
.field label { font-size: var(--step--1); font-weight: 550; color: var(--ink); }
.field label .req { color: var(--signal); font-weight: 450; }

.field__hint { font-size: 0.8125rem; color: var(--ink-3); font-family: var(--mono); }

.field input,
.field select,
.field textarea {
  width: 100%;
  min-height: 2.875rem;
  padding: 0.7rem 0.85rem;
  background: var(--ground);
  border: 1px solid var(--rule-strong);
  border-radius: 2px;
  color: var(--ink);
  font: 450 var(--step-0)/1.5 var(--sans);
  transition: border-color 160ms var(--ease), box-shadow 160ms var(--ease);
}
.field textarea { min-height: 8.5rem; resize: vertical; }
.field select { appearance: none; padding-right: 2.5rem; cursor: pointer; }

.field__select { position: relative; }
.field__select::after {
  content: "";
  position: absolute;
  right: 1rem; bottom: 1.1875rem;
  width: 0.5rem; height: 0.5rem;
  border-right: 1.5px solid var(--ink-2);
  border-bottom: 1.5px solid var(--ink-2);
  transform: rotate(45deg);
  pointer-events: none;
}

.field input:hover,
.field select:hover,
.field textarea:hover { border-color: var(--ink-2); }

.field input:focus,
.field select:focus,
.field textarea:focus {
  outline: none;
  border-color: var(--signal);
  box-shadow: 0 0 0 3px color-mix(in oklab, var(--signal) 18%, transparent);
}
.field input:focus-visible,
.field select:focus-visible,
.field textarea:focus-visible { outline: none; }

/* Chrome paints autofilled inputs a fixed pale yellow-blue that ignores the
   page palette entirely, and there is no background property that overrides it.
   An inset shadow the full height of the control is the only thing that does. */
.field input:-webkit-autofill,
.field input:-webkit-autofill:hover,
.field input:-webkit-autofill:focus,
.field textarea:-webkit-autofill,
.field select:-webkit-autofill {
  -webkit-text-fill-color: var(--ink);
  -webkit-box-shadow: 0 0 0 100px var(--ground) inset;
  caret-color: var(--ink);
}

.field__error {
  font-size: 0.8125rem;
  color: var(--danger);
  font-weight: 500;
  display: none;
}
.field.is-invalid input,
.field.is-invalid select,
.field.is-invalid textarea { border-color: var(--danger); }
.field.is-invalid .field__error { display: block; }
.field.is-invalid .field__hint { display: none; }

.form__foot { display: flex; flex-wrap: wrap; align-items: center; gap: 1rem; }
.form__foot .btn { min-width: 11rem; }

.form__status { font-size: var(--step--1); font-weight: 500; }
.form__status[data-tone="error"] { color: var(--danger); }
.form__status:empty { display: none; }

.form__privacy {
  font-family: var(--mono);
  font-size: 0.8125rem;
  color: var(--ink-3);
  line-height: 1.55;
  max-width: 52ch;
}

.form-success {
  display: grid;
  gap: 0.9rem;
  padding: 2rem;
  border: 1px solid var(--rule);
  background: var(--ground);
  border-radius: 2px;
}
.form-success h3 { font-size: var(--step-2); }
.form-success p { color: var(--ink-2); }

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

.site-footer {
  border-top: 1px solid var(--rule);
  background: var(--ground-2);
  padding-block: clamp(2.5rem, 5vw, 3.5rem);
}
.site-footer__inner {
  width: var(--shell);
  margin-inline: auto;
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: flex-start;
  gap: 2rem;
}
.site-footer__meta { display: grid; gap: 0.4rem; font-size: var(--step--1); color: var(--ink-3); }
.site-footer__meta strong { color: var(--ink); font-weight: 550; }
.site-footer nav ul { display: grid; gap: 0.55rem; }
.site-footer nav a { font-size: var(--step--1); color: var(--ink-2); text-decoration: none; }
.site-footer nav a:hover { color: var(--signal); }
.site-footer__fine {
  width: var(--shell);
  margin: 2rem auto 0;
  padding-top: 1.25rem;
  border-top: 1px solid var(--rule);
  font-family: var(--mono);
  font-size: 0.75rem;
  color: var(--ink-3);
  max-width: none;
}

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

/* The hidden starting state is gated on scripting being available, because only
   scripting can undo it. With JS off or blocked the content simply renders, and
   there is no flash of hidden content the way a class toggle would give. */
@media (scripting: enabled) {
  .reveal {
    opacity: 0;
    transform: translateY(14px);
  }
}
.reveal.is-in {
  opacity: 1;
  transform: none;
  transition: opacity var(--dur) var(--ease), transform var(--dur) var(--ease);
  transition-delay: calc(var(--i, 0) * 55ms);
}

/* --------------------------------------------------------- breakpoints */

@media (max-width: 60rem) {
  .hero__grid { grid-template-columns: minmax(0, 1fr); }
  .hero__figure { max-width: 24rem; }
  .split,
  .contact__grid { grid-template-columns: minmax(0, 1fr); }
  .split__aside { position: static; }
}

/* Just above the mobile breakpoint the inline nav packs tight enough that
   two of the links break onto a second line. Tighten the rhythm instead and
   keep every label on one line. */
@media (max-width: 54rem) {
  .site-header__inner { gap: 1rem; }
  .nav__list { gap: 1.125rem; }
  .nav__list a { white-space: nowrap; }
}

@media (max-width: 48rem) {
  :root { --gutter: 1rem; }

  .nav { display: none; }
  .nav-toggle {
    display: inline-flex;
    align-items: center;
    gap: 0.5rem;
    min-height: 2.75rem;
    padding: 0.5rem 0.75rem;
    margin-right: -0.75rem;
    background: none;
    border: 0;
    color: var(--ink);
    font: 500 var(--step--1)/1 var(--sans);
    cursor: pointer;
  }
  .nav-toggle__bars { display: grid; gap: 4px; width: 1.125rem; }
  .nav-toggle__bars i {
    height: 1.5px;
    background: currentColor;
    transition: transform 240ms var(--ease), opacity 160ms var(--ease);
  }
  .nav-toggle[aria-expanded="true"] .nav-toggle__bars i:nth-child(1) { transform: translateY(5.5px) rotate(45deg); }
  .nav-toggle[aria-expanded="true"] .nav-toggle__bars i:nth-child(2) { opacity: 0; }
  .nav-toggle[aria-expanded="true"] .nav-toggle__bars i:nth-child(3) { transform: translateY(-5.5px) rotate(-45deg); }

  /* The panel is positioned against the header, not the viewport: the header's
     backdrop-filter makes it the containing block for fixed descendants, so
     `position: fixed; inset: var(--header-h) 0 0` collapses to a ~1px strip and
     the links spill over the page. Absolute + an explicit height is stable
     whether or not the filter is honoured. */
  .nav.is-open {
    display: block;
    position: absolute;
    top: 100%;
    left: 0;
    right: 0;
    height: calc(100vh - var(--header-h));
    height: calc(100dvh - var(--header-h));
    z-index: 49;
    background: var(--ground);
    border-top: 1px solid var(--rule);
    padding: 0.5rem var(--gutter) calc(1.5rem + env(safe-area-inset-bottom));
    overflow-y: auto;
    overscroll-behavior: contain;
  }
  .nav.is-open .nav__list {
    flex-direction: column;
    align-items: stretch;
    gap: 0;
    width: 100%;
    max-width: 32rem;
    margin-inline: auto;
  }
  .nav.is-open .nav__list li { border-bottom: 1px solid var(--rule); }
  .nav.is-open .nav__list li:last-child { border-bottom: 0; padding-top: 1.5rem; }
  .nav.is-open .nav__list a {
    display: block;
    padding-block: 0.9375rem;
    font-size: var(--step-1);
    line-height: 1.3;
    color: var(--ink);
  }
  .nav.is-open .nav__list .btn {
    display: flex;
    width: 100%;
    padding-block: 0.875rem;
    font-size: var(--step-0);
  }
  /* Outranks the `.nav.is-open .nav__list a` colour above, which would
     otherwise paint ink-on-signal for the CTA. */
  .nav.is-open .nav__list .btn--primary,
  .nav.is-open .nav__list .btn--primary:hover { color: var(--ground); }

  body.nav-open { overflow: hidden; }

  .strip__list li { padding-inline: 0.7rem; }

  .capability { grid-template-columns: minmax(0, 1fr); gap: 0.5rem; }
  .capability__idx { padding-top: 0; }

  .fact { padding-right: 0; }

  .form__row { grid-template-columns: minmax(0, 1fr); }
  .form__foot .btn { width: 100%; }
  .site-footer__inner { flex-direction: column; gap: 1.75rem; }
}

@media (max-width: 26rem) {
  .hero__actions { display: grid; width: 100%; }
  .hero__actions .btn { width: 100%; }
}

@media (prefers-reduced-motion: reduce) {
  html { scroll-behavior: auto; }
  *, *::before, *::after {
    animation-duration: 1ms !important;
    animation-iteration-count: 1 !important;
    transition-duration: 1ms !important;
  }
  .reveal { opacity: 1; transform: none; }
  .detector rect.cell { opacity: var(--o, 1); animation: none; }
  /* The scene still renders under reduce; site.js just does not animate it, so
     what the reader gets is one still frame of a solid object, which has no
     motion in it to switch off. Only the stylesheet's own cell reveal is
     disabled above, and that runs on the schematic. */
}

/* ------------------------------------------------- capability statement */

.cs-bar {
  border-bottom: 1px solid var(--rule);
  background: var(--ground-2);
  padding-block: 0.875rem;
}
.cs-bar__inner {
  width: var(--shell-narrow);
  margin-inline: auto;
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 0.75rem 1.25rem;
}
.cs-bar__inner .brand { margin-right: auto; }

.cs-page { padding-block: clamp(2rem, 5vw, 3.5rem); }

.cs-sheet {
  width: var(--shell-narrow);
  margin-inline: auto;
  padding: clamp(1.75rem, 4vw, 3rem);
  background: var(--ground);
  border: 1px solid var(--rule);
  border-radius: 2px;
}

.cs-masthead {
  display: flex;
  flex-wrap: wrap;
  align-items: flex-end;
  justify-content: space-between;
  gap: 1rem 2rem;
  padding-bottom: 1.25rem;
  border-bottom: 2px solid var(--ink);
}
.cs-masthead img { width: 2.75rem; height: 2.75rem; }
.cs-masthead__id { display: flex; align-items: center; gap: 0.875rem; }
.cs-masthead h1 { font-size: var(--step-2); letter-spacing: -0.015em; }
.cs-masthead h1 span {
  display: block;
  font-family: var(--mono);
  font-size: var(--step--1);
  font-weight: 450;
  letter-spacing: 0.14em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-top: 0.25rem;
}
.cs-masthead__tag {
  font-size: var(--step--1);
  color: var(--ink-2);
  max-width: 30ch;
  text-align: right;
}

.cs-grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
  gap: 0 clamp(1.5rem, 4vw, 3rem);
  align-items: start;
}

.cs-block { padding-block: 1.375rem; border-bottom: 1px solid var(--rule); }
.cs-block:last-child { border-bottom: 0; }
.cs-block h2 {
  font-family: var(--mono);
  font-size: var(--step--1);
  font-weight: 500;
  letter-spacing: 0.1em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-bottom: 0.75rem;
}
.cs-block > p:not(.cs-inline) { font-size: var(--step--1); color: var(--ink-2); }

.cs-list li {
  display: grid;
  grid-template-columns: 0.9rem minmax(0, 1fr);
  gap: 0.5rem;
  padding-block: 0.25rem;
  font-size: var(--step--1);
  line-height: 1.5;
}
.cs-list li::before {
  content: "";
  width: 0.3125rem; height: 0.3125rem;
  margin-top: 0.5rem;
  background: var(--signal-mark);
}
.cs-list b { font-weight: 550; }

.cs-inline {
  display: flex;
  flex-wrap: wrap;
  gap: 0.3rem 0.75rem;
  font-family: var(--mono);
  font-size: 0.8125rem;
  color: var(--ink-2);
}

.cs-foot {
  margin-top: 1.25rem;
  padding-top: 1.125rem;
  border-top: 2px solid var(--ink);
  display: flex;
  flex-wrap: wrap;
  justify-content: space-between;
  align-items: flex-end;
  gap: 1rem;
  font-size: var(--step--1);
}
.cs-foot__note {
  font-family: var(--mono);
  font-size: 0.75rem;
  color: var(--ink-3);
  max-width: 58ch;
  line-height: 1.5;
}

@media (max-width: 48rem) {
  .cs-grid { grid-template-columns: minmax(0, 1fr); }
  .cs-masthead__tag { text-align: left; }
  .cs-sheet { border-inline: 0; border-radius: 0; padding-inline: 0; }
  .cs-bar__inner .btn { width: 100%; }
}

/* ------------------------------------------------------------ small pages */

.slim {
  min-height: calc(100dvh - var(--header-h));
  display: grid;
  align-content: center;
  padding-block: clamp(3rem, 10vw, 7rem);
}
.slim__inner { width: var(--shell-narrow); margin-inline: auto; display: grid; gap: 1.5rem; justify-items: start; }
.slim h1 { font-size: var(--step-3); }
.slim p { color: var(--ink-2); font-size: var(--step-1); line-height: 1.5; }
.slim__actions { display: flex; flex-wrap: wrap; gap: 0.75rem; margin-top: 0.5rem; }
