/* HOMNOX — homnox.com
   Hand-written CSS. No framework, no utility classes, no build step.

   The design language is the validated one: Urbanist (prose/UI), Lora
   (display), Space Mono (labels, indices, data); #0A0A0A / #FFFFFF / #0f744c
   / #C0C0C0 and their alphas are the entire palette. Boundaries are drawn only
   where they carry meaning: a rule above a list item, the footer's copyright
   rule, a focus ring. No boxes, no radius, no shadows, no decorative
   gradients. The single exception is the header: a translucent blurred
   surface so the menu stays legible over photography (.hdr::before).

   Page types:
     .s      full-viewport editorial section (home only)
     .phead  inner-page masthead
     .band   inner-page content band, light or dark
   Both use the same 12-column grid, the same rhythm unit and the same ink. */

/* ── Tokens ───────────────────────────────────────────────────────────── */

:root {
  --black: #0A0A0A;
  --white: #FFFFFF;
  --green: #0f744c;
  --silver: #C0C0C0;

  --font-body: 'Urbanist', system-ui, sans-serif;
  --font-heading: 'Lora', Georgia, serif;
  --font-mono: 'Space Mono', ui-monospace, monospace;

  --hdr: 4.5rem;
  --edge: clamp(1.25rem, 5vw, 5rem);
  --inset: max(var(--edge), calc((100vw - 1360px) / 2));
  --shell: calc(100% - 2 * var(--inset));
  --gut: clamp(1.25rem, 2vw, 2rem);

  /* Rhythm unit: 1, 1.5 and 2.5 of this are the only block gaps used. */
  --row: clamp(2.25rem, 4.8svh, 4.5rem);

  --fs-display: clamp(2.5rem, 3.5vw + 1.1rem, 5rem);
  --fs-h1: clamp(2.25rem, 3vw + 1rem, 4.25rem);
  --fs-h2: clamp(1.75rem, 1.9vw + .9rem, 3rem);
  --fs-h3: clamp(1.1875rem, .5vw + 1.05rem, 1.4375rem);
  --fs-lead: clamp(1.0625rem, .35vw + .95rem, 1.3125rem);
  --fs-body: clamp(1rem, .16vw + .95rem, 1.125rem);
  --fs-small: .8125rem;
  --fs-label: .6875rem;

  --lh-display: 1.03;
  --lh-h2: 1.1;
  --lh-lead: 1.52;
  --lh-body: 1.65;

  --e-out: cubic-bezier(.16, 1, .3, 1);
  --e-soft: cubic-bezier(.2, .8, .2, 1);

  /* Header chrome follows whichever themed section is in view. */
  --chrome-ink: var(--black);
  --ink-nav: rgba(10, 10, 10, .66);
  --logo-w: 0;
  --logo-g: 1;
  /* Glass under the menu. Alphas are set for legibility over photography,
     not for maximum transparency: the nav is 12px and sits over whatever the
     page happens to be showing. */
  --glass: rgba(255, 255, 255, .76);
  --glass-solid: rgba(255, 255, 255, .97);
  --glass-edge: rgba(10, 10, 10, .12);
}

html.on-dark {
  --chrome-ink: var(--white);
  --ink-nav: rgba(255, 255, 255, .78);
  --logo-w: 1;
  --logo-g: 0;
  --glass: rgba(10, 10, 10, .68);
  --glass-solid: rgba(10, 10, 10, .95);
  --glass-edge: rgba(255, 255, 255, .14);
}

/* Without JS the header cannot follow the section under it. Every page opens
   on a dark masthead, so that is the safe default rather than dark ink on a
   black band. */
html:not(.js) {
  --chrome-ink: var(--white);
  --ink-nav: rgba(255, 255, 255, .78);
  --logo-w: 1;
  --logo-g: 0;
  --glass: rgba(10, 10, 10, .68);
  --glass-solid: rgba(10, 10, 10, .95);
  --glass-edge: rgba(255, 255, 255, .14);
}

html.nav-open {
  --chrome-ink: var(--white);
  --ink-nav: rgba(255, 255, 255, .78);
  --logo-w: 1;
  --logo-g: 0;
  --glass: rgba(10, 10, 10, .68);
  --glass-solid: rgba(10, 10, 10, .95);
  --glass-edge: rgba(255, 255, 255, .14);
}

/* ── Base ─────────────────────────────────────────────────────────────── */

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

html {
  -webkit-text-size-adjust: 100%;
  scroll-behavior: smooth;
}

body {
  font-family: var(--font-body);
  font-size: var(--fs-body);
  font-weight: 400;
  line-height: var(--lh-body);
  background: var(--white);
  color: var(--black);
  -webkit-font-smoothing: antialiased;
  text-rendering: optimizeLegibility;
}

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

a {
  color: inherit;
  text-decoration: none;
}

button {
  font: inherit;
  color: inherit;
  background: none;
  border: 0;
}

ul,
ol {
  list-style: none;
}

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

::selection {
  background: var(--green);
  color: var(--white);
}

/* ── Type ─────────────────────────────────────────────────────────────── */

h1,
h2,
h3,
h4 {
  font-family: var(--font-heading);
  font-weight: 400;
  text-wrap: balance;
}

