/* =====================================================================
   NORTHERN EDGE — layout
   Shell, container, bleeds, section rhythm, surface contexts, the
   diagonal cut and the grain overlay.
   Section vertical rhythm is set ONCE on .ne-section. Modifiers only.
   ===================================================================== */

.ne-shell {
  min-height: 100vh;
  display: flex;
  flex-direction: column;
  /* clip, not hidden: hidden on an ancestor breaks position: sticky inside. */
  overflow-x: clip;
}

.ne-main {
  flex: 1 1 auto;
}

/* Clear the sticky call bar so it never covers the footer. */
@media (max-width: 991.98px) {
  body {
    padding-bottom: calc(var(--ne-callbar-h) + env(safe-area-inset-bottom));
  }
}

/* ------------------------------------------------------------ CONTAINER */
.ne-wrap {
  width: 100%;
  max-width: var(--ne-wrap);
  margin-inline: auto;
  padding-left: var(--ne-gut-l);
  padding-right: var(--ne-gut);
}
.ne-wrap--narrow {
  max-width: 880px;
}
/* Wide container for the bento and the fleet, which want more room than
   reading measure allows. */
.ne-wrap--wide {
  max-width: 1600px;
}

/* Leaves room for the fixed side rail so full-width rows do not run under
   it on desktop. The rail is decorative, but text sliding beneath it looks
   like a mistake. */
@media (min-width: 1200px) {
  :root {
    --ne-gut-l: calc(var(--ne-rail-w) + clamp(20px, 2vw, 40px));
  }
}

/* --------------------------------------------------------------- BLEEDS
   Three sections break the container on purpose. Without this the page has
   no dynamics — every band the same width is what made the old build read
   as a template. */
.ne-bleed-r {
  margin-right: calc(50% - 50vw);
}
.ne-bleed-l {
  margin-left: calc(50% - 50vw);
}
.ne-bleed {
  margin-inline: calc(50% - 50vw);
  width: 100vw;
}

/* ---------------------------------------------------- SECTION RHYTHM */
.ne-section {
  position: relative;
  padding-block: var(--ne-section-y);
}
.ne-section--tight {
  padding-block: clamp(48px, 5vw, 88px);
}
.ne-section--flush {
  padding-block: 0;
}
/* Extra room under a section that closes a chapter of the page. */
.ne-section--tail {
  padding-bottom: clamp(112px, 13vw, 200px);
}

/* The head stack is fixed, so a page that opens on a plain section rather
   than on a hero has to clear it itself. Without this the first heading on
   the contact, 404 and legal pages sits under the bar on a phone, where
   --ne-section-y is smaller than the stack is tall. */
.ne-main > .ne-section:first-child {
  padding-top: calc(
    var(--ne-hazard-h) + var(--ne-header-h) + clamp(48px, 6vw, 96px)
  );
}
@media (min-width: 992px) {
  .ne-main > .ne-section:first-child {
    padding-top: calc(
      var(--ne-hazard-h) + var(--ne-topbar-h) + var(--ne-header-h) +
        clamp(48px, 6vw, 96px)
    );
  }
}

/* ------------------------------------------------- SURFACE CONTEXTS
   Every section carries exactly one surface class. A section with no
   surface class is a bug: its text colour would fall back to the body
   and can land dark-on-dark.

   Each one rebinds the four contract properties, so everything inside
   inherits the right foreground, hairline and accent without a single
   per-surface override further down the cascade. */
.ne-section--ink,
.ne-section--dark {
  background-color: var(--ne-ink);
  --ne-fg: var(--ne-white);
  --ne-fg-soft: var(--ne-mute);
  --ne-rule: var(--ne-steel);
  --ne-accent: var(--ne-amber);
  color: var(--ne-fg);
}
.ne-section--graphite {
  background-color: var(--ne-graphite);
  --ne-fg: var(--ne-white);
  --ne-fg-soft: var(--ne-mute);
  --ne-rule: var(--ne-steel);
  --ne-accent: var(--ne-amber);
  color: var(--ne-fg);
}
.ne-section--light {
  background-color: var(--ne-concrete);
  --ne-fg: var(--ne-ink);
  --ne-fg-soft: var(--ne-mute-ink);
  --ne-rule: color-mix(in srgb, var(--ne-ink) 14%, transparent);
  /* Amber fails contrast on concrete; the deep tone passes. */
  --ne-accent: var(--ne-amber-deep);
  color: var(--ne-fg);
}
.ne-section--amber {
  background-color: var(--ne-amber);
  /* All text on amber is ink, and that includes the accent. White on amber
     measures 2.03:1 — it fails at any size — so the split-heading pattern is
     carried here by an ink underline instead of a colour change. The accent
     token pointed at white before, which put the CTA band's accent phrase
     and every eyebrow on this surface below the floor. */
  --ne-fg: var(--ne-ink);
  --ne-fg-soft: color-mix(in srgb, var(--ne-ink) 72%, transparent);
  --ne-rule: color-mix(in srgb, var(--ne-ink) 22%, transparent);
  --ne-accent: var(--ne-ink);
  color: var(--ne-fg);
}
/* Text sits over photography. The scrim inside does the contrast work, so
   the contract is fixed to the dark reading regardless of the frame. */
