/* ============================================================
   OFF THE GROUND — madebyotg
   Design system: warm-paper editorial (Exo Ape) ·
   alternating light/dark rooms (Aspelin Reitan) ·
   black cinema for motion (Stink Studios) ·
   single red flare accent (brand)
   ============================================================ */

:root {
  /* color */
  --paper:    #f6f5f2;   /* bone canvas */
  --paper-2:  #edebe5;   /* deeper bone band */
  --ink:      #2e3033;   /* primary text */
  --steel:    #54565a;   /* brand gray — secondary text */
  --hairline: #dcd8d0;   /* warm rules & borders */
  --red:      #e63329;   /* the flare — use sparingly */
  --void:     #0b0b0c;   /* cinema room */
  --void-2:   #131315;
  --white:    #ffffff;

  /* type */
  --serif: "Cormorant Garamond", "Times New Roman", serif;
  --sans:  "Jost", "Helvetica Neue", Arial, sans-serif;

  /* rhythm */
  --gutter: clamp(20px, 5vw, 96px);
  --section: clamp(110px, 16vh, 220px);
}

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

html { scroll-behavior: smooth; }

body {
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-weight: 300;
  font-size: 16px;
  line-height: 1.7;
  -webkit-font-smoothing: antialiased;
  overflow-x: hidden;
}

/* A width/height pair in the markup is a presentational hint, and
   wherever the CSS sizes an image by width alone the hint wins — the
   image renders at its attribute height instead of scaling. That is what
   turned the drone mark in the header into a red smear.

   This rule fixes it, but it can only fix it for a browser holding this
   stylesheet, and a browser can hold an old one for a long time. So the
   handful of images the CSS sizes by width alone carry no height
   attribute either: correct markup and correct CSS, so neither one
   depends on the other having arrived. Any rule that really wants a
   height still overrides this. */
img { display: block; max-width: 100%; height: auto; }

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

em { font-style: italic; font-weight: 300; }

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

/* ---------- reveal animation ---------- */
.reveal {
  opacity: 0;
  transform: translateY(26px);
  transition: opacity 1s cubic-bezier(.22,.61,.36,1), transform 1s cubic-bezier(.22,.61,.36,1);
  transition-delay: var(--d, 0s);
}
.reveal.in { opacity: 1; transform: none; }
@media (prefers-reduced-motion: reduce) {
  .reveal { opacity: 1; transform: none; transition: none; }
  html { scroll-behavior: auto; }
}

/* ---------- shared section furniture ---------- */
.section-kicker {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: .42em;
  text-transform: uppercase;
  color: var(--steel);
  margin-bottom: 28px;
}
.section-kicker::before {
  content: "";
  display: inline-block;
  width: 34px;
  height: 1px;
  background: var(--red);
  vertical-align: middle;
  margin-right: 16px;
}
.section-kicker--light { color: #9a9a9e; }

.section-title {
  font-family: var(--serif);
  font-weight: 300;
  font-size: clamp(40px, 5.4vw, 84px);
  line-height: 1.04;
  letter-spacing: .01em;
}

/* ============================================================
   NAV
   ============================================================ */
.nav {
  position: fixed;
  inset: 0 0 auto 0;
  z-index: 100;
  display: flex;
  align-items: center;
  justify-content: space-between;
  padding: 22px var(--gutter);
  transition: background .5s ease, box-shadow .5s ease, padding .5s ease;
}
.nav.scrolled {
  background: color-mix(in srgb, var(--paper) 92%, transparent);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  padding-top: 14px;
  padding-bottom: 14px;
  box-shadow: 0 1px 0 var(--hairline);
}

.nav__brand { display: flex; align-items: center; gap: 12px; }
.nav__mark { width: 34px; }
.nav__word {
  font-family: var(--serif);
  font-weight: 500;
  font-size: 17px;
  letter-spacing: .3em;
  text-transform: uppercase;
  color: var(--steel);
}

.nav__links { display: flex; align-items: center; gap: 34px; }
.nav__links a {
  font-size: 12px;
  font-weight: 500;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--steel);
  position: relative;
  transition: color .3s ease;
}
.nav__links a:hover { color: var(--ink); }
.nav__links a:not(.nav__cta)::after {
  content: "";
  position: absolute;
  left: 0; bottom: -6px;
  width: 100%; height: 1px;
  background: var(--red);
  transform: scaleX(0);
  transform-origin: right;
  transition: transform .4s cubic-bezier(.22,.61,.36,1);
}
.nav__links a:not(.nav__cta):hover::after { transform: scaleX(1); transform-origin: left; }

.nav__cta {
  border: 1px solid var(--hairline);
  padding: 10px 22px;
  border-radius: 999px;
  white-space: nowrap;
  flex-shrink: 0;
  transition: border-color .3s ease, color .3s ease;
}
.nav__cta:hover { border-color: var(--red); color: var(--red) !important; }

/* Call us — shown only where there is a handset to dial it with.
   A tel: link on a desktop browser is, for most people, a dead end or a
   dialog about which app should handle it; on a phone it is the shortest
   path from this page to a conversation. The menu is already open under
   a thumb when it appears, so it sits with the other actions rather than
   floating over the page as a bubble. */
.nav__call { display: none; }

@media (hover: none) and (max-width: 860px) {
  .nav__call {
    display: inline-block;
    color: var(--red) !important;
    white-space: nowrap;
  }
}