h1 {
  font-size: var(--fs-h1);
  line-height: var(--lh-display);
  letter-spacing: -.025em;
}

h2 {
  font-size: var(--fs-h2);
  line-height: var(--lh-h2);
  letter-spacing: -.015em;
}

h3 {
  font-size: var(--fs-h3);
  line-height: 1.3;
  letter-spacing: -.005em;
}

p {
  text-wrap: pretty;
}

.eyebrow {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  line-height: 1.4;
  letter-spacing: .16em;
  text-transform: uppercase;
  color: var(--ink-2, rgba(10, 10, 10, .62));
}

.lead,
.lede {
  font-size: var(--fs-lead);
  line-height: var(--lh-lead);
  letter-spacing: -.006em;
  color: var(--ink-2, rgba(10, 10, 10, .62));
  max-width: 56ch;
}

.mono {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: .14em;
  text-transform: uppercase;
}

.small {
  font-size: var(--fs-small);
  line-height: 1.55;
  color: var(--ink-2, rgba(10, 10, 10, .62));
}

.green {
  color: var(--green);
}

/* Inline links inside prose keep the rule as their affordance. */
.prose a,
.copy a {
  border-bottom: 1px solid var(--rule, rgba(10, 10, 10, .22));
  transition: border-color .18s linear;
}

.prose a:hover,
.copy a:hover {
  border-bottom-color: var(--green);
}

/* ── Controls ─────────────────────────────────────────────────────────── */

/* The filled control. Green is the only fill in the system; white text on
   #0f744c measures 6.07:1, so the primary action is AA at any size. */
.btn {
  display: inline-flex;
  align-items: center;
  justify-content: center;
  gap: .75rem;
  min-height: 3.25rem;
  padding: .5rem 1.9rem;
  background: var(--green);
  color: var(--white);
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  line-height: 1;
  letter-spacing: .14em;
  text-transform: uppercase;
  transition: background-color .18s linear, color .18s linear;
}

.btn:hover {
  background: var(--white);
  color: var(--black);
}

.btn--dark:hover {
  background: var(--black);
  color: var(--white);
}

/* The ruled text action. */
.more {
  display: inline-flex;
  align-items: center;
  gap: .6rem;
  padding-bottom: .55rem;
  border-bottom: 1px solid var(--rule, rgba(10, 10, 10, .22));
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--ink-2, rgba(10, 10, 10, .62));
  transition: color .18s linear, border-color .18s linear;
}

.more::after {
  content: "\2192";
}

.more:hover {
  color: var(--ink, currentColor);
  border-bottom-color: var(--green);
}

.actions {
  display: flex;
  flex-wrap: wrap;
  align-items: center;
  gap: 1.5rem 2rem;
}

/* ── Header ───────────────────────────────────────────────────────────── */

.hdr {
  position: fixed;
  inset: 0 0 auto;
  z-index: 30;
  height: var(--hdr);
  display: flex;
  align-items: center;
  gap: clamp(1rem, 1.8vw, 1.9rem);
  padding-inline: var(--inset);
  background: transparent;
  color: var(--ink-nav);
  transition: color .32s var(--e-soft);
}

/* A translucent surface under the menu. The blur lives on a pseudo-element
   rather than on .hdr itself: `backdrop-filter` makes an element the
   containing block for its fixed descendants, and the primary nav is a
   `position: fixed` child of the header. Putting the blur on the header would
   re-anchor the nav to a 4.5rem box and break the mobile panel.

   z-index 0 keeps the surface above the page and below the logo, nav and menu
   button, which are all positioned at 20 or 30. */
.hdr::before {
  content: "";
  position: fixed;
  inset: 0 0 auto;
  height: var(--hdr);
  z-index: 0;
  pointer-events: none;
  background: var(--glass);
  border-bottom: 1px solid var(--glass-edge);
  -webkit-backdrop-filter: blur(18px) saturate(160%);
  backdrop-filter: blur(18px) saturate(160%);
  transition: background-color .32s var(--e-soft), border-color .32s var(--e-soft);
}

/* No blur support: a solid surface rather than an unreadable transparent one. */
@supports not ((backdrop-filter: blur(1px)) or (-webkit-backdrop-filter: blur(1px))) {
  .hdr::before {
    background: var(--glass-solid);
  }
}

/* A reader who asked for less transparency gets the solid surface. */
@media (prefers-reduced-transparency: reduce) {
  .hdr::before {
    background: var(--glass-solid);
    -webkit-backdrop-filter: none;
    backdrop-filter: none;
  }
}

.logo {
  display: grid;
  flex: none;
}

.logo > img {
  grid-area: 1 / 1;
  width: clamp(7.5rem, 10vw, 9rem);
  height: auto;
}

.hdr .logo > img {
  transition: opacity .32s var(--e-soft);
}

.hdr .logo > img:first-child {
  opacity: var(--logo-w);
}

.hdr .logo > img:last-child {
  opacity: var(--logo-g);
}

/* The nav is nested inside the header so the wordmark and the menu button
   stay in the same row; both must paint above the open panel, which is a
   fixed child of the header and would otherwise cover them. */
.hdr > .logo,
.hdr > .menu {
  position: relative;
  z-index: 30;
}