.ne-section--media {
  background-color: var(--ne-ink);
  --ne-fg: var(--ne-white);
  --ne-fg-soft: color-mix(in srgb, var(--ne-white) 74%, transparent);
  --ne-rule: color-mix(in srgb, var(--ne-white) 22%, transparent);
  --ne-accent: var(--ne-amber);
  color: var(--ne-fg);
  overflow: hidden;
  isolation: isolate;
}

/* ------------------------------------------------------- GRAIN OVERLAY
   The difference between flat black and photographed black. Applied to the
   dark surfaces only — on concrete it would read as dirt. Never intercepts
   pointer events, and sits under the content because it is texture, not a
   veil. */
.ne-section--ink::after,
.ne-section--dark::after,
.ne-section--graphite::after,
.ne-section--media::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: 0;
  background-image: var(--ne-noise);
  background-repeat: repeat;
  opacity: var(--ne-noise-opacity);
  pointer-events: none;
}
/* Content sits above the grain. */
.ne-section > * {
  position: relative;
  z-index: 1;
}

/* ------------------------------------------------- THE DIAGONAL CUT
   Signature device, lifted from the business card. Same shallow angle
   every time, alternating direction down the page, with the amber rule
   sitting on the seam. animations.js draws that rule in on scroll. */
.ne-cut-tl,
.ne-cut-tr {
  margin-top: calc(var(--ne-cut) * -1);
  padding-top: calc(var(--ne-section-y) + var(--ne-cut));
  z-index: 1;
}
.ne-cut-tl {
  clip-path: polygon(0 0, 100% var(--ne-cut), 100% 100%, 0 100%);
}
.ne-cut-tr {
  clip-path: polygon(0 var(--ne-cut), 100% 0, 100% 100%, 0 100%);
}
.ne-cut-tl > .ne-seam,
.ne-cut-tr > .ne-seam {
  content: "";
  position: absolute;
  inset: 0 0 auto 0;
  height: calc(var(--ne-cut) + 4px);
  background-color: var(--ne-amber);
  pointer-events: none;
  transform-origin: left center;
  z-index: 2;
}
.ne-cut-tl > .ne-seam {
  clip-path: polygon(0 0, 100% var(--ne-cut), 100% calc(var(--ne-cut) + 4px), 0 4px);
}
.ne-cut-tr > .ne-seam {
  clip-path: polygon(0 var(--ne-cut), 100% 0, 100% 4px, 0 calc(var(--ne-cut) + 4px));
}

/* A tight section that also carries a cut needs its own top padding sum. */
.ne-section--tight.ne-cut-tl,
.ne-section--tight.ne-cut-tr {
  padding-top: calc(clamp(48px, 5vw, 88px) + var(--ne-cut));
}

/* --------------------------------------------------------- SPLIT GRIDS */
.ne-split {
  display: grid;
  gap: var(--ne-space-8);
  align-items: start;
}
/* Grid tracks take an auto minimum, which means a column refuses to shrink
   below its widest unbreakable content and pushes the whole grid past the
   container instead. min-width: 0 is what lets the column give, and the
   wrap is what stops a long word doing it anyway. Without this the process
   steps on the homepage escaped their column. */
.ne-split > * {
  min-width: 0;
  max-width: 100%;
  overflow-wrap: break-word;
}
@media (min-width: 992px) {
  .ne-split {
    grid-template-columns: 5fr 7fr;
    gap: clamp(40px, 6vw, 96px);
  }
  .ne-split--7-5 {
    grid-template-columns: 7fr 5fr;
  }
  .ne-split--half {
    grid-template-columns: 1fr 1fr;
  }
  .ne-split__sticky {
    position: sticky;
    top: calc(var(--ne-header-h-docked) + 48px);
  }
}

/* ------------------------------------------------------- HAZARD RULE
   Appearance one of three: the strip above the header. The other two are
   the loader's progress fill and the rule closing the footer. */
.ne-hazard {
  height: var(--ne-hazard-h);
  background-image: var(--ne-hazard);
  opacity: 0.85;
}