@media (max-width: 1080px) {
  .nav { gap: 16px; }
  .nav__brand { min-width: 0; flex-shrink: 1; }
  .nav__mark { width: 28px; flex-shrink: 0; }
  .nav__word {
    font-size: 13px;
    letter-spacing: .14em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
  .nav__links { gap: 18px; }
  .nav__links a { font-size: 11px; letter-spacing: .1em; }
  .nav__links a.nav__cta { padding: 11px 14px; }
}

/* --- the menu button, phones and small tablets only -------------------- */
.nav__toggle {
  display: none;
  width: 48px;
  height: 48px;
  flex-shrink: 0;
  background: none;
  border: 0;
  cursor: pointer;
  position: relative;
  z-index: 2;
  color: var(--ink);
}
.nav--over .nav__toggle { color: var(--paper); }
.nav.is-open .nav__toggle { color: var(--ink); }

/* three rules that become a cross */
.nav__bars,
.nav__bars::before,
.nav__bars::after {
  position: absolute;
  left: 50%;
  width: 22px;
  height: 1.5px;
  background: currentColor;
  transition: transform .38s cubic-bezier(.22,.61,.36,1), opacity .2s ease;
}
.nav__bars { top: 50%; transform: translate(-50%, -50%); }
.nav__bars::before, .nav__bars::after { content: ""; left: 0; }
.nav__bars::before { transform: translateY(-7px); }
.nav__bars::after  { transform: translateY(7px); }

.nav.is-open .nav__bars { transform: translate(-50%, -50%) rotate(45deg); }
.nav.is-open .nav__bars::before { transform: translateY(0) rotate(-90deg); }
.nav.is-open .nav__bars::after  { opacity: 0; }

body.nav-open { overflow: hidden; }

@media (max-width: 760px) {
  .nav__toggle { display: block; }
  .nav__word { font-size: 13px; letter-spacing: .14em; }
  .nav__mark { width: 28px; }
  .nav__brand { gap: 10px; }

  /* The links become a full-screen panel rather than disappearing. Phones
     previously had no navigation at all: the bar carried a logo and one
     button, and every section and service page was unreachable. */
  .nav__links {
    position: fixed;
    inset: 0;
    z-index: 1;
    display: flex;
    flex-direction: column;
    align-items: flex-start;
    justify-content: center;
    gap: 0;
    padding: 88px var(--gutter) 40px;
    background: var(--paper);
    opacity: 0;
    visibility: hidden;
    transform: translateY(-12px);
    transition: opacity .34s ease, transform .38s cubic-bezier(.22,.61,.36,1), visibility .34s;
  }
  .nav.is-open .nav__links { opacity: 1; visibility: visible; transform: none; }

  .nav__links a {
    font-family: var(--serif);
    font-size: clamp(28px, 8vw, 40px);
    font-weight: 300;
    letter-spacing: .01em;
    text-transform: none;
    color: var(--ink);
    width: 100%;
    padding: 14px 0;
    border-bottom: 1px solid var(--hairline);
  }
  /* the animated underline is a pointer affordance; it has no meaning here */
  .nav__links a:not(.nav__cta)::after { display: none; }

  /* The two actions at the foot of the menu are a pair and are built as
     one: same pill, same tracked sans, side by side. Left to inherit the
     menu's 40px serif, the call button came out twice the size of the
     CTA beside it and read as a mistake. */
  .nav__links a.nav__cta,
  .nav__links a.nav__call {
    align-self: flex-start;
    width: auto;
    border: 1px solid var(--ink);
    border-radius: 999px;
    padding: 16px 30px;
    font-family: var(--sans);
    font-size: 12px;
    letter-spacing: .2em;
    text-transform: uppercase;
    border-bottom: 1px solid var(--ink);
  }

  .nav__links a.nav__call {
    margin-top: 28px;
    border-color: var(--red);
  }

  /* Calling is the shorter path, so it leads; the form keeps its own
     weight rather than competing for it. */
  .nav__links a.nav__cta { margin-top: 12px; }

  /* the bar keeps its own ground while the panel is open, and the brand
     stays above it so the header still reads as a header */
  .nav.is-open {
    background: transparent;
    box-shadow: none;
    /* This is not cosmetic. A backdrop-filter makes an element the
       containing block for its position: fixed descendants, exactly as a
       transform does — so once .scrolled applied the blur, the panel's
       inset: 0 resolved against this 128px bar instead of the viewport.
       Scroll down, open the menu, and the links spilled out of a strip
       across the top of the page with the content showing through. It
       looked fine at the top of the page, which is the only place it was
       ever tested. */
    backdrop-filter: none;
    -webkit-backdrop-filter: none;
  }
  .nav__brand { position: relative; z-index: 2; }
  .nav.is-open .nav__word { color: var(--ink); }
}

/* Narrow phones, one block.
   Two solutions to this landed here from parallel work and fought each other:
   at 375px both applied, and which declaration won came down to a mix of
   source order and selector specificity, so the bar ended up with one
   branch's mark size and the other's padding.

   This keeps the mechanical half, which is the robust one: the brand may
   shrink and truncate, the button may not. Sizes are then only a floor
   rather than the thing holding the layout together. Measured to fit at
   344px, the narrowest phone in use. */
@media (max-width: 520px) {
  .nav { gap: 12px; padding-left: 18px; padding-right: 18px; }
  .nav__brand { min-width: 0; flex-shrink: 1; }
  .nav__mark { width: 26px; flex-shrink: 0; }
  .nav__word {
    font-size: 12px;
    letter-spacing: .1em;
    white-space: nowrap;
    overflow: hidden;
    text-overflow: ellipsis;
  }
}

/* ============================================================
   HERO
   ============================================================ */
.hero {
  min-height: 100svh;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  text-align: center;
  padding: 120px var(--gutter) 80px;
  position: relative;
  isolation: isolate;
}

.hero__eyebrow {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: .48em;
  text-transform: uppercase;
  color: var(--steel);
  margin-bottom: clamp(28px, 5vh, 56px);
}

.hero__mark {
  width: clamp(110px, 13vw, 200px);
  margin-bottom: clamp(14px, 3vh, 34px);
}

/* ---------- the drone, in flight ----------
   Three layers so the craft can behave like a craft: the body banks,
   the rotors blur, and a shadow on the ground reacts to altitude.  */
.drone {
  position: relative;
  width: 100%;
  aspect-ratio: 1200 / 173;
  transform-origin: 50% 60%;
  animation:
    hover-bob   4.6s ease-in-out infinite,
    hover-drift 11s  ease-in-out infinite;
}

.drone__rotors,
.drone__body {
  position: absolute;
  left: 0;
  width: 100%;
  /* The markup carries width and height so the browser can reserve the
     box before the file lands. Those attributes are a presentational
     hint, and with no height declared here the hint wins: the layers
     rendered at a fixed 61px and 112px instead of scaling, and the mark
     came apart. */
  height: auto;
}
/* rotors occupy the top 35.3% of the mark, body the rest — same split as the source */
.drone__rotors { top: 0; }
.drone__body   { top: 35.3%; }

/* the illusion of spin: a fast horizontal squash, too quick to read as motion */
.drone__rotors {
  transform-origin: 50% 50%;
  animation: rotor-spin .16s linear infinite;
}

/* the craft banks left and right as it holds position */
.drone__body {
  transform-origin: 50% 30%;
  animation: bank 6.8s ease-in-out infinite;
}

/* ground shadow: tightens and darkens as the drone drops, spreads as it rises */
.drone__shadow {
  position: absolute;
  left: 50%;
  bottom: -46%;
  width: 34%;
  height: 7%;
  transform: translateX(-50%);
  border-radius: 50%;
  background: radial-gradient(closest-side, rgba(46,48,51,.30), rgba(46,48,51,0) 78%);
  animation: shadow-breathe 4.6s ease-in-out infinite;
}

@keyframes hover-bob {
  0%, 100% { transform: translateY(0); }
  50%      { transform: translateY(-13px); }
}
@keyframes hover-drift {
  0%, 100% { margin-left: 0; }
  33%      { margin-left: 9px; }
  66%      { margin-left: -7px; }
}
@keyframes rotor-spin {
  0%, 100% { transform: scaleX(1)   scaleY(1); }
  50%      { transform: scaleX(.93) scaleY(.86); }
}
@keyframes bank {
  0%, 100% { transform: rotate(0deg); }
  28%      { transform: rotate(1.6deg); }
  62%      { transform: rotate(-1.9deg); }
}
@keyframes shadow-breathe {
  0%, 100% { opacity: .5;  transform: translateX(-50%) scale(1); }
  50%      { opacity: .26; transform: translateX(-50%) scale(1.22); }
}

@media (prefers-reduced-motion: reduce) {
  .drone,
  .drone__rotors,
  .drone__body,
  .drone__shadow { animation: none; }
  .drone__shadow { opacity: .35; }
}

.hero__title {
  font-family: var(--serif);
  font-weight: 300;
  font-size: clamp(64px, 13.5vw, 224px);
  line-height: .96;
  letter-spacing: .06em;
  text-transform: uppercase;
  color: var(--ink);
}

.hero__tag {
  margin-top: clamp(24px, 4vh, 48px);
  font-size: clamp(11px, 1.2vw, 14px);
  font-weight: 500;
  letter-spacing: .52em;
  text-transform: uppercase;
  color: var(--steel);
}

/* The visible mark is a 1px line, which is not something a thumb can hit, so
   the box is a real 44px target and the line is drawn down the middle of it.
   overflow:hidden has to stay: it is what clips the dropping line. */
.hero__scroll {
  position: absolute;
  bottom: 36px;
  left: 50%;
  transform: translateX(-50%);
  width: 44px;
  height: 64px;
  overflow: hidden;
}
.hero__scroll span {
  position: absolute;
  top: 0;
  bottom: 0;
  left: 50%;
  width: 1px;
  margin-left: -0.5px;
  background: var(--steel);
  animation: drop 2.2s cubic-bezier(.65,0,.35,1) infinite;
}
@keyframes drop {
  0%   { transform: translateY(-100%); }
  55%  { transform: translateY(0); }
  100% { transform: translateY(100%); }
}

/* ============================================================
   INTRO
   ============================================================ */
.intro {
  padding: var(--section) var(--gutter);
  max-width: 1200px;
  margin: 0 auto;
}
.intro__kicker {
  font-size: 11px;
  font-weight: 500;
  letter-spacing: .42em;
  text-transform: uppercase;
  color: var(--steel);
  margin-bottom: 40px;
}
.intro__kicker::before {
  content: "";
  display: inline-block;
  width: 34px; height: 1px;
  background: var(--red);
  vertical-align: middle;
  margin-right: 16px;
}
.intro__statement {
  font-family: var(--serif);
  font-weight: 300;
  font-size: clamp(30px, 4vw, 62px);
  line-height: 1.22;
  letter-spacing: .005em;
  max-width: 20em;
}
.intro__note {
  margin-top: 48px;
  max-width: 44ch;
  color: var(--steel);
  font-size: 15px;
}

/* ============================================================
   SERVICES
   ============================================================ */
.services {
  background: var(--paper-2);
  padding: var(--section) var(--gutter);
}
.services__head {
  max-width: 1400px;
  margin: 0 auto clamp(40px, 5vh, 64px);
  display: grid;
  gap: 20px;
}
.services__headside {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  flex-wrap: wrap;
}

/* --- the rail -----------------------------------------------------------
   Seven packages as full-width rows ran to several screens of scrolling
   before the work below it was reached. They are one row now: a track that
   scrolls sideways, snapping card to card.

   Native overflow scrolling does the work — touch, trackpad, shift-wheel
   and the arrow keys all drive it with no script at all. The buttons are an
   addition for a mouse, not the mechanism.

   Nothing is hidden. All seven cards are in the markup, rendered and
   reachable; the rail changes how far they sit from the left edge, which is
   the difference between a carousel and a tab strip that a crawler reads as
   one visible panel. */
.services__rail {
  max-width: 1400px;
  margin: 0 auto;
  overflow-x: auto;
  overscroll-behavior-x: contain;
  scroll-snap-type: x mandatory;
  scroll-behavior: smooth;
  /* Room for the cards' focus ring and lift, which a tight clip would cut. */
  padding: 4px 4px 28px;
  /* Matched to that padding. Snapping aligns a card to the snapport, which
     scroll-padding defines; left at 0 the rail came to rest 4px in, which
     is not the start, and the back arrow was live with nowhere to go. */
  scroll-padding-inline: 4px;
  /* A thin rule stands in for the scrollbar on the platforms that hide it. */
  scrollbar-width: thin;
  scrollbar-color: var(--hairline) transparent;
}
.services__rail:focus-visible {
  outline: 2px solid var(--red);
  outline-offset: 4px;
}
.services__rail::-webkit-scrollbar { height: 6px; }
.services__rail::-webkit-scrollbar-track { background: transparent; }
.services__rail::-webkit-scrollbar-thumb {
  background: var(--hairline);
  border-radius: 3px;
}

.services__track {
  list-style: none;
  display: flex;
  gap: clamp(14px, 1.6vw, 24px);
  margin: 0;
  padding: 0;
}

/* --- a card ------------------------------------------------------------- */
.service {
  flex: 0 0 clamp(260px, 30%, 380px);
  scroll-snap-align: start;
  border: 1px solid var(--hairline);
  background: color-mix(in srgb, var(--white) 55%, transparent);
  transition: background .45s ease, border-color .45s ease, transform .45s cubic-bezier(.22,.61,.36,1);
}
.service:hover {
  background: var(--white);
  border-color: color-mix(in srgb, var(--steel) 35%, transparent);
  transform: translateY(-4px);
}

/* The whole card is the link: a card that only responds on its title is a
   card most people will click and nothing will happen. */
.service__link {
  display: grid;
  grid-template-rows: auto 1fr auto;
  gap: 18px;
  height: 100%;
  padding: clamp(24px, 2.4vw, 34px);
}
.service__link:focus-visible { outline: 2px solid var(--red); outline-offset: -4px; }

.service__num {
  font-size: 12px;
  letter-spacing: .3em;
  color: var(--steel);
  transition: color .3s ease;
}
.service:hover .service__num { color: var(--red); }

.service__title {
  font-family: var(--serif);
  font-weight: 300;
  font-size: clamp(25px, 2.1vw, 33px);
  line-height: 1.08;
  letter-spacing: .015em;
}

.service__desc {
  margin-top: 12px;
  color: var(--steel);
  font-size: 15px;
  line-height: 1.55;
}

.service__arrow {
  font-size: 22px;
  color: var(--hairline);
  transition: color .3s ease, transform .45s cubic-bezier(.22,.61,.36,1);
}
.service:hover .service__arrow { color: var(--red); transform: translateX(8px); }

/* --- the step buttons --------------------------------------------------- */
.services__nav { display: flex; gap: 8px; }
/* `display` from a class outranks the UA sheet's [hidden] rule, so without
   this the buttons appear before main.js reveals them — and stay if it
   never runs. They ship hidden on purpose. */
.services__nav[hidden] { display: none; }

.services__step {
  /* 44px: the smallest target a thumb can be relied on to hit. */
  width: 44px;
  height: 44px;
  display: grid;
  place-items: center;
  background: transparent;
  border: 1px solid var(--hairline);
  color: var(--ink);
  font-size: 17px;
  cursor: pointer;
  transition: background .3s ease, border-color .3s ease, color .3s ease;
}
.services__step:hover:not(:disabled) {
  background: var(--white);
  border-color: color-mix(in srgb, var(--steel) 35%, transparent);
  color: var(--red);
}
.services__step:focus-visible { outline: 2px solid var(--red); outline-offset: 2px; }
.services__step:disabled { opacity: .3; cursor: default; }

/* On a phone the card is most of the width, and the sliver of the next one
   says "this scrolls" more plainly than any button could. */
@media (max-width: 700px) {
  .service { flex-basis: 82%; }
  .service__arrow { display: none; }
  .service__link { grid-template-rows: auto 1fr; }
}
@media (max-width: 560px) {
  .service { flex-basis: 86%; }
  .service__title { font-size: clamp(24px, 6.4vw, 30px); }
  .service:hover { transform: none; }
}

@media (prefers-reduced-motion: reduce) {
  .services__rail { scroll-behavior: auto; }
  .service:hover { transform: none; }
  .service:hover .service__arrow { transform: none; }
}

/* --- capabilities that run through every project ------------------------ */
/* ============================================================
   FLIGHT TRAJECTORY
   The four capabilities every project carries, drawn as a route rather
   than a list. The old grid put four items into three columns and left
   the fourth stranded on its own row; a route has no such problem,
   because the sequence is the layout.
   ============================================================ */
.traj {
  max-width: 1400px;
  margin: clamp(56px, 8vh, 96px) auto 0;
  padding-top: clamp(40px, 5vh, 64px);
  border-top: 1px solid var(--hairline);
}

.traj__title {
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(26px, 3vw, 40px);
  line-height: 1.1;
  letter-spacing: .01em;
}
.traj__note {
  margin-top: 12px;
  color: var(--steel);
  font-size: 14px;
  letter-spacing: .01em;
}

.traj__legs {
  list-style: none;
  display: grid;
  grid-template-columns: repeat(5, 1fr);
  gap: 0;
  margin-top: clamp(44px, 6vh, 76px);
}

.leg {
  position: relative;
  padding: 0 clamp(14px, 1.6vw, 28px) 0 0;
}

/* the route itself: one hairline per leg, so it spans the full row without
   a separate element to keep in sync */
.leg::before {
  content: "";
  position: absolute;
  top: 5px;
  left: 0;
  right: 0;
  height: 1px;
  background: var(--hairline);
}
/* the last leg's line stops at its own marker: the route arrives, it does
   not trail off the edge of the section */
.leg:last-child::before { right: calc(100% - 11px); }

/* direction, stated once per join rather than repeated as decoration */
.leg:not(:last-child)::after {
  content: "";
  position: absolute;
  top: 2px;
  right: clamp(14px, 1.6vw, 28px);
  width: 7px;
  height: 7px;
  border-top: 1px solid var(--hairline);
  border-right: 1px solid var(--hairline);
  transform: rotate(45deg);
}

.leg__marker {
  position: absolute;
  top: 0;
  left: 0;
  width: 11px;
  height: 11px;
  border-radius: 50%;
  border: 1px solid var(--steel);
  background: var(--paper);
  transition: border-color .35s ease, background .35s ease;
}
/* the aircraft is on the last leg by the time the reader gets here */
.leg:last-child .leg__marker { background: var(--red); border-color: var(--red); }

.leg__stage {
  display: block;
  margin-top: 30px;
  font-size: 10px;
  font-weight: 500;
  letter-spacing: .28em;
  text-transform: uppercase;
  color: var(--red);
}

.leg__title {
  margin-top: 14px;
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(19px, 1.5vw, 24px);
  line-height: 1.15;
  letter-spacing: .01em;
}

.leg__desc {
  margin-top: 10px;
  max-width: 26ch;
  color: var(--steel);
  font-size: 14px;
  line-height: 1.6;
}

@media (hover: hover) and (pointer: fine) {
  .leg:hover .leg__marker { border-color: var(--red); background: var(--red); }
}

/* Below this the route turns vertical. Five columns of prose need more width
   than four did, so the turn happens earlier: a horizontal flow chart whose
   columns are too narrow to read is no longer doing the job either way. */
@media (max-width: 1080px) {
  .traj__legs { grid-template-columns: 1fr; }

  .leg {
    padding: 0 0 clamp(26px, 4vh, 40px) 30px;
  }
  .leg:last-child { padding-bottom: 0; }

  /* the line runs down the left instead of across the top */
  .leg::before {
    top: 5px;
    bottom: 0;
    left: 5px;
    right: auto;
    width: 1px;
    height: auto;
  }
  .leg:last-child::before { display: none; }
  .leg:not(:last-child)::after { display: none; }

  .leg__stage { margin-top: 0; }
  .leg__title { margin-top: 8px; }
  .leg__desc { max-width: 46ch; }
}

.work__all {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  margin-top: 22px;
  font-size: 11px;
  font-weight: 500;
  letter-spacing: .28em;
  text-transform: uppercase;
  color: var(--ink);
  border-bottom: 1px solid var(--red);
  padding-bottom: 3px;
  transition: color .3s ease;
}
.work__all:hover { color: var(--red); }
.work__all:focus-visible { outline: 2px solid var(--red); outline-offset: 4px; }

/* ============================================================
   WORK
   ============================================================ */
.work { padding: var(--section) var(--gutter); max-width: 1500px; margin: 0 auto; }
.work__head { margin-bottom: clamp(60px, 8vh, 110px); }


/* Each slot in the composition owns its shape, so a landscape poster and
   a portrait still can sit in the grid without breaking the rhythm. */
.tile__img { position: relative; }
.tile:nth-child(1) .tile__img,
.tile:nth-child(4) .tile__img { aspect-ratio: 16 / 10; }
.tile:nth-child(2) .tile__img,
.tile:nth-child(3) .tile__img { aspect-ratio: 4 / 5; }
.tile:nth-child(5) .tile__img,
.tile:nth-child(6) .tile__img { aspect-ratio: 3 / 2; }

/* the affordance: a tile is a way in, not a photograph on a wall */
.tile__go {
  position: absolute;
  left: 0;
  bottom: 0;
  padding: 11px 18px;
  background: var(--paper);
  color: var(--ink);
  font-size: 10px;
  letter-spacing: .26em;
  text-transform: uppercase;
  transform: translateY(100%);
  transition: transform .45s cubic-bezier(.22,.61,.36,1);
}
.tile:hover .tile__go,
.tile:focus-visible .tile__go { transform: translateY(0); }

.tile:focus-visible { outline: 2px solid var(--red); outline-offset: 6px; }

@media (hover: none) {
  /* no hover on touch — keep the label visible so the tile still reads
     as something to tap */
  .tile__go { transform: none; }
}

.work__grid {
  display: grid;
  grid-template-columns: repeat(12, 1fr);
  gap: clamp(48px, 7vw, 110px) clamp(20px, 3vw, 48px);
}

.tile { grid-column: span 6; }
.tile:nth-child(1) { grid-column: 1 / 9; }
.tile:nth-child(2) { grid-column: 9 / 13; align-self: end; }
.tile:nth-child(3) { grid-column: 1 / 5; }
.tile:nth-child(4) { grid-column: 5 / 13; }
.tile:nth-child(5) { grid-column: 1 / 7; }
.tile:nth-child(6) { grid-column: 7 / 13; }

.tile__img {
  overflow: hidden;
  background: var(--paper-2);
}
/* The image overfills its frame so it has somewhere to travel; --shift is
   written per tile from scroll position in main.js.

   Drift and hover are separate properties on purpose. They were one
   `transform` under one `transition: transform 1.4s`, which meant the
   drift — rewritten from scroll position every frame — was made to take
   1.4 seconds to arrive. The image chased the scroll instead of tracking
   it, and under a pointer, with the hover scale riding on the same
   property, that reads as the picture shaking.

   `translate` and `scale` are independent properties, so each gets the
   behaviour it needs: the drift lands immediately, the hover still
   eases. It is the same fix already applied for touch, where the whole
   effect was dropped; here the effect is kept and the lag removed. */
.tile__img img {
  width: 100%;
  height: calc(100% + 44px);
  margin-top: -22px;
  object-fit: cover;
  translate: 0 var(--shift, 0px);
  scale: 1;
  transition: scale 1.4s cubic-bezier(.22,.61,.36,1);
  will-change: translate, scale;
}
.tile:hover .tile__img img { scale: 1.045; }

/* no drift, and no overfill to hide, when motion is not wanted — and no
   will-change, since there is nothing left to promote a layer for */
@media (prefers-reduced-motion: reduce) {
  .tile__img img {
    height: 100%;
    margin-top: 0;
    transform: none;
    translate: none;
    scale: none;
    will-change: auto;
  }
}

/* The preview film sits over the poster and fades in once it is actually
   playing. The poster stays underneath as the frame-holder, so a tile is
   never empty while the clip loads and never blank if it fails to. */
.tile__vid {
  position: absolute;
  /* The same box as the poster underneath, not inset: 0.
     The poster is deliberately 44px taller than its frame and pulled up
     22px, so the drift has somewhere to travel. The clip sat at the
     frame's own edges, which left the two layers 22px apart and at
     different heights: the image appeared to jump as the clip faded in
     over it, and on hover the two scaled about different centres. They
     are one picture, so they get one geometry — including the drift
     offset, which both inherit from .tile__img. */
  left: 0;
  top: -22px;
  width: 100%;
  height: calc(100% + 44px);
  object-fit: cover;
  opacity: 0;
  /* Split for the same reason as the poster above. */
  translate: 0 var(--shift, 0px);
  scale: 1;
  transition: opacity .6s ease, scale 1.4s cubic-bezier(.22,.61,.36,1);
  pointer-events: none;
}
.tile__vid.is-playing { opacity: 1; }
.tile:hover .tile__vid.is-playing { scale: 1.045; }

/* A commercial with no stills behind it. The clip is the tile rather
   than an overlay on a poster, so it is visible from the start instead
   of waiting for a hover it may never get. */
.tile__vid--only { opacity: 1; }

/* The drift is a pointer-era flourish, and on a touch screen it was the
   thing making the gallery feel unsteady: the offset is written from
   scroll position every frame, so the tiles appear to slide against the
   page. It no longer lags the scroll — translate and scale are separate
   properties now — but on a touch screen the effect is unwanted either
   way. The script skips it where there is no hover; this is the matching
   half — no overfill to conceal travel that no longer happens, no
   transition to lag, and no will-change holding every tile on its own
   layer for the life of the page, which is not free on a phone.

   This block sits below the rules it overrides rather than above them,
   and has to. It used to come first, which was fine for the poster —
   declared earlier still — but not for .tile__vid, declared after it at
   the same specificity and therefore winning. Nothing showed it: the
   clips did not play on touch at all, so nobody ever saw a video
   keeping an overfill the poster beneath it had given up. The moment
   they did play, the two layers were 22px and 44px apart and the tile
   jumped as the clip faded in. */
@media (hover: none) {
  .tile__img img,
  .tile__vid {
    height: 100%;
    margin-top: 0;
    top: 0;
    transform: none;
    translate: none;
    scale: none;
    transition: none;
    will-change: auto;
  }
  /* The fade is the one piece of motion worth keeping here: it is what
     covers the swap from poster to clip. */
  .tile__vid { transition: opacity .6s ease; }
}

/* And the box needs its own height. Every other tile is sized by the
   poster inside it, with the aspect-ratio rules above only shaping the
   six slots that are on screen. A tile holding nothing but an absolutely
   positioned clip has no content to measure, so in any other slot it
   collapsed and the clip rendered 44px tall — the padding, and nothing
   else. This is the same 16:10 the wide slots use. */
.tile__img--vid { aspect-ratio: 16 / 10; }

/* Reduced motion keeps the poster and never fetches a clip — except
   where the clip is all there is, which would otherwise leave an empty
   tile. */
@media (prefers-reduced-motion: reduce) {
  .tile__vid:not(.tile__vid--only) { display: none; }
}

.tile figcaption,
.tile__cap {
  display: flex;
  justify-content: space-between;
  align-items: baseline;
  gap: 16px;
  margin-top: 16px;
  border-bottom: 1px solid var(--hairline);
  padding-bottom: 12px;
}
.tile figcaption span,
.tile__cap span {
  font-size: 12px;
  letter-spacing: .18em;
  text-transform: uppercase;
  color: var(--ink);
}
.tile figcaption em,
.tile__cap em {
  font-family: var(--serif);
  font-size: 16px;
  color: var(--steel);
}
.tile:hover figcaption em,
.tile:hover .tile__cap em { color: var(--red); }

@media (max-width: 700px) {
  .tile, .tile:nth-child(n) { grid-column: 1 / 13; }
}

/* ============================================================
   STUDIO
   ============================================================ */
.studio {
  display: grid;
  grid-template-columns: minmax(0, 5fr) minmax(0, 6fr);
  gap: clamp(40px, 6vw, 110px);
  padding: var(--section) var(--gutter);
  max-width: 1500px;
  margin: 0 auto;
  align-items: center;
}
.studio__media { overflow: hidden; }
.studio__media img { width: 100%; height: auto; }

.studio__text {
  color: var(--steel);
  font-size: 15px;
  max-width: 52ch;
  margin-top: 24px;
}

.studio__facts {
  list-style: none;
  display: flex;
  gap: clamp(28px, 4vw, 64px);
  margin-top: 56px;
  border-top: 1px solid var(--hairline);
  padding-top: 32px;
}
.studio__facts strong {
  display: block;
  font-family: var(--serif);
  font-weight: 300;
  font-size: clamp(30px, 3vw, 46px);
  line-height: 1;
  color: var(--ink);
}
.studio__facts span {
  display: block;
  margin-top: 10px;
  font-size: 11px;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: var(--steel);
}

@media (max-width: 860px) {
  .studio { grid-template-columns: 1fr; }
  .studio__facts { flex-wrap: wrap; }
}

/* ============================================================
   CONTACT
   ============================================================ */
.contact {
  background: var(--paper-2);
  text-align: center;
  padding: var(--section) var(--gutter);
}
.contact .section-kicker::before { margin-right: 16px; }

.contact__title {
  font-family: var(--serif);
  font-weight: 300;
  font-size: clamp(52px, 8.5vw, 140px);
  line-height: 1.02;
  margin-bottom: clamp(40px, 6vh, 70px);
}
.contact__title em { color: var(--steel); }

.contact__mail {
  display: inline-block;
  font-family: var(--serif);
  font-size: clamp(20px, 2.4vw, 34px);
  font-weight: 400;
  border-bottom: 1px solid var(--ink);
  padding-bottom: 6px;
  transition: color .3s ease, border-color .3s ease;
}
.contact__mail:hover { color: var(--red); border-color: var(--red); }

/* --- enquiry form ------------------------------------------------------- */
.enquiry {
  max-width: 640px;
  margin: 0 auto;
  text-align: left;
}

.enquiry__row {
  display: grid;
  gap: 0 28px;
  grid-template-columns: 1fr 1fr;
}
@media (max-width: 640px) {
  .enquiry__row { grid-template-columns: 1fr; }
}

.enquiry .field { margin-bottom: 26px; }

.enquiry label {
  display: block;
  font-size: 10px;
  letter-spacing: .28em;
  text-transform: uppercase;
  color: var(--steel);
  margin-bottom: 10px;
}
.enquiry label em {
  font-style: normal;
  letter-spacing: .16em;
  opacity: .6;
}

.enquiry input,
.enquiry select,
.enquiry textarea {
  width: 100%;
  padding: 12px 0;
  font-family: var(--sans);
  font-weight: 300;
  font-size: 16px;
  color: var(--ink);
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--hairline);
  border-radius: 0;
  transition: border-color .3s ease;
  -webkit-appearance: none;
  appearance: none;
}