.nav {
  position: fixed;
  z-index: 20;
  top: 0;
  right: var(--inset);
  height: var(--hdr);
  display: flex;
  align-items: center;
  margin-left: auto;
}

.nav > ul {
  display: flex;
  align-items: center;
  gap: clamp(.9rem, 1.7vw, 1.5rem);
}

.nav a {
  position: relative;
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: .12em;
  text-transform: uppercase;
  white-space: nowrap;
  transition: color .18s linear;
}

.nav a::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  bottom: -.55em;
  height: 1px;
  background: var(--green);
  transform: scaleX(0);
  transform-origin: left;
  transition: transform .18s linear;
}

.nav a:hover {
  color: var(--chrome-ink);
}

.nav a:hover::after,
.nav a:focus-visible::after,
.nav a[aria-current="page"]::after {
  transform: scaleX(1);
}

/* The index numerals exist only for the overlay panel. */
.nav .n {
  display: none;
}

/* The principal call to action in the header is the only filled text link. */
.nav .login {
  color: var(--chrome-ink);
}

.menu {
  display: none;
  width: 44px;
  height: 44px;
  cursor: pointer;
  margin-left: auto;
}

.menu > span {
  display: block;
  width: 20px;
  height: 1.5px;
  margin: 5px auto;
  background: currentColor;
  transition: transform .36s var(--e-out), opacity .2s linear;
}

/* ── Section index rail (home only) ───────────────────────────────────── */

.rail {
  position: fixed;
  z-index: 20;
  right: 1.5rem;
  top: 50%;
  transform: translateY(-50%);
  display: none;
  --r-ink: rgba(255, 255, 255, .78);
  --r-active: var(--white);
}

.rail a {
  position: relative;
  display: flex;
  align-items: center;
  justify-content: flex-end;
  gap: .5rem;
  padding: .62rem 0;
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  line-height: 1;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--r-ink);
  transition: color .4s var(--e-soft);
}

.rail a::after {
  content: "";
  width: 1.5rem;
  height: 2px;
  background: currentColor;
  transition: width .4s var(--e-out), background-color .4s var(--e-soft);
}

.rail a:hover {
  color: var(--r-active);
}

.rail a[aria-current="true"] {
  color: var(--r-active);
}

.rail a[aria-current="true"]::after {
  width: 3rem;
  background: var(--green);
}

.rail .t {
  position: absolute;
  right: calc(100% + .6rem);
  top: 50%;
  translate: 0 -50%;
  white-space: nowrap;
  opacity: 0;
  transform: translateX(.4rem);
  transition: opacity .3s var(--e-out), transform .3s var(--e-out);
}

.rail:hover .t,
.rail a:focus-visible .t {
  opacity: 1;
  transform: none;
}

html[data-rail="dark"] .rail {
  --r-ink: rgba(10, 10, 10, .7);
  --r-active: var(--black);
}

/* ── Themed surfaces ──────────────────────────────────────────────────── */

[data-theme="light"] {
  --ink: var(--black);
  --ink-2: rgba(10, 10, 10, .62);
  --rule: rgba(10, 10, 10, .16);
  --wash: rgba(10, 10, 10, .035);
  background-color: var(--white);
  color: var(--black);
}

[data-theme="dark"] {
  --ink: var(--white);
  --ink-2: rgba(255, 255, 255, .68);
  --rule: rgba(255, 255, 255, .16);
  --wash: rgba(255, 255, 255, .045);
  background-color: var(--black);
  color: var(--white);
}

/* ── Full-viewport section (home) ─────────────────────────────────────── */

.s {
  position: relative;
  display: grid;
  align-content: center;
  min-height: 100svh;
  padding: calc(var(--hdr) + var(--row)) var(--inset) calc(var(--row) * 1.25);
  overflow: clip;
  scroll-snap-align: start;
}

.s__in {
  position: relative;
  z-index: 2;
  display: grid;
  grid-template-columns: minmax(0, 1fr);
  row-gap: calc(var(--row) * .55);
}

/* Half-bleed media: sits in the section's own padding box, so `inset` reaches
   the viewport edge without a negative offset. */
.s__media {
  position: relative;
  z-index: 1;
  overflow: clip;
}

.head {
  display: grid;
  gap: 1.1rem;
  align-content: start;
}

.head h2 + .lead {
  margin-top: .35rem;
}

.s__media img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* ── Film ─────────────────────────────────────────────────────────────── */

/* The film is a slide: full frame, no type over it. The headline lives in
   .s--intro directly below, so the footage is never asked to carry words. */
.s--film {
  min-height: 100svh;
  padding: 0;
  align-content: stretch;
  background-color: var(--black);
  background-image: url('media/video/hero-poster-v1.jpg');
  /* The home film carries its own type at the lower left; anchor the crop to
     the left so that copy is never cut by a tall viewport. */
  background-position: left center;
  background-size: cover;
  background-repeat: no-repeat;
}

.s--film > video {
  position: absolute;
  left: 0;
  top: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: left center;
  z-index: 0;
}

/* Only two light touches: the header's legibility at the top, and a short
   fade into the statement section at the bottom. */
.s--film::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    linear-gradient(180deg, rgba(10, 10, 10, .5) 0%, rgba(10, 10, 10, 0) 18%),
    linear-gradient(0deg, rgba(10, 10, 10, .62) 0%, rgba(10, 10, 10, 0) 26%);
}

/* A hairline, not a label: the only affordance the slide needs. */
.film-cue {
  position: absolute;
  z-index: 3;
  left: 50%;
  bottom: clamp(1rem, 2.5vh, 2rem);
  translate: -50% 0;
  display: grid;
  place-items: center;
  width: 44px;
  height: 72px;
}

.film-cue > span {
  position: relative;
  display: block;
  width: 1px;
  height: 100%;
  overflow: hidden;
  background: rgba(255, 255, 255, .3);
}

.film-cue > span::after {
  content: "";
  position: absolute;
  left: 0;
  top: -40%;
  width: 2px;
  margin-left: -1px;
  height: 34%;
  background: var(--green);
  animation: cue 2.8s cubic-bezier(.5, 0, .5, 1) infinite;
}

@keyframes cue {
  to {
    top: 100%;
  }
}

/* ── The claim ────────────────────────────────────────────────────────── */

.s--intro {
  min-height: min(86svh, 56rem);
  align-content: center;
}

/* A shell-width hairline marks where the film ends and the argument begins.
   Without it the two black grounds read as one slide. */
.s--intro::before {
  content: "";
  position: absolute;
  top: 0;
  left: var(--inset);
  right: var(--inset);
  height: 1px;
  background: var(--rule);
}

.s--intro .s__in {
  max-width: 64rem;
  row-gap: calc(var(--row) * .7);
}

.s--intro h1 {
  font-size: var(--fs-display);
  max-width: 24ch;
}

.intro__sub {
  max-width: 46ch;
  font-size: var(--fs-lead);
  line-height: var(--lh-lead);
  color: var(--ink-2);
}

/* ── Numbered rows (failures, layers, principles) ─────────────────────── */

.rows {
  display: grid;
  gap: 0;
  counter-reset: row;
  border-top: 1px solid var(--rule);
}

.rows > li {
  position: relative;
  display: grid;
  gap: .4rem;
  padding: calc(var(--row) * .62) 0 calc(var(--row) * .62) 3.25rem;
  border-bottom: 1px solid var(--rule);
  counter-increment: row;
}

.rows > li::before {
  content: counter(row, decimal-leading-zero);
  position: absolute;
  left: 0;
  top: calc(var(--row) * .62);
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: .14em;
  color: var(--green);
}

.rows h3 {
  font-size: var(--fs-h3);
}

.rows p {
  color: var(--ink-2);
  max-width: 52ch;
}

/* ── Paths: the three filled calls to action ──────────────────────────── */

.paths {
  display: grid;
  gap: calc(var(--row) * .9) var(--gut);
}

.path {
  display: grid;
  /* The footer row is 1fr so the three filled buttons sit on one baseline
     even when the copy above them runs to different lengths. */
  grid-template-rows: auto auto 1fr auto;
  align-content: start;
  gap: .85rem;
  padding-top: 1.25rem;
  border-top: 1px solid var(--rule);
}

.path > .mono {
  color: var(--green);
}

.path > h3 {
  font-size: var(--fs-h3);
}

.path > p {
  color: var(--ink-2);
  font-size: var(--fs-body);
}

.path > .btn {
  justify-self: start;
  align-self: end;
  margin-top: .35rem;
}

/* ── Inner pages ──────────────────────────────────────────────────────── */

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

/* Masthead: dark, generous, one idea. Every page hero is a full screen —
   the same first impression as the home film — with the title pinned to the
   lower left. min-height, not height, so a long title can never be clipped. */
.phead {
  position: relative;
  display: grid;
  align-content: end;
  min-height: 100svh;
  padding: calc(var(--hdr) + var(--row) * 1.5) 0 calc(var(--row) * 1.15);
  overflow: clip;
}

.phead .wrap {
  display: grid;
  gap: calc(var(--row) * .5);
  max-width: 62rem;
}

.phead h1 {
  font-size: var(--fs-h1);
  max-width: 22ch;
}

.phead .lede {
  max-width: 52ch;
}

/* Inner-page masthead with a photograph behind it (developers). The scrim is
   the legibility contract: the title sits bottom-left over the brightest part
   of the frame. */
.phead__bg {
  position: absolute;
  inset: 0;
  z-index: 0;
  display: block;
}

.phead__bg img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  /* Each page sets --focus (desktop) and --focus-m (mobile) inline on the
     image; the mobile source is already cropped to a 4:5 window. */
  object-position: var(--focus-m, 50% 50%);
}

.phead--media::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 1;
  pointer-events: none;
  background:
    /* The nav sits on the top band; this keeps it legible over a bright sky. */
    linear-gradient(180deg, rgba(10, 10, 10, .9) 0%, rgba(10, 10, 10, .5) 9%, rgba(10, 10, 10, 0) 20%),
    linear-gradient(90deg, rgba(10, 10, 10, .9) 0%, rgba(10, 10, 10, .62) 40%, rgba(10, 10, 10, .18) 70%, rgba(10, 10, 10, .34) 100%),
    linear-gradient(0deg, rgba(10, 10, 10, .86) 0%, rgba(10, 10, 10, .12) 58%, rgba(10, 10, 10, 0) 100%);
}