.enquiry textarea { resize: vertical; min-height: 96px; line-height: 1.6; }

/* the caret the native select loses to appearance:none */
.enquiry select {
  background-image:
    linear-gradient(45deg, transparent 50%, var(--steel) 50%),
    linear-gradient(135deg, var(--steel) 50%, transparent 50%);
  background-position: calc(100% - 9px) 21px, calc(100% - 4px) 21px;
  background-size: 5px 5px;
  background-repeat: no-repeat;
  padding-right: 24px;
  cursor: pointer;
}

.enquiry input:focus,
.enquiry select:focus,
.enquiry textarea:focus {
  outline: none;
  border-bottom-color: var(--ink);
}

.enquiry input::placeholder,
.enquiry textarea::placeholder { color: var(--hairline); }

.enquiry .invalid { border-bottom-color: var(--red); }

/* honeypot — off-screen rather than display:none, which some bots skip */
.enquiry__trap {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.enquiry__submit {
  margin-top: 6px;
  padding: 15px 40px;
  font-family: var(--sans);
  font-size: 11px;
  font-weight: 400;
  letter-spacing: .28em;
  text-transform: uppercase;
  color: var(--paper);
  background: var(--ink);
  border: 1px solid var(--ink);
  cursor: pointer;
  transition: background .3s ease, color .3s ease;
}
.enquiry__submit:hover { background: var(--red); border-color: var(--red); }
.enquiry__submit[disabled] { opacity: .5; cursor: default; }
.enquiry__submit[disabled]:hover { background: var(--ink); border-color: var(--ink); }

.enquiry__status {
  margin-top: 20px;
  font-size: 13px;
  line-height: 1.6;
  color: var(--steel);
  min-height: 1.6em;
}
.enquiry__status.is-error { color: var(--red); }

.enquiry--sent {
  text-align: center;
  font-family: var(--serif);
  font-size: clamp(22px, 3vw, 34px);
  line-height: 1.35;
  padding: 40px 0;
}

.enquiry__alt {
  margin-top: 44px;
  font-size: 11px;
  letter-spacing: .24em;
  text-transform: uppercase;
  color: var(--steel);
}
.enquiry__alt .contact__mail,
.enquiry__alt .contact__call {
  font-size: 15px;
  letter-spacing: .06em;
  text-transform: none;
  padding-bottom: 2px;
}

/* The call link matches the email beside it: same weight, same rule
   under it, so neither reads as the louder option. */
.contact__call {
  display: inline-flex;
  align-items: center;
  gap: 8px;
  font-family: var(--serif);
  font-weight: 400;
  border-bottom: 1px solid var(--ink);
  transition: color .3s ease, border-color .3s ease;
}
.contact__call:hover { color: var(--red); border-color: var(--red); }

/* Sized in em so it tracks the number it sits beside rather than
   needing its own breakpoint. */
.contact__icon {
  width: 1em;
  height: 1em;
  flex-shrink: 0;
  color: var(--red);
}

/* The separator between the two, and the gap either side of it. On a
   phone the pair wraps onto two lines and the dot would be stranded at
   the end of the first, so it goes. */
/* --hairline is a border colour; as type on this background it measured
   about 1.1:1 and simply was not there. */
.enquiry__or { margin: 0 14px; color: var(--steel); opacity: .55; }

@media (max-width: 560px) {
  .enquiry__alt {
    display: flex;
    flex-direction: column;
    align-items: center;
    gap: 14px;
  }
  .enquiry__or { display: none; }

  /* As a line of text this is a 29px target and a thumb is about 44.
     The padding is all above, so the rule stays tight under the text
     where it belongs rather than floating off the bottom of a padded
     box; the margin carries the rest of the clearance. */
  .enquiry__alt .contact__call {
    padding-top: 16px;
    margin-bottom: 6px;
  }
}

.contact__meta {
  margin-top: 36px;
  font-size: 11px;
  letter-spacing: .32em;
  text-transform: uppercase;
  color: var(--steel);
}

/* ============================================================
   PROJECT PAGES  (/work/*.html)
   ============================================================ */
.nav--solid {
  background: color-mix(in srgb, var(--paper) 94%, transparent);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
  box-shadow: 0 1px 0 var(--hairline);
}
/* over the cinematic hero the bar has to read on a dark plate instead */
.nav--over {
  background: transparent;
  box-shadow: none;
  backdrop-filter: none;
  -webkit-backdrop-filter: none;
}
.nav--over .nav__word,
.nav--over .nav__links a { color: rgba(255,255,255,.82); }
.nav--over .nav__links a:hover { color: #fff; }
.nav--over .nav__cta { border-color: rgba(255,255,255,.34); }
.nav--over.scrolled {
  background: color-mix(in srgb, var(--void) 88%, transparent);
  box-shadow: 0 1px 0 rgba(255,255,255,.09);
  backdrop-filter: blur(12px);
  -webkit-backdrop-filter: blur(12px);
}

/* --- cinematic hero ------------------------------------------------------
   The silent ambient loop plays behind the title, so the project opens on
   the work itself rather than on a headline. */
.phero {
  position: relative;
  min-height: min(92svh, 900px);
  display: flex;
  align-items: flex-end;
  overflow: hidden;
  background: var(--void);
  isolation: isolate;
}

.phero__video,
.phero__still {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: -2;
}

/* a scrim so type stays legible over any frame */
.phero::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background:
    linear-gradient(to top, rgba(11,11,12,.92) 0%, rgba(11,11,12,.45) 42%, rgba(11,11,12,.30) 100%);
}

.phero__inner {
  width: 100%;
  max-width: 1500px;
  margin: 0 auto;
  padding: 0 var(--gutter) clamp(52px, 9vh, 104px);
  color: var(--paper);
}

.phero__kicker {
  font-size: 11px;
  letter-spacing: .42em;
  text-transform: uppercase;
  color: rgba(255,255,255,.62);
  margin-bottom: clamp(18px, 3vh, 30px);
}
.phero__kicker::before {
  content: "";
  display: inline-block;
  width: 34px;
  height: 1px;
  background: var(--red);
  vertical-align: middle;
  margin-right: 16px;
}
.phero__kicker a { transition: color .3s ease; }
.phero__kicker a:hover { color: #fff; }

.phero__title {
  font-family: var(--serif);
  font-weight: 300;
  font-size: clamp(46px, 8vw, 132px);
  line-height: .98;
  letter-spacing: .005em;
  margin-bottom: clamp(18px, 2.6vh, 30px);
}

.phero__tag {
  font-family: var(--serif);
  font-size: clamp(19px, 2vw, 28px);
  line-height: 1.45;
  color: rgba(255,255,255,.76);
  max-width: 30ch;
}

/* --- facts ---------------------------------------------------------------- */
.pfacts {
  max-width: 1500px;
  margin: 0 auto;
  padding: clamp(38px, 6vh, 64px) var(--gutter);
  display: grid;
  gap: clamp(22px, 3vw, 48px);
  grid-template-columns: repeat(auto-fit, minmax(170px, 1fr));
  border-bottom: 1px solid var(--hairline);
}
.pfacts dt {
  font-size: 10px;
  letter-spacing: .3em;
  text-transform: uppercase;
  color: var(--steel);
  margin-bottom: 8px;
}
.pfacts dd { font-size: 16px; }

/* --- the statement -------------------------------------------------------- */
.pstatement {
  max-width: 1200px;
  margin: 0 auto;
  padding: clamp(80px, 13vh, 170px) var(--gutter);
}
.pstatement p {
  font-family: var(--serif);
  font-weight: 300;
  font-size: clamp(28px, 4.2vw, 62px);
  line-height: 1.16;
  letter-spacing: .01em;
  max-width: 20ch;
}
.pstatement em { color: var(--steel); }

/* --- the beats ------------------------------------------------------------ */
.pstory {
  max-width: 1200px;
  margin: 0 auto;
  padding: 0 var(--gutter) clamp(80px, 12vh, 160px);
  display: grid;
  gap: clamp(40px, 6vh, 72px);
}

.beat {
  display: grid;
  gap: clamp(10px, 2vw, 44px);
  grid-template-columns: 1fr;
  padding-top: clamp(26px, 3.5vh, 40px);
  border-top: 1px solid var(--hairline);
}
@media (min-width: 820px) {
  .beat { grid-template-columns: 220px 1fr; align-items: start; }
  /* every paragraph is its own grid item — pin them to the copy column */
  .beat > p { grid-column: 2; }

  /* A beat with no label has nothing to put in the narrow column, and an
     empty 220px indent reads as a mistake. Give it the full width. */
  .beat:not(:has(h2)) { grid-template-columns: 1fr; }
  .beat:not(:has(h2)) > p { grid-column: 1; }
}

.beat h2 {
  font-size: 11px;
  font-weight: 400;
  letter-spacing: .3em;
  text-transform: uppercase;
  color: var(--steel);
  padding-top: 6px;
}

.beat p {
  font-size: clamp(16px, 1.4vw, 20px);
  line-height: 1.72;
  max-width: 58ch;
}
.beat p + p { margin-top: 20px; }

/* --- the film, in the cinema room ------------------------------------------ */
.pfilm {
  background: var(--void);
  padding: clamp(72px, 11vh, 150px) var(--gutter);
}
.pfilm__inner { max-width: 1400px; margin: 0 auto; }

.pfilm__frame {
  position: relative;
  overflow: hidden;
  background: #000;
  aspect-ratio: 16 / 9;
}
.pfilm__frame video {
  width: 100%;
  height: 100%;
  object-fit: cover;
  display: block;
}
/* the full film sits on top only once it has been started */
.pfilm__full { position: absolute; inset: 0; opacity: 0; pointer-events: none; }
.is-playing .pfilm__full { opacity: 1; pointer-events: auto; }
.is-playing .pfilm__loop { opacity: 0; }

.pfilm__play {
  position: absolute;
  left: 50%;
  top: 50%;
  transform: translate(-50%, -50%);
  width: clamp(64px, 7vw, 92px);
  height: clamp(64px, 7vw, 92px);
  border-radius: 50%;
  border: 1px solid rgba(255,255,255,.5);
  background: rgba(11,11,12,.34);
  color: #fff;
  cursor: pointer;
  display: grid;
  place-items: center;
  backdrop-filter: blur(6px);
  -webkit-backdrop-filter: blur(6px);
  transition: background .35s ease, border-color .35s ease, transform .45s cubic-bezier(.22,.61,.36,1), opacity .3s ease;
}
.pfilm__play svg { width: 34%; height: 34%; margin-left: 8%; }
.pfilm__play:hover { background: var(--red); border-color: var(--red); transform: translate(-50%, -50%) scale(1.07); }
.is-playing .pfilm__play { opacity: 0; pointer-events: none; }

.pfilm__cap {
  margin-top: 18px;
  display: flex;
  justify-content: space-between;
  gap: 20px;
  font-size: 11px;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: #8d8d92;
}

/* --- next project --------------------------------------------------------- */
.pnext {
  position: relative;
  display: block;
  min-height: min(56svh, 520px);
  overflow: hidden;
  background: var(--void);
  isolation: isolate;
}
.pnext img {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  z-index: -2;
  transform: scale(1.001);
  transition: transform 1.6s cubic-bezier(.22,.61,.36,1);
}
.pnext::after {
  content: "";
  position: absolute;
  inset: 0;
  z-index: -1;
  background: linear-gradient(to top, rgba(11,11,12,.88), rgba(11,11,12,.42));
  transition: background .5s ease;
}
.pnext:hover img { transform: scale(1.05); }

.pnext__inner {
  max-width: 1500px;
  margin: 0 auto;
  padding: clamp(64px, 11vh, 130px) var(--gutter);
  color: var(--paper);
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 28px;
  min-height: inherit;
}
.pnext__label {
  font-size: 11px;
  letter-spacing: .42em;
  text-transform: uppercase;
  color: rgba(255,255,255,.6);
  margin-bottom: 14px;
}
.pnext__name {
  font-family: var(--serif);
  font-weight: 300;
  font-size: clamp(30px, 5vw, 76px);
  line-height: 1.02;
}
.pnext__arrow {
  font-style: normal;
  font-size: clamp(26px, 3vw, 44px);
  color: rgba(255,255,255,.6);
  transition: transform .45s cubic-bezier(.22,.61,.36,1), color .35s ease;
}
.pnext:hover .pnext__arrow { transform: translateX(12px); color: var(--red); }

/* --- closing call --------------------------------------------------------- */
.pcta {
  text-align: center;
  padding: clamp(96px, 15vh, 190px) var(--gutter);
}
.pcta .section-title { margin-bottom: 40px; }

.button-line {
  display: inline-block;
  font-size: 12px;
  letter-spacing: .26em;
  text-transform: uppercase;
  padding: 15px 36px;
  border: 1px solid var(--ink);
  transition: background .35s ease, color .35s ease, border-color .35s ease;
}
.button-line:hover { background: var(--red); border-color: var(--red); color: var(--paper); }

/* the frames page reuses the home grid without its section padding */
.work--flush { padding-top: 0; }
.project { padding-top: clamp(120px, 16vh, 190px); }
.project__head { max-width: 1100px; margin: 0 auto; padding: 0 var(--gutter) clamp(48px, 7vh, 84px); }
.project__head .section-kicker a { transition: color .3s ease; }
.project__head .section-kicker a:hover { color: var(--red); }
.project__title {
  font-family: var(--serif);
  font-weight: 300;
  font-size: clamp(46px, 7.5vw, 112px);
  line-height: 1.02;
  margin-bottom: clamp(20px, 3vh, 32px);
}
.project__tag {
  font-family: var(--serif);
  font-size: clamp(20px, 2.2vw, 30px);
  line-height: 1.4;
  color: var(--steel);
  max-width: 34ch;
}

/* ============================================================
   TOUCH TARGETS
   The audit walked all 22 pages at 360 and 390px and found 86 links
   under the 44px a finger actually needs, plus two labels under 11px.
   Fixed here in one place rather than scattered through each section.
   ============================================================ */
@media (max-width: 760px) {
  /* footer links sat at 25px tall; the padding buys the height without
     changing how the row reads */
  .footer__links { gap: 4px 24px; }
  .footer__links a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    font-size: 12px;
  }

  .footer__brand { min-height: 44px; }
  .nav__brand { min-height: 44px; align-items: center; }

  /* 10px is below what anyone should be asked to read on a phone */
  .leg__stage { font-size: 11px; }
  .footer__legal { font-size: 12px; line-height: 1.6; }

  /* the carousel dots are the one control on the film section */
  .film__dot { min-width: 44px; }

  /* remaining standalone links the audit found short of 44px: the 404
     recovery list, the contact address, the story titles on the work hub,
     and the kicker links that act as a way back */
  .nf a,
  .contact__mail,
  .story__title a,
  .stories__list a,
  .section-kicker a,
  .phero__kicker a {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }
  .contact__mail { padding-bottom: 10px; }

  /* labels that were sitting at 10px */
  .svc__relCat, .tile__go { font-size: 11px; }
}

/* A pointer that cannot hover cannot discover anything on hover, so the
   affordances that were hover-only need to be resting state instead. */
@media (hover: none) {
  .tile__go { transform: none; }
  .work__all { text-decoration-color: var(--red); }
}

/* ============================================================
   FOOTER
   ============================================================ */
.footer {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 24px;
  flex-wrap: wrap;
  padding: 40px var(--gutter);
  border-top: 1px solid var(--hairline);
}
.footer__brand { display: flex; align-items: center; gap: 10px; }
.footer__brand img { width: 26px; }
.footer__brand span {
  font-family: var(--serif);
  font-size: 13px;
  letter-spacing: .28em;
  text-transform: uppercase;
  color: var(--steel);
}
/* five tracked uppercase links measure ~400px, so without wrapping they push
   the whole document sideways on any phone narrower than that */
.footer__links { display: flex; flex-wrap: wrap; gap: 14px 26px; }
.footer__links a {
  font-size: 11px;
  letter-spacing: .2em;
  text-transform: uppercase;
  color: var(--steel);
  transition: color .3s ease;
}
.footer__links a:hover { color: var(--red); }
.footer__legal { font-size: 11px; color: var(--steel); letter-spacing: .06em; }

/* ============================================================
   FILM — the dark room, lit by sky
   Same serif voice as the rest of the site; the atmosphere
   comes from a real aerial sky plate behind the content.
   ============================================================ */
/* ============================================================
   CHARTER — the brands strip

   A board of marks between the services and the work: who we have flown
   for, read before what we flew. Short — the whole thing is about the
   height of two lines of the copy above it.

   On the page tone rather than the deeper bone band, because the
   services section above it is already that band and two of them
   running together would read as one long grey block with a heading
   dropped into the middle. What separates this instead is the pair of
   rules: services closes, a hairline, the manifest, a hairline, and
   the portfolio opens. The lower one doubles as an editorial rule over
   "Selected work".

   It is light on purpose. Everything else about the idea says departure
   board, and a departure board is dark — but a brand logo is almost
   always dark ink on nothing, and on a dark band half of them would
   disappear. The announcement comes from the type instead: tracked-out
   uppercase, hairline cells, the marks passing in one direction.
   ============================================================ */
.charter {
  background: var(--paper);
  border-top: 1px solid var(--hairline);
  border-bottom: 1px solid var(--hairline);
  padding: clamp(44px, 6vh, 72px) 0;
  overflow: hidden;
}

.charter__head {
  padding: 0 var(--gutter);
  margin-bottom: clamp(26px, 4vh, 40px);
}

.charter__head .section-kicker { margin-bottom: 14px; }

.charter__title {
  font-family: var(--serif);
  font-weight: 300;
  font-size: clamp(26px, 3.2vw, 44px);
  line-height: 1.1;
  letter-spacing: .01em;
}

/* The board bleeds past the gutter on both sides: a strip that began
   and ended inside the margin would read as a list that happens to
   move, rather than as marks passing through. */
.charter__board {
  position: relative;
}

/* The fade at each end is what sells the pass-through — marks arrive and
   leave rather than appearing and vanishing at a hard edge. Masked
   rather than covered with a gradient overlay, so it works whatever is
   behind it and never sits on top of a link. */
@supports (mask-image: linear-gradient(90deg, #000, #000)) {
  .charter__board {
    -webkit-mask-image: linear-gradient(90deg, transparent, #000 7%, #000 93%, transparent);
    mask-image: linear-gradient(90deg, transparent, #000 7%, #000 93%, transparent);
  }
}

/* Also the scroll container, so the strip can be read by dragging and by
   keyboard when the animation is paused or off. overflow-x: auto rather
   than hidden for that reason — and the scrollbar is hidden because it
   would sit across the bottom of the marks. */
.charter__rail {
  display: flex;
  width: max-content;
  overflow-x: auto;
  scrollbar-width: none;
  animation: charter-run calc(var(--cells, 6) * 4s) linear infinite;
}

.charter__rail::-webkit-scrollbar { display: none; }

.charter__rail:focus-visible {
  outline: 2px solid var(--red);
  outline-offset: 4px;
}

/* A mark nobody can click is decoration. The strip stops under a
   pointer and whenever anything inside it takes focus. */
.charter__board:hover .charter__rail,
.charter__rail:focus-within,
.charter__rail:focus {
  animation-play-state: paused;
}

/* Exactly one copy of the list. The rail holds --runs of them, so one
   copy is 100%/--runs of its width — see the note in index.njk. */
@keyframes charter-run {
  from { transform: translate3d(0, 0, 0); }
  to   { transform: translate3d(calc(-100% / var(--runs, 2)), 0, 0); }
}

.charter__run {
  display: flex;
  list-style: none;
  margin: 0;
  padding: 0;
}

.charter__cell {
  position: relative;
  flex: 0 0 auto;
}

/* The divider is drawn rather than bordered, for two reasons. A border
   is part of the box, so dropping it from the first cell — which has to
   go, or the strip opens with a stray rule under the mask — would make
   the first run one pixel narrower than the second, and the loop would
   land a pixel out every time round. And a rule inset from the top and
   bottom reads as a board column rather than a table cell. */
.charter__cell::before {
  content: "";
  position: absolute;
  left: 0;
  top: 14%;
  bottom: 14%;
  width: 1px;
  background: var(--hairline);
}

.charter__run:first-child .charter__cell:first-child::before { display: none; }

.charter__mark {
  display: grid;
  place-items: center;
  width: clamp(170px, 20vw, 240px);
  min-height: 72px;
  padding: 10px clamp(18px, 2.5vw, 34px);
}

.charter__mark img {
  /* A logo is given a box and sits inside it. Any other rule here —
     a width, a fill — crops somebody's mark or stretches it. */
  max-width: 100%;
  max-height: 52px;
  width: auto;
  height: auto;
  object-fit: contain;
  /* Marks arrive in every colour there is. Holding them at one weight
     is what makes a row of them read as a list rather than a collage;
     the colour comes back under the pointer. */
  filter: grayscale(1);
  opacity: .62;
  transition: opacity .4s ease, filter .4s ease;
}

/* Only a link lights up. A mark that goes nowhere stays as it is, so
   the brightening is an affordance rather than decoration that happens
   to follow the cursor. */
a.charter__mark:hover img,
a.charter__mark:focus-visible img {
  filter: grayscale(0);
  opacity: 1;
}

a.charter__mark:focus-visible {
  outline: 2px solid var(--red);
  outline-offset: 2px;
}

.charter__mark--flat { cursor: default; }

@media (max-width: 700px) {
  .charter__mark img { max-height: 40px; }
  .charter__mark { min-height: 62px; }
}

/* Standing still, the rail is a list that scrolls by hand. The second
   run is removed rather than left in place: duplicated marks are only
   there to make a loop seamless, and without the loop they are the
   manifest printed twice. */
@media (prefers-reduced-motion: reduce) {
  .charter__rail {
    animation: none;
    width: auto;
    max-width: 100%;
    scroll-snap-type: x proximity;
  }

  .charter__rail > [aria-hidden="true"] { display: none; }

  .charter__cell { scroll-snap-align: center; }
}

.film {
  position: relative;
  isolation: isolate;
  background: var(--void);
  color: #e9e9e6;
  padding: var(--section) var(--gutter);
  overflow: hidden;
}

/* the sky: a real frame from the ridgeline aerial, drifting slowly */
.film__sky {
  position: absolute;
  inset: -6% -4% auto -4%;
  height: 78%;
  z-index: -1;
  background: url("../assets/sky-plate.jpg") center 34% / cover no-repeat;
  opacity: .85;
  animation: drift 46s ease-in-out infinite alternate;
}
.film__sky::after {
  content: "";
  position: absolute;
  inset: 0;
  background:
    linear-gradient(to bottom,
      rgba(11,11,12,.28) 0%,
      rgba(11,11,12,.46) 38%,
      rgba(11,11,12,.86) 74%,
      var(--void) 100%);
}
@keyframes drift {
  from { transform: scale(1.04) translate3d(-1.2%, 0, 0); }
  to   { transform: scale(1.12) translate3d(1.2%, -1.5%, 0); }
}
@media (prefers-reduced-motion: reduce) {
  .film__sky { animation: none; }
}

.film__head {
  max-width: 1400px;
  margin: 0 auto clamp(56px, 8vh, 100px);
}
.film__display {
  font-family: var(--serif);
  font-weight: 300;
  font-size: clamp(44px, 6.4vw, 104px);
  line-height: 1.02;
  letter-spacing: .012em;
  color: var(--white);
  text-shadow: 0 2px 40px rgba(0,0,0,.45);
}
.film__display em { color: #cfd6dd; }

.film__lede {
  margin-top: 32px;
  max-width: 56ch;
  font-size: 15px;
  color: #b6b6bb;
}

/* ---------- carousel ---------- */
.film__carousel { max-width: 1200px; margin: 0 auto; }

.film__viewport { overflow: hidden; }

.film__track {
  display: flex;
  transition: transform .78s cubic-bezier(.22,.61,.36,1);
  will-change: transform;
}

.vcard {
  flex: 0 0 100%;
  min-width: 0;
  /* off-slide cards recede rather than sitting at full strength */
  opacity: .32;
  transition: opacity .6s ease;
}
.vcard.is-current { opacity: 1; }

/* ---------- carousel controls ---------- */
.film__controls {
  display: flex;
  align-items: center;
  justify-content: center;
  gap: clamp(14px, 2vw, 26px);
  margin-top: clamp(28px, 4vh, 44px);
}

.film__arrow,
.film__toggle {
  width: 44px;
  aspect-ratio: 1;
  flex: none;
  border-radius: 50%;
  border: 1px solid rgba(255,255,255,.22);
  background: transparent;
  color: #cfcfd4;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: border-color .3s ease, color .3s ease, background .3s ease;
}
.film__arrow svg { width: 18px; }
.film__toggle svg { width: 16px; }
.film__arrow:hover,
.film__toggle:hover {
  border-color: var(--red);
  color: var(--red);
  background: rgba(230,51,41,.08);
}
.film__arrow:focus-visible,
.film__toggle:focus-visible { outline: 2px solid var(--red); outline-offset: 3px; }

/* the toggle swaps its icon with the running state */
.film__toggle-play { display: none; }
.film__carousel.is-paused .film__toggle-pause { display: none; }
.film__carousel.is-paused .film__toggle-play  { display: block; }

.film__dots {
  display: flex;
  align-items: center;
  gap: 10px;
}

/* each dot carries its own progress sweep toward the next slide.
   The button is 44px tall so it can actually be hit on a phone; the 2px rail
   is drawn inside it rather than being the element itself. */
.film__dot {
  position: relative;
  width: 54px;
  height: 44px;
  border: 0;
  padding: 0;
  background: transparent;
  cursor: pointer;
}
.film__dot::before,
.film__dot::after {
  content: "";
  position: absolute;
  left: 0;
  right: 0;
  top: 50%;
  height: 2px;
  margin-top: -1px;
}
.film__dot::before { background: rgba(255,255,255,.2); transition: background .3s ease; }
.film__dot:hover::before { background: rgba(255,255,255,.4); }
.film__dot:focus-visible { outline: 2px solid var(--red); outline-offset: -4px; }
.film__dot::after {
  background: var(--red);
  transform: scaleX(0);
  transform-origin: left;
}
/* completed slides stay filled */
.film__dot.is-done::after { transform: scaleX(1); }
/* the active dot sweeps across while the timer runs */
.film__dot.is-active::after {
  animation: dot-sweep var(--slide-ms, 7000ms) linear forwards;
}
.film__carousel.is-paused .film__dot.is-active::after { animation-play-state: paused; }

@keyframes dot-sweep {
  from { transform: scaleX(0); }
  to   { transform: scaleX(1); }
}

@media (max-width: 560px) {
  .film__dot { width: 34px; }
}

@media (prefers-reduced-motion: reduce) {
  .film__track { transition: none; }
  .film__dot.is-active::after { animation: none; transform: scaleX(1); }
  .vcard { opacity: 1; }
}

.vcard__frame {
  position: relative;
  aspect-ratio: 16 / 9;
  overflow: hidden;
  border-radius: 11px;
  border: 1px solid rgba(255,255,255,.14);
  background: var(--void-2);
  display: flex;
  align-items: center;
  justify-content: center;
}

.vcard__loop,
.vcard__full {
  position: absolute;
  inset: 0;
  width: 100%;
  height: 100%;
  object-fit: cover;
  background: var(--void-2);
}
.vcard__loop { filter: brightness(.74) saturate(.92); transition: filter .6s ease; }
.vcard:hover .vcard__loop { filter: brightness(.9) saturate(1); }
.vcard__full { display: none; object-fit: contain; background: #000; }

.vcard.is-playing .vcard__loop { display: none; }
.vcard.is-playing .vcard__full { display: block; }
.vcard.is-playing .vcard__play,
.vcard.is-playing .vcard__dur { opacity: 0; pointer-events: none; }

.vcard__play {
  position: relative;
  width: clamp(58px, 5.5vw, 84px);
  aspect-ratio: 1;
  border-radius: 50%;
  border: 1px solid rgba(255,255,255,.5);
  background: rgba(0,0,0,.28);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: border-color .4s ease, color .4s ease, transform .4s ease,
              background .4s ease, opacity .4s ease;
}
.vcard__play svg { width: 34%; margin-left: 6%; }
.vcard__play:hover {
  border-color: var(--red);
  color: var(--red);
  background: rgba(0,0,0,.5);
  transform: scale(1.07);
}
.vcard__play:focus-visible { outline: 2px solid var(--red); outline-offset: 4px; }

.vcard__dur {
  position: absolute;
  right: 14px;
  bottom: 12px;
  font-size: 11px;
  letter-spacing: .18em;
  color: #fff;
  background: rgba(0,0,0,.45);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  padding: 5px 10px;
  border-radius: 999px;
  transition: opacity .4s ease;
}

.vcard__meta { padding-top: 22px; }
.vcard__title {
  font-family: var(--serif);
  font-weight: 400;
  font-size: clamp(22px, 2.2vw, 30px);
  line-height: 1.18;
  color: var(--white);
}
.vcard__desc {
  margin-top: 12px;
  max-width: 48ch;
  font-size: 14px;
  color: #a3a3a8;
}
.vcard__tags {
  list-style: none;
  display: flex;
  flex-wrap: wrap;
  gap: 8px;
  margin-top: 20px;
}
.vcard__tags li {
  font-size: 10px;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: #c9c9ce;
  border: 1px solid rgba(255,255,255,.18);
  border-radius: 999px;
  padding: 6px 14px;
}

.film__note {
  max-width: 1400px;
  margin: clamp(48px, 6vh, 80px) auto 0;
  font-size: 13px;
  letter-spacing: .04em;
  color: #77777c;
}

/* ============================================================
   LIGHTBOX
   ============================================================ */
.tile__img { position: relative; }

.tile__zoom {
  position: absolute;
  right: 14px;
  bottom: 14px;
  width: 44px;
  aspect-ratio: 1;
  border-radius: 50%;
  border: 1px solid rgba(255,255,255,.55);
  background: rgba(0,0,0,.3);
  backdrop-filter: blur(4px);
  -webkit-backdrop-filter: blur(4px);
  color: #fff;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  opacity: 0;
  transform: translateY(6px);
  transition: opacity .35s ease, transform .35s ease, border-color .3s ease, color .3s ease;
}
.tile__zoom svg { width: 19px; }
.tile:hover .tile__zoom,
.tile__zoom:focus-visible { opacity: 1; transform: none; }
/* A touch device never hovers, so the affordance would never appear and the
   photographs would look like flat images rather than something to open. */
@media (hover: none) {
  .tile__zoom { opacity: 1; transform: none; }
}
.tile__zoom:hover { border-color: var(--red); color: var(--red); }
.tile__zoom:focus-visible { outline: 2px solid var(--red); outline-offset: 3px; }

[data-zoom] { cursor: zoom-in; }
[data-zoom] img { cursor: zoom-in; }

/* ---------- overlay ---------- */
.lb {
  position: fixed;
  inset: 0;
  z-index: 200;
  display: grid;
  grid-template-columns: auto 1fr auto;
  align-items: center;
  gap: clamp(4px, 2vw, 28px);
  padding: clamp(52px, 7vh, 88px) clamp(8px, 3vw, 48px);
  background: rgba(8,8,9,.94);
  backdrop-filter: blur(14px);
  -webkit-backdrop-filter: blur(14px);
  opacity: 0;
  transition: opacity .32s ease;
}
.lb[hidden] { display: none; }
.lb.is-open { opacity: 1; }

.lb__stage {
  grid-column: 2;
  display: flex;
  flex-direction: column;
  align-items: center;
  justify-content: center;
  min-width: 0;
  min-height: 0;
  height: 100%;
  gap: 20px;
}

.lb__img {
  max-width: 100%;
  max-height: calc(100% - 64px);
  width: auto;
  height: auto;
  object-fit: contain;
  border-radius: 3px;
  box-shadow: 0 30px 90px rgba(0,0,0,.6);
  transform: scale(.985);
  opacity: 0;
  transition: opacity .4s ease, transform .5s cubic-bezier(.22,.61,.36,1);
}
.lb__img.is-ready { opacity: 1; transform: none; }

.lb__cap {
  display: flex;
  align-items: baseline;
  flex-wrap: wrap;
  justify-content: center;
  gap: 6px 18px;
  text-align: center;
}
.lb__title {
  font-size: 12px;
  letter-spacing: .22em;
  text-transform: uppercase;
  color: #fff;
}
.lb__cat {
  font-family: var(--serif);
  font-size: 17px;
  color: var(--red);
}
.lb__count {
  font-size: 11px;
  letter-spacing: .2em;
  color: #77777c;
}

/* ---------- controls ---------- */
.lb__close,
.lb__nav {
  border: 1px solid rgba(255,255,255,.24);
  background: rgba(255,255,255,.04);
  color: #e9e9e6;
  border-radius: 50%;
  display: flex;
  align-items: center;
  justify-content: center;
  cursor: pointer;
  transition: border-color .3s ease, color .3s ease, background .3s ease, transform .3s ease;
}
.lb__close:hover,
.lb__nav:hover { border-color: var(--red); color: var(--red); background: rgba(230,51,41,.08); }
.lb__close:focus-visible,
.lb__nav:focus-visible { outline: 2px solid var(--red); outline-offset: 3px; }

.lb__close {
  position: absolute;
  top: clamp(14px, 2.5vh, 28px);
  right: clamp(14px, 3vw, 40px);
  width: 44px;
  aspect-ratio: 1;
}
.lb__close svg { width: 18px; }

.lb__nav {
  width: clamp(40px, 4vw, 54px);
  aspect-ratio: 1;
  flex: none;
}
.lb__nav svg { width: 20px; }
.lb__nav--prev { grid-column: 1; }
.lb__nav--next { grid-column: 3; }

body.lb-open { overflow: hidden; }

@media (max-width: 640px) {
  .lb { grid-template-columns: 1fr; padding: 64px 12px 84px; }
  .lb__stage { grid-column: 1; }
  .lb__nav {
    position: absolute;
    bottom: 20px;
    grid-column: auto;
  }
  .lb__nav--prev { left: 28%; }
  .lb__nav--next { right: 28%; }
}

@media (prefers-reduced-motion: reduce) {
  .lb, .lb__img { transition: none; }
  .lb__img { transform: none; }
}

/* ============================================================
   SMALL SCREENS & TOUCH
   Last in the file on purpose: these override base rules declared earlier,
   and a media query adds no specificity of its own.
   ============================================================ */

/* Uppercase at .28em tracking is hard enough to read; at 10px on a phone it
   stops being worth setting. Raised only here, so the desktop composition is
   left as drawn. */
@media (max-width: 760px) {
  .enquiry label { font-size: 12px; letter-spacing: .2em; }
  .vcard__tags li { font-size: 11px; }
}

/* Controls a finger has to find. 44px is the smallest reliable target; several
   of these were text links with no padding, so they get the height without
   moving anything around them. */
@media (hover: none) {
  .nav__brand,
  .footer__brand { min-height: 44px; }

  .footer__links a,
  .contact__mail {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
  }

  .enquiry select,
  .enquiry input:not([tabindex="-1"]),
  .enquiry textarea { min-height: 44px; }
}