.phead--media .wrap {
  position: relative;
  z-index: 2;
}

/* Content band. The reading measure never exceeds ~66 characters; media may
   run to the shell edge. */
.band {
  padding: calc(var(--row) * 1.4) 0;
}

.band--tight {
  padding: calc(var(--row) * .9) 0;
}

/* The closing ask on inner pages: a ruled band rather than a new page. */
.band--cta {
  border-top: 1px solid var(--rule);
  padding-block: calc(var(--row) * 1.6);
}

.band .wrap {
  display: grid;
  gap: calc(var(--row) * .75);
}

.cols {
  display: grid;
  gap: calc(var(--row) * .85) var(--gut);
}

.col {
  display: grid;
  align-content: start;
  gap: calc(var(--row) * .45);
  max-width: 66ch;
}

.col > h2 + p,
.col > h3 + p {
  margin-top: -.15rem;
}

.col p {
  color: var(--ink-2);
}

.col p.strong {
  color: var(--ink);
}

.sec-head {
  display: grid;
  gap: .85rem;
  max-width: 62ch;
}

/* ── Ordered steps ────────────────────────────────────────────────────── */

.steps {
  display: grid;
  counter-reset: step;
  border-top: 1px solid var(--rule);
}

.steps > li {
  position: relative;
  display: grid;
  gap: .35rem;
  padding: calc(var(--row) * .55) 0 calc(var(--row) * .55) 3.5rem;
  border-bottom: 1px solid var(--rule);
  counter-increment: step;
}

.steps > li::before {
  content: counter(step, decimal-leading-zero);
  position: absolute;
  left: 0;
  top: calc(var(--row) * .55);
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: .14em;
  color: var(--green);
}

.steps h3 {
  font-size: var(--fs-body);
  font-family: var(--font-body);
  font-weight: 600;
  letter-spacing: .01em;
}

.steps p {
  max-width: 62ch;
  color: var(--ink-2);
}

.step__text {
  display: grid;
  gap: .35rem;
}

.step__media {
  margin: .5rem 0 0;
}

.step__media picture,
.step__media img {
  display: block;
  width: 100%;
}

.step__media img {
  aspect-ratio: 4 / 3;
  object-fit: cover;
}

.program-media {
  margin: 0;
}

.program-media picture,
.program-media img {
  display: block;
  width: 100%;
}

.program-media img {
  aspect-ratio: 1721 / 914;
  object-fit: cover;
}

/* ── Bulleted ruled list ──────────────────────────────────────────────── */

.list {
  display: grid;
  border-top: 1px solid var(--rule);
}

.list > li {
  max-width: 78ch;
  padding: calc(var(--row) * .45) 0;
  border-bottom: 1px solid var(--rule);
  color: var(--ink-2);
}

.list--split {
  grid-template-columns: repeat(3, minmax(0, 1fr));
  column-gap: var(--gut);
  border-bottom: 1px solid var(--rule);
}

.list--split > li {
  max-width: none;
  border-bottom: 0;
}

.list--split strong {
  display: block;
  margin-bottom: .15rem;
}

.list strong {
  color: var(--ink);
  font-weight: 600;
}

/* ── Figures, images and placeholders ─────────────────────────────────── */

figure {
  display: grid;
  gap: .75rem;
  align-content: start;
}

figure img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

figcaption {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  line-height: 1.5;
  letter-spacing: .1em;
  text-transform: uppercase;
  color: var(--ink-2);
}

.frame {
  position: relative;
  overflow: clip;
  background: var(--wash);
}

.frame img {
  width: 100%;
  height: 100%;
  object-fit: cover;
}

/* Home: the failure and infrastructure photographs are cropped from the
   right, keeping their subject against the frame's outer edge. */
.s--failure figure img,
.s--infra .s__media img {
  object-position: right;
}

/* Image placeholder. The visible block is deliberately quiet: a hairline, a
   mono tag and an art-direction note. The full brief lives in the HTML
   comment above it so a photographer can work from the source. */
.ph {
  position: relative;
  display: grid;
  align-content: space-between;
  gap: 1.5rem;
  aspect-ratio: var(--ar, 4 / 3);
  min-height: 12rem;
  padding: clamp(1rem, 1.6vw, 1.5rem);
  border: 1px solid var(--rule);
  background:
    repeating-linear-gradient(135deg,
      transparent 0 14px,
      var(--wash) 14px 15px);
}

.ph__tag {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  line-height: 1;
  letter-spacing: .14em;
  text-transform: uppercase;
  color: var(--green);
}

.ph__note {
  align-self: end;
  max-width: 34ch;
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  line-height: 1.55;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink-2);
}

.ph__corner {
  position: absolute;
  right: clamp(1rem, 1.6vw, 1.5rem);
  top: clamp(1rem, 1.6vw, 1.5rem);
  width: 1.75rem;
  height: 1px;
  background: var(--green);
}

/* ── Callout / statement ──────────────────────────────────────────────── */

.statement {
  max-width: 24ch;
  font-family: var(--font-heading);
  font-size: clamp(1.75rem, 2.6vw + .6rem, 3.25rem);
  line-height: 1.14;
  letter-spacing: -.015em;
}

/* ── Team ─────────────────────────────────────────────────────────────── */

.roster {
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  gap: calc(var(--row) * .8) var(--gut);
}

.person {
  display: grid;
  gap: .8rem;
  align-content: start;
}

.person .frame {
  aspect-ratio: 4 / 5;
}

.person img {
  width: 100%;
  height: 100%;
  object-fit: cover;
  object-position: center top;
  /* The source portraits come from different sessions and backgrounds.
     Grayscale gives the grid one voice; colour returns on hover. */
  filter: grayscale(1);
  transition: transform .6s var(--e-out), filter .4s linear;
}

.person:hover img,
.person:focus-within img {
  transform: scale(1.02);
  filter: grayscale(0);
}

/* The name is the LinkedIn link; the portrait repeats it for the mouse but is
   removed from the tab order so a card is one stop, not two. */
.person h3 a {
  /* Inline, not flex: a long name must wrap normally and leave the arrow
     after the last word rather than stranded at the column edge. */
  display: inline;
  transition: color .18s linear;
}

.person h3 a:hover {
  color: var(--green);
}

.ext {
  margin-left: .3rem;
  font-family: var(--font-mono);
  font-size: .72em;
  line-height: 1;
  color: var(--green);
}

.person h3 {
  font-family: var(--font-body);
  font-size: var(--fs-body);
  font-weight: 600;
  letter-spacing: .01em;
}

.person p {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: .12em;
  text-transform: uppercase;
  color: var(--ink-2);
}

/* ── Address / channels ───────────────────────────────────────────────── */

.addr {
  font-style: normal;
  display: grid;
  gap: .15rem;
  color: var(--ink-2);
}

.addr strong {
  color: var(--ink);
  font-weight: 500;
}

.addr a {
  color: var(--ink);
  border-bottom: 1px solid var(--rule);
  justify-self: start;
}

.channels {
  display: grid;
  gap: 0 var(--gut);
}

.channel {
  display: grid;
  align-content: start;
  gap: .5rem;
  padding: calc(var(--row) * .7) 0;
  border-top: 1px solid var(--rule);
  border-bottom: 1px solid var(--rule);
}

.channel h2,
.channel h3 {
  font-family: var(--font-heading);
  font-size: var(--fs-h3);
}

.channel p {
  color: var(--ink-2);
  max-width: 46ch;
}

.channel .btn {
  justify-self: start;
  margin-top: .35rem;
}

/* ── Closing call to action ───────────────────────────────────────────── */

.cta {
  display: grid;
  gap: calc(var(--row) * .55);
  justify-items: start;
}

.cta h2 {
  max-width: 18ch;
}

.cta .lead {
  max-width: 44ch;
}

/* ── Footer ───────────────────────────────────────────────────────────── */

footer {
  background: var(--black);
  color: var(--white);
  padding: calc(var(--row) * 1.4) 0 calc(var(--row) * .75);
}

.foot {
  width: var(--shell);
  margin-inline: auto;
  display: grid;
  gap: calc(var(--row) * 1);
}

.foot__top {
  display: grid;
  gap: calc(var(--row) * .9) var(--gut);
}

.foot__brand {
  display: grid;
  gap: .9rem;
  align-content: start;
}

.foot__brand .logo > img {
  width: clamp(9rem, 13vw, 11rem);
}

.foot__brand > p {
  color: rgba(255, 255, 255, .6);
  max-width: 34ch;
}

.foot__col {
  display: grid;
  gap: .9rem;
  align-content: start;
}

.foot__col > h2 {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  font-weight: 400;
  line-height: 1.4;
  letter-spacing: .16em;
  text-transform: uppercase;
  /* Silver, not green: #0f744c on black is 3.42:1, under the 4.5:1 that
     11px text needs. */
  color: var(--silver);
}

.foot__col ul {
  display: grid;
  gap: .45rem;
}

.foot__col a {
  color: rgba(255, 255, 255, .66);
  transition: color .18s linear;
}

.foot__col a:hover {
  color: var(--white);
}

.foot__addr {
  font-style: normal;
  color: rgba(255, 255, 255, .6);
  font-size: var(--fs-small);
  line-height: 1.7;
}

.foot__addr a {
  color: rgba(255, 255, 255, .82);
}

.foot__bottom {
  display: grid;
  gap: .9rem;
  padding-top: 1.25rem;
  border-top: 1px solid rgba(255, 255, 255, .16);
}

.foot__legal {
  max-width: 78ch;
  font-size: .75rem;
  line-height: 1.6;
  color: rgba(255, 255, 255, .5);
}

.foot__copy {
  font-family: var(--font-mono);
  font-size: var(--fs-label);
  letter-spacing: .14em;
  text-transform: uppercase;
  color: rgba(255, 255, 255, .5);
}

/* On a dark ground the brand green measures 3.4:1 — under the 4.5:1 that
   11px text needs. The numerals and tags switch to silver there, exactly as
   the footer labels do; green stays on the rules and the fill, where the 3:1
   threshold applies. */
[data-theme="dark"] .rows > li::before,
[data-theme="dark"] .steps > li::before,
[data-theme="dark"] .path > .mono,
[data-theme="dark"] .ph__tag {
  color: var(--silver);
}

/* ── Reveal ───────────────────────────────────────────────────────────── */

/* Scoped to html.js so a visitor whose JS never runs gets every element
   visible. The observer adds .is-in once and unobserves. */
.js .rev {
  opacity: 0;
  transform: translateY(14px);
  transition: opacity .7s var(--e-out), transform .7s var(--e-out);
}

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

.js .rev:nth-child(2) {
  transition-delay: .06s;
}

.js .rev:nth-child(3) {
  transition-delay: .12s;
}

.js .rev:nth-child(4) {
  transition-delay: .18s;
}

.js .rev:nth-child(5) {
  transition-delay: .24s;
}

.js .rev:nth-child(6) {
  transition-delay: .3s;
}

/* ── Desktop composition ──────────────────────────────────────────────── */

@media (min-width: 900px) {
  .s__in {
    grid-template-columns: repeat(12, minmax(0, 1fr));
    column-gap: var(--gut);
  }

  .s__in > * {
    grid-column: 1 / 9;
  }

  .s--intro .s__in > * {
    grid-column: 1 / -1;
  }

  .s__in > .eyebrow {
    grid-column: 1 / 7;
  }

  .s__in > .lead {
    grid-column: 1 / 7;
  }

  .s__in > .rows {
    grid-column: 1 / 8;
  }

  .s__in > .paths {
    grid-column: 1 / -1;
  }

  /* Halves */
  .s--failure .s__in > .head,
  .s--demand .s__in > .head,
  .s--eco .s__in > .head {
    grid-column: 1 / 7;
  }

  .s--failure .s__in > .rows {
    grid-column: 1 / 7;
  }

  .s--failure .s__in > figure {
    grid-column: 8 / 13;
    grid-row: 1 / span 2;
    align-self: end;
  }

  .s--infra {
    align-content: center;
  }

  .s--infra .s__media {
    position: absolute;
    z-index: 1;
    top: 0;
    right: 0;
    bottom: 0;
    width: 46%;
  }

  .s--infra .s__media::after {
    content: "";
    position: absolute;
    inset: 0;
    background: linear-gradient(90deg, var(--black) 0%, rgba(10, 10, 10, .55) 34%, rgba(10, 10, 10, 0) 70%);
  }

  .s--infra .s__media .ph,
  .s--infra .s__media figure {
    height: 100%;
    border: 0;
  }

  .s--infra .s__in > * {
    grid-column: 1 / 7;
  }

  .s--demand .s__in > figure {
    grid-column: 8 / 13;
    align-self: end;
  }

  .s--eco .s__in > figure {
    grid-column: 8 / 13;
    grid-row: 1;
    align-self: end;
  }

  .s--eco .s__in > .list {
    grid-column: 1 / -1;
  }

  .s--eco .s__in > p:last-child {
    grid-column: 1 / 7;
  }

  /* Three paths in one row */
  .paths {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  .s--entry .s__in > .sec-head {
    grid-column: 1 / 8;
  }

  .s--close .s__in > * {
    grid-column: 3 / 11;
  }

  .s--close .s__in > .cta {
    grid-column: 1 / 9;
  }

  /* Inner pages */
  .cols {
    grid-template-columns: repeat(12, minmax(0, 1fr));
  }

  .col {
    grid-column: span 6;
  }

  .col--4 {
    grid-column: span 4;
  }

  .col--5 {
    grid-column: span 5;
  }

  .col--7 {
    grid-column: span 7;
  }

  .col--8 {
    grid-column: span 8;
  }

  .cols--text-media > .col:first-child {
    grid-column: 1 / 7;
  }

  .cols--text-media > .col:last-child {
    grid-column: 8 / 13;
  }

  /* Only media hangs from the bottom of the row. Text columns align to the
     top so a list's first rule never floats above the heading beside it. */
  .cols--text-media > .col:last-child:has(figure) {
    align-self: end;
  }

  /* The hero image is 16:9 and a desktop viewport is narrower, so cover crops
     the sides. Each page sets --focus to keep its annotation cards whole. */
  .phead__bg img {
    object-position: var(--focus, 50% 50%);
  }

  .cols--media-text > .col:first-child {
    grid-column: 1 / 6;
  }

  .cols--media-text > .col:last-child {
    grid-column: 7 / 13;
  }

  .roster {
    grid-template-columns: repeat(4, minmax(0, 1fr));
  }

  .channels {
    grid-template-columns: repeat(3, minmax(0, 1fr));
  }

  .foot__top {
    grid-template-columns: repeat(12, minmax(0, 1fr));
  }

  .foot__brand {
    grid-column: 1 / 5;
  }

  .foot__col:nth-of-type(1) {
    grid-column: 5 / 8;
  }

  .foot__col:nth-of-type(2) {
    grid-column: 8 / 11;
  }

  .foot__col:nth-of-type(3) {
    grid-column: 11 / 13;
  }

  .rail {
    display: block;
  }

  /* Developers steps: text and image share the row at desktop widths. Scoped
     to steps that carry media so the plain steps lists on funds and contact
     keep their single column. */
  .steps > li:has(.step__media) {
    grid-template-columns: minmax(0, 1fr) minmax(0, 1fr);
    gap: var(--gut);
    align-items: start;
  }

  .step__media {
    margin-top: 0;
  }

  .step__media img {
    aspect-ratio: 2 / 1;
  }
}

@media (min-width: 1200px) {
  :root {
    /* Reserve the rail's gutter so the fixed index never lands on media. */
    --inset: max(calc(var(--edge) + 6rem), calc((100vw - 1360px) / 2));
  }
}

/* Between the overlay breakpoint and the full desktop row the seven items
   would run into the wordmark. Drop the word "Institutional" from the login
   link and tighten the tracking rather than move a link into a submenu. */
@media (min-width: 900px) and (max-width: 1100px) {
  .nav > ul {
    gap: .8rem;
  }

  .nav a {
    font-size: .625rem;
    letter-spacing: .1em;
  }

  .nav .login-long {
    display: none;
  }
}

/* ── Narrow ───────────────────────────────────────────────────────────── */

@media (max-width: 899px) {
  .nav {
    position: fixed;
    inset: 0;
    z-index: 25;
    height: auto;
    margin: 0;
    background: var(--black);
    display: grid;
    align-content: center;
    padding-inline: var(--inset);
    clip-path: inset(0 0 100% 0);
    /* Hidden from the tab order while closed; the clip-path alone would leave
       six off-screen links focusable. */
    visibility: hidden;
    transition: clip-path .52s var(--e-out), visibility 0s linear .52s;
  }

  .nav > ul {
    flex-direction: column;
    align-items: flex-start;
    gap: 1.05rem;
  }

  .nav a {
    font-family: var(--font-heading);
    font-size: clamp(1.6rem, 6vw, 2.25rem);
    line-height: 1.25;
    letter-spacing: 0;
    text-transform: none;
    color: var(--white);
  }

  .nav a::after {
    display: none;
  }

  .nav .n {
    display: inline;
    margin-right: .9rem;
    font-family: var(--font-mono);
    font-size: var(--fs-label);
    letter-spacing: .16em;
    vertical-align: middle;
    color: var(--silver);
  }

  .nav .login {
    /* Large text in the panel, so the brand green clears 3:1 here. */
    color: var(--green);
  }

  html.nav-open .nav {
    clip-path: inset(0 0 0 0);
    visibility: visible;
    transition-delay: 0s;
  }

  html.nav-open body {
    overflow: hidden;
  }

  .menu {
    display: block;
  }

  html.nav-open .menu > span:first-child {
    transform: translateY(3.25px) rotate(45deg);
  }

  html.nav-open .menu > span:last-child {
    transform: translateY(-3.25px) rotate(-45deg);
  }

  .s {
    min-height: auto;
    padding-block: calc(var(--hdr) + 3rem) 3.5rem;
    align-content: start;
  }

  /* Full height here is corrected for portrait viewports below, where a
     16:9 film cannot fill the screen without losing most of its width. */
  .s--film {
    /* The header is fixed and the film is the first section on the home page.
       On mobile, start the film below the header so the opening frame is not
       covered by the menu. */
    margin-top: var(--hdr);
    min-height: calc(100svh - var(--hdr));
    padding: 0;
  }

  .s--intro {
    min-height: auto;
  }

  .s__in {
    gap: 0;
  }

  .s--infra .s__media {
    margin-top: var(--row);
  }

  .paths {
    grid-template-columns: minmax(0, 1fr);
  }

  .roster {
    grid-template-columns: repeat(2, minmax(0, 1fr));
  }

  .phead {
    padding-block: calc(var(--hdr) + 5rem) 3.5rem;
  }
}

@media (max-width: 560px) {
  :root {
    --edge: 1.25rem;
  }

  .person .frame {
    aspect-ratio: 1 / 1;
  }

  .foot__top {
    gap: calc(var(--row) * .8);
  }
}

/* ── The film in portrait ─────────────────────────────────────────────── */

/* The film is 16:9 and its typography is burned into the left half of the
   frame. In a portrait viewport a full-height section is far taller than 16:9,
   so `cover` scales the frame up to that height and discards most of its
   width: at 390x844 only 26% of the frame is visible, which cuts the film's
   own copy mid-sentence, and at 320x568 only 32%.

   In portrait the film sets the height instead. It scales to the screen width
   and the section follows, so the whole frame is shown. Landscape is left
   alone: there the crop is mild (90% of the frame at 1440x900, 100% at
   844x390) and the full-screen treatment still works. */
@media (orientation: portrait) {
  .s--film {
    min-height: 0;
    aspect-ratio: 16 / 9;
  }
}

@media (prefers-reduced-motion: reduce) {

  html {
    scroll-behavior: auto;
  }

  *,
  *::before,
  *::after {
    animation-duration: .001ms !important;
    transition-duration: .001ms !important;
  }

  .js .rev {
    opacity: 1;
    transform: none;
  }

  .s--film > video {
    display: none;
  }

  .film-cue > span::after {
    animation: none;
    top: 0;
  }
}

/* ── Print ────────────────────────────────────────────────────────────── */

/* The header and the section rail are both `position: fixed`, so without this
   they repeat on every printed page — and the header now carries a glass
   surface, which would print as a solid tinted band across the top of each
   one. Chrome is screen furniture. */
@media print {
  .hdr,
  .rail {
    display: none;
  }
}
