/* ==========================================================================
   Martin Gabriel Holdings — stylesheet

   One file, no framework, no external request. The design rests on four
   things and very little else: a high-contrast serif for anything that
   speaks, a neutral grotesque for anything that informs, hairline rules
   instead of boxes, and a great deal of unused space. A private holding
   company should look like it is in no hurry.
   ========================================================================== */

/* ------------------------------------------------------------------ fonts */

@font-face {
  font-family: 'Playfair Display';
  src: url('/assets/fonts/playfair-400.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Playfair Display';
  src: url('/assets/fonts/playfair-400i.woff2') format('woff2');
  font-weight: 400;
  font-style: italic;
  font-display: swap;
}
@font-face {
  font-family: 'Playfair Display';
  src: url('/assets/fonts/playfair-500.woff2') format('woff2');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Playfair Display';
  src: url('/assets/fonts/playfair-600.woff2') format('woff2');
  font-weight: 600;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Inter';
  src: url('/assets/fonts/inter-300.woff2') format('woff2');
  font-weight: 300;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Inter';
  src: url('/assets/fonts/inter-400.woff2') format('woff2');
  font-weight: 400;
  font-style: normal;
  font-display: swap;
}
@font-face {
  font-family: 'Inter';
  src: url('/assets/fonts/inter-500.woff2') format('woff2');
  font-weight: 500;
  font-style: normal;
  font-display: swap;
}

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

:root {
  /* Paper, not white. A holding company's stationery is never #fff. */
  --paper: #f7f6f3;
  --ink: #16161a;
  --ink-2: #4c4c50;
  --ink-3: #71716a;

  --rule: #e0dfda;
  --rule-strong: #c9c8c2;

  /* One accent, used sparingly. Change this line and the whole site moves. */
  --accent: #788a64;
  --accent-deep: #5d6a4e;
  --accent-wash: #eceee6;

  /* The dark band. Ink, not black — pure black on paper reads as a hole. */
  --night: #14140f;
  --night-ink: #f4f3ee;
  --night-ink-2: #a9a89f;
  --night-rule: #35352d;

  --serif: 'Playfair Display', 'Iowan Old Style', Georgia, 'Times New Roman', serif;
  --sans: 'Inter', -apple-system, BlinkMacSystemFont, 'Segoe UI', Helvetica, Arial, sans-serif;

  --gutter: clamp(24px, 5vw, 76px);
  --measure: 1240px;
  --rail: 190px;

  /*
    One easing curve for the whole site, so every movement on the page feels
    like it comes from the same hand.

    This is an exponential ease-out. It covers roughly 80% of its distance in
    the first third of its duration and then spends the remaining two thirds
    arriving. Things that decelerate like that read as heavy — as objects with
    mass being set down, rather than graphics being animated. It is the single
    most important line in this stylesheet for how expensive the site feels.
  */
  --ease-glide: cubic-bezier(0.19, 1, 0.22, 1);
}

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

html {
  -webkit-text-size-adjust: 100%;
  /* The browser's own smooth scroll, for anyone without the script. It gives
     no control over duration — the browser decides, and for a long page it
     decides fast. That is precisely why the script takes it over. */
  scroll-behavior: smooth;
  scroll-padding-top: 110px;
}
@media (prefers-reduced-motion: reduce) {
  html {
    scroll-behavior: auto;
  }
}

/* When the script is driving, the native smoothing must be off, or the browser
   and the animation loop fight over the same scroll position and the result
   stutters. */
html.js-reveal {
  scroll-behavior: auto;
}

body {
  margin: 0;
  background: var(--paper);
  color: var(--ink);
  font-family: var(--sans);
  font-size: 17px;
  line-height: 1.65;
  -webkit-font-smoothing: antialiased;
  -moz-osx-font-smoothing: grayscale;
}

img {
  max-width: 100%;
  height: auto;
  display: block;
}

.shell {
  max-width: var(--measure);
  margin: 0 auto;
  padding-inline: var(--gutter);
}

.skip {
  position: absolute;
  left: -9999px;
  top: 0;
  background: var(--ink);
  color: var(--paper);
  padding: 12px 20px;
  z-index: 100;
  font-size: 12px;
  letter-spacing: 0.14em;
  text-transform: uppercase;
}
.skip:focus {
  left: 0;
}

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

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

.site-header {
  position: sticky;
  top: 0;
  z-index: 20;
  background: color-mix(in srgb, var(--paper) 88%, transparent);
  backdrop-filter: saturate(140%) blur(10px);
  -webkit-backdrop-filter: saturate(140%) blur(10px);
}

.site-header__inner {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 28px;
  flex-wrap: wrap;
  padding-block: 22px;
  border-bottom: 1px solid var(--rule);
}

.monogram {
  display: inline-flex;
  align-items: center;
  gap: 16px;
  text-decoration: none;
  color: var(--ink);
}

/* The double-ruled box is the mark. It is drawn, not an image, so it stays
   crisp at every density and costs nothing to load. */
.monogram__box {
  font-family: var(--serif);
  font-size: 19px;
  letter-spacing: 0.06em;
  line-height: 1;
  padding: 11px 13px 9px;
  border: 1px solid var(--ink);
  box-shadow: inset 0 0 0 3px var(--paper), inset 0 0 0 4px var(--ink);
  transition: color 0.2s ease;
}

.monogram__name {
  font-family: var(--serif);
  font-size: clamp(17px, 1.7vw, 20px);
  letter-spacing: 0.005em;
}

.monogram:hover .monogram__name {
  color: var(--accent-deep);
}

.nav {
  display: flex;
  align-items: center;
  gap: clamp(10px, 1.6vw, 22px);
  flex-wrap: wrap;
}

.nav a {
  font-size: 11.5px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-2);
  text-decoration: none;
  padding-bottom: 3px;
  border-bottom: 1px solid transparent;
  transition: color 0.18s ease, border-color 0.18s ease;
}
.nav a:hover {
  color: var(--ink);
  border-bottom-color: var(--accent);
}

.nav__sep {
  color: var(--rule-strong);
  font-size: 11px;
  user-select: none;
}

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

.hero {
  padding-block: clamp(64px, 12vw, 160px) clamp(48px, 7vw, 96px);
}

.hero__title {
  font-size: clamp(46px, 10.5vw, 132px);
  line-height: 0.94;
  letter-spacing: -0.025em;
  margin: 0;
  font-weight: 400;
}

/*
  The reveal.

  The brief was "premium, high-end, dynamic". Premium, in typography, is not
  motion for its own sake — it is weight and inevitability. So: nothing bounces,
  nothing loops, nothing repeats on scroll. The name rises once from behind an
  edge, the rule draws itself, and the page settles. Two seconds, then still
  for ever.

  Two details do the work.

  The first is the easing. cubic-bezier(0.16, 1, 0.3, 1) covers most of its
  distance immediately and then takes a long time to arrive. Motion that
  decelerates like that reads as heavy — as something with mass being set down
  rather than a graphic being animated.

  The second is the tracking. Each line begins fractionally tighter than it
  ends and opens to its final letter-spacing as it rises, so the type appears
  to settle into position rather than slide into it. It is the difference
  between a word arriving and a word being placed. Nobody consciously notices
  it; it is the reason the whole thing feels expensive.
*/

.hero__mask {
  display: block;
  overflow: hidden;
  /*
    Overflow clips at the padding box, so this is how much of the glyph below
    the line box survives — and with line-height at 0.94 the line box ends
    above the baseline, so a descender needs real room.

    0.16em was not enough and the tail of the 'g' in "Holdings" was cut. At
    132px that gave 21px of clearance for a descender that reaches about 29px.
    0.34em gives 45px, which clears it with margin to spare at every size in
    the clamp.

    The negative margin cancels the padding for layout, so the two lines stay
    optically set solid however much room the clip is given.
  */
  padding-bottom: 0.44em;
  margin-bottom: -0.44em;
}

.hero__line {
  display: block;
  will-change: transform, opacity;
  transform: translateY(120%);
  opacity: 0;
  /*
    3200ms, up from 2600ms. Slow is not the same as calm, and the difference is
    where the time goes. On this curve roughly four fifths of the distance is
    covered in the first third of the duration, so lengthening it does not make
    the name crawl up the page — it makes the last fraction of the arrival take
    longer, which is the part that reads as weight. The type appears to come to
    rest rather than to stop.
  */
  animation: hero-rise 3200ms var(--ease-glide) forwards;
}

/* The surname carries a beat of delay. Read aloud, "Martin Gabriel Holdings"
   has that pause in it too. The gap has widened twice now, each time with the
   duration: at a slower speed a short gap stops reading as a beat and starts
   reading as a stumble. */
.hero__mask:nth-child(1) .hero__line {
  animation-delay: 300ms;
}
.hero__mask:nth-child(2) .hero__line {
  animation-delay: 740ms;
}

@keyframes hero-rise {
  from {
    transform: translateY(120%);
    opacity: 0;
    letter-spacing: -0.055em;
  }
  to {
    transform: translateY(0);
    opacity: 1;
    letter-spacing: -0.025em;
  }
}

/*
  THE LIGHT PASS.

  Once the name has settled, a band of light crosses it left to right, as if
  the letterforms were a polished surface catching a source moving past. It
  happens once. A sheen that loops is a button on a bargain website; a sheen
  that happens once is light in a room.

  It ends by running off the last letter and simply not being there any more.
  There was a flare here for a while — a specular catch on the trailing edge,
  and it worked — but it was one event too many. The light crossing the name is
  the idea; a second thing happening at the end of it competes with the idea
  rather than completing it. Restraint is the more expensive choice, and the
  version without it is better.

  How it works: the text is painted with a gradient instead of a flat colour,
  the gradient is clipped to the glyphs, and the gradient — not the text — is
  what moves. So the light appears to travel *through* the letters rather than
  over them, which is what makes it read as a reflection rather than an
  overlay.

  THE DANGER, AND THE GUARD.

  Clipping a background to text requires the text's own colour to be
  transparent. Get that wrong and the masthead disappears. Three things prevent
  it:

    1. The whole thing sits inside @supports. A browser that cannot clip a
       background to text never sees `color: transparent` at all.
    2. The gradient is built so that its resting position — before the
       animation, and after it, and if the animation never runs at all — shows
       nothing but ink. The light lives at 72% along a gradient that is two and
       a half times the width of the line, so at rest it is parked off to one
       side, outside the visible slice.
    3. `forwards` holds the finished state, which is again solid ink.

  In other words the animation only ever *adds* the light. Remove the animation
  by any means — reduced motion, an old engine, a stalled paint — and what is
  left is the name in ink, exactly as before.
*/
@supports ((background-clip: text) or (-webkit-background-clip: text)) {
  .hero__line {
    /*
      The geometry here is the whole trick, and it is easy to get wrong — the
      first attempt did.

      The gradient is three times the width of the line, so at any moment only
      a third of it is visible. Parked left (0%) you see the gradient's first
      third; parked right (100%) you see its last third. The light therefore
      has to live strictly between those two — anywhere inside 33%–67% — or it
      is still showing when the animation finishes and the name stays lit.

      It sits at 50%, in a band from 42% to 58%. Hidden at both ends, visible
      only in transit.
    */
    background-image: linear-gradient(
      96deg,
      var(--ink) 0%,
      var(--ink) 42%,
      #3c3d46 45%,
      #8f919d 47.5%,
      #d7d9e0 49.2%,
      #eceef2 50%,
      #d7d9e0 50.8%,
      #8f919d 52.5%,
      #3c3d46 55%,
      var(--ink) 58%,
      var(--ink) 100%
    );
    background-size: 300% 100%;
    /*
      Starts parked RIGHT, and travels to LEFT — which sends the light across
      the name from left to right, not the other way about.

      That is the opposite of the intuition, and I got it backwards first time.
      background-position: 100% shows the gradient's right-hand third; as the
      value falls towards 0% the window slides left through the image, so a
      feature in the middle of the gradient first appears at the LEFT edge of
      the text and exits at the right. Moving the window left moves the visible
      feature right.
    */
    background-position: 100% 0;
    background-repeat: no-repeat;
    -webkit-background-clip: text;
    background-clip: text;
    color: transparent;
    -webkit-text-fill-color: transparent;

    /*
      THE DESCENDER, AND WHY IT WAS BEING CUT.

      Once the text is painted by a background rather than a colour, the paint
      is bounded by this element's own box. WebKit enforces that strictly. With
      line-height at 0.94 the line box ends *above* the bottom of the glyphs,
      so the tail of the 'g' in "Holdings" fell outside the box and was sliced
      off — a flat horizontal cut, which is exactly what it looked like.

      It only started happening when the light pass was added, because until
      then the letters were painted with `color`, which no box constrains.

      Chromium is more forgiving here, which is why it looked right in testing
      and wrong in Safari. The fix is not the mask — that had room. It is to
      give the painted element itself enough box to hold its own descenders,
      then cancel that padding for layout so nothing moves.
    */
    padding-bottom: 0.34em;
    margin-bottom: -0.34em;

    /*
      Shrink-to-fit. The gradient is sized to this box, so a box that ends at
      the last letter means the light crosses exactly the width of the name
      rather than the width of the column — the sweep is tied to the letters
      instead of merely passing over them, and it leaves the name at the
      trailing edge of the final glyph rather than somewhere out in the margin.
    */
    width: fit-content;

    /* Two animations on one element: the entrance, and then the light. The
       entrance owns transform, opacity and tracking; the light owns nothing
       but background-position. They never touch the same property. */
    /* The entrance eases, because it is an object being set down. The light
       does not, because a reflection has no reason to accelerate — a source
       moving past a surface at a steady rate is what this is imitating, and
       any easing here makes the band rush through the middle of the name and
       linger at the edges where nothing can be seen. Linear, and longer. */
    animation: hero-rise 3200ms var(--ease-glide) forwards,
      hero-sheen 2800ms linear forwards;
  }

  /*
    Delays are per-animation and in the same order as the names above.

    WHY 1800ms AND NOT 2800ms.

    The sheen used to start at 2.8s, which was chosen as the point where the
    first line's rise animation mathematically ends. That turned out to be the
    wrong quantity twice over, and the result was a hero that visibly paused
    before the light arrived.

    First, the name stops moving long before its animation stops running. On
    this curve it is 99% of the way home at 59% of its duration — 2.19s — and
    the last one per cent of a 120% rise is about two pixels. Nothing after
    that is visible movement.

    Second, and larger: the delay is not when the light appears. The band sits
    in the middle of a gradient three times the width of the line, so it opens
    its travel off the leading edge of the text where there is nothing to light
    up. Measured rather than guessed — see tools/sheen-onset.js — the first
    trace of it lands at 16% of the sweep and it is properly present by 20%,
    which is about half a second of animation that looks like nothing
    happening.

    Half a second of nothing, plus six tenths of a second of a name that had
    already stopped moving, is the pause. So the sheen is pulled a full second
    earlier: 1800ms + ~500ms of invisible travel puts the first light on the
    letters at about 2.3s, which is where the eye says the name has landed.
  */
  .hero__mask:nth-child(1) .hero__line {
    animation-delay: 300ms, 1800ms;
  }
  .hero__mask:nth-child(2) .hero__line {
    /* The second line is caught a moment later, as a single source crossing a
       surface at an angle would catch it. The 180ms interval between the two
       is the part that matters and is preserved exactly. */
    animation-delay: 740ms, 1980ms;
  }

  @keyframes hero-sheen {
    to {
      background-position: 0% 0;
    }
  }
}

/* Short, not full width. The rule is a signature, not a divider — and it
   draws itself, left to right, once the name has landed.

   Everything below the name moves on the same clock as the name, so slowing
   the title means slowing these too. Left alone they would finish first, and
   the signature arriving before the name it sits under is the choreography
   backwards. */
.hero__rule {
  border: 0;
  border-top: 1px solid var(--rule-strong);
  margin: clamp(34px, 5vw, 56px) 0 clamp(28px, 4vw, 40px);
  width: min(56%, 620px);
  transform-origin: left center;
  transform: scaleX(0);
  animation: hero-draw 2200ms var(--ease-glide) 1720ms forwards;
}

@keyframes hero-draw {
  to {
    transform: scaleX(1);
  }
}

.hero__lede,
.hero__aside {
  opacity: 0;
  animation: hero-settle 1800ms var(--ease-glide) forwards;
}
.hero__lede {
  animation-delay: 2340ms;
}
.hero__aside {
  animation-delay: 2700ms;
}

@keyframes hero-settle {
  from {
    opacity: 0;
    transform: translateY(14px);
  }
  to {
    opacity: 1;
    transform: translateY(0);
  }
}

.hero__foot {
  display: flex;
  align-items: flex-end;
  justify-content: space-between;
  gap: 32px;
  flex-wrap: wrap;
}

.hero__lede {
  font-size: clamp(19px, 2.1vw, 27px);
  line-height: 1.45;
  font-weight: 300;
  color: var(--ink-2);
  max-width: 30ch;
  margin: 0;
}

.hero__aside {
  font-size: 11px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin: 0;
  white-space: nowrap;
}

/* --------------------------------------------------------------- sections */

/*
  Every section is a two-column grid: a narrow left rail carrying an oversized
  serif numeral and its label, and the content beside it. It is the device the
  previous build used, and it is the right one — it numbers the argument and
  gives the page a spine. Here it is applied to all seven sections rather than
  the first four, so the page reads as one document.
*/

.section {
  padding-block: clamp(58px, 8vw, 118px);
  border-top: 1px solid var(--rule);
}
.section--flush {
  border-top: 0;
}

.section__grid {
  display: grid;
  grid-template-columns: var(--rail) minmax(0, 1fr);
  gap: clamp(28px, 5vw, 76px);
  align-items: start;
}

.rail {
  position: sticky;
  top: 116px;
}

.rail__number {
  font-family: var(--serif);
  font-size: clamp(50px, 6vw, 76px);
  line-height: 0.86;
  color: var(--accent);
  letter-spacing: -0.02em;
  display: block;
}

.rail__label {
  display: block;
  margin-top: 14px;
  font-size: 10.5px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-3);
}

.section__heading {
  font-size: clamp(30px, 4.4vw, 54px);
  line-height: 1.08;
  letter-spacing: -0.02em;
  margin: 0;
  max-width: 18ch;
}

.section__prose {
  margin-top: clamp(24px, 3vw, 38px);
}

.section__extra {
  margin-top: clamp(34px, 4.5vw, 58px);
}

/* The one dark band. It breaks the length of the page and gives the
   methodology the weight of a statement rather than a list. */
.section--dark {
  background: var(--night);
  color: var(--night-ink);
  border-top-color: var(--night);
}
.section--dark .rail__number {
  color: var(--night-ink-2);
}
.section--dark .rail__label,
.section--dark .prose p {
  color: var(--night-ink-2);
}
.section--dark .section__heading {
  color: var(--night-ink);
}
.section--dark .tenet {
  border-top-color: var(--night-rule);
}
.section--dark .tenet__term {
  color: var(--night-ink);
}
.section--dark .tenet__gloss {
  color: var(--night-ink-2);
}
.section--dark .tenet__numeral {
  color: #66665a;
}

/* ------------------------------------------------------------------- type */

.display {
  font-family: var(--serif);
  font-weight: 400;
  color: var(--ink);
  letter-spacing: -0.015em;
  margin: 0;
}

.prose {
  max-width: 60ch;
}
.prose p {
  margin: 0 0 1.3em;
  color: var(--ink-2);
}
.prose p:last-child {
  margin-bottom: 0;
}

.stroke {
  width: 56px;
  height: 2px;
  background: var(--accent);
  border: 0;
  margin: 26px 0 0;
}

.arrow-link {
  display: inline-flex;
  align-items: center;
  gap: 10px;
  font-size: 11.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink);
  text-decoration: none;
  margin-top: 46px;
}
.arrow-link::after {
  content: '\2192';
  font-size: 14px;
  transition: transform 0.2s ease;
}
.arrow-link:hover {
  color: var(--accent-deep);
}
.arrow-link:hover::after {
  transform: translateX(4px);
}

/* -------------------------------------------------------------- companies */

.holdings {
  display: grid;
}

.holding {
  padding-block: clamp(30px, 4vw, 48px);
  border-top: 1px solid var(--rule);
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: clamp(18px, 4vw, 56px);
  align-items: start;
}
.holding:first-child {
  border-top: 0;
  padding-top: 0;
}
/* With no aside to hold the second column open, the entry uses the full
   measure rather than reserving a right-hand margin for a link that is not
   there. */
.holding--no-site {
  grid-template-columns: minmax(0, 1fr);
}

.holding__name {
  font-family: var(--serif);
  font-size: clamp(26px, 3vw, 36px);
  font-weight: 400;
  line-height: 1.15;
  margin: 0;
}

.holding__what {
  margin: 12px 0 0;
  color: var(--ink-2);
  max-width: 44ch;
}

.holding__meta {
  margin: 16px 0 0;
  font-size: 13px;
  color: var(--ink-3);
}

/* A company's systems, one level in. The left rule is the hierarchy: it says
   these belong to the entry above rather than sitting alongside it. */
.systems {
  margin-top: 24px;
  padding-left: clamp(16px, 2vw, 26px);
  border-left: 1px solid var(--rule);
}

.systems__label {
  margin: 0 0 12px;
  font-size: 10px;
  letter-spacing: 0.22em;
  text-transform: uppercase;
  color: var(--ink-3);
}

.system {
  margin: 0 0 22px;
  max-width: 44ch;
}
.system:last-child {
  margin-bottom: 0;
}

/* Name on its own line, gloss beneath — the same order the company entries
   above use. Running them inline saved a line and cost the alignment. */
.system__name {
  display: block;
  font-family: var(--serif);
  font-size: 20px;
  line-height: 1.2;
  color: var(--ink);
  margin-bottom: 5px;
}

.system__what {
  display: block;
  color: var(--ink-2);
  font-size: 15px;
  line-height: 1.55;
}

/* A required disclosure, set as a particular rather than as small print. Same
   size and colour as the address beneath it, because both are simply facts
   about the thing above them, and a disclosure typeset apologetically invites
   the reader to wonder what is being apologised for. */
.system__note {
  display: block;
  margin-top: 6px;
  font-size: 12px;
  line-height: 1.5;
  color: var(--ink-3);
}

.system__site {
  display: block;
  margin-top: 6px;
  font-size: 12px;
  color: var(--ink-3);
  text-decoration: none;
  border-bottom: 1px solid transparent;
  width: fit-content;
  transition: color 0.18s ease, border-color 0.18s ease;
}
.system__site:hover {
  color: var(--accent-deep);
  border-bottom-color: var(--accent);
}

.holding__aside {
  padding-top: 10px;
  text-align: right;
}

.holding__site {
  font-size: 13px;
  color: var(--ink);
  text-decoration: none;
  border-bottom: 1px solid var(--rule-strong);
  padding-bottom: 2px;
  white-space: nowrap;
  transition: color 0.18s ease, border-color 0.18s ease;
}
.holding__site:hover {
  color: var(--accent-deep);
  border-bottom-color: var(--accent);
}

/* ---------------------------------------------------------------- tenets */

.tenets {
  display: grid;
}

.tenet {
  display: grid;
  grid-template-columns: 76px minmax(0, 1fr);
  gap: clamp(16px, 3vw, 36px);
  padding-block: clamp(26px, 3.4vw, 42px);
  border-top: 1px solid var(--rule);
  align-items: start;
}
.tenet:first-child {
  border-top: 0;
  padding-top: 0;
}

.tenet__numeral {
  font-family: var(--serif);
  font-size: clamp(24px, 2.6vw, 32px);
  line-height: 1;
  color: var(--rule-strong);
  /* Sit the numeral's cap height on the term's cap height rather than its
     box on the term's box, which reads as a misalignment. */
  margin-top: -5px;
}

.tenet__term {
  font-size: 11.5px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  font-weight: 500;
  margin: 0 0 12px;
  color: var(--ink);
}

.tenet__gloss {
  margin: 0;
  color: var(--ink-2);
  max-width: 56ch;
}

/* ----------------------------------------------------------- particulars */

/* A ruled table without being a table: two columns of label-and-value that
   collapse to one on a narrow screen. */

.particulars {
  margin: 0;
  display: grid;
  grid-template-columns: repeat(2, minmax(0, 1fr));
  border: 1px solid var(--rule);
}

/* Cells carry only a top and left rule; the container supplies the outside.
   That way no cell has to know whether it is last in its row, and an odd
   number of entries cannot leave a hairline hanging in mid-air. */
.particulars__row {
  padding: clamp(20px, 2.6vw, 30px);
  border-top: 1px solid var(--rule);
  border-left: 1px solid var(--rule);
}
.particulars__row:nth-child(-n + 2) {
  border-top: 0;
}
.particulars__row:nth-child(2n + 1) {
  border-left: 0;
}
/* An odd final entry takes the full width rather than leaving a void. */
.particulars__row:last-child:nth-child(2n + 1) {
  grid-column: 1 / -1;
}

.particulars dt {
  font-size: 10.5px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-bottom: 10px;
}

.particulars dd {
  margin: 0;
  font-family: var(--serif);
  font-size: clamp(19px, 2vw, 24px);
  line-height: 1.2;
  color: var(--ink);
}

/* --------------------------------------------------------------- the form */

.form {
  max-width: 620px;
}

.field {
  margin-bottom: 30px;
}

.field label {
  display: block;
  font-size: 10.5px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--ink-3);
  margin-bottom: 10px;
}

.field input,
.field textarea {
  width: 100%;
  font-family: var(--sans);
  font-size: 16px;
  line-height: 1.5;
  color: var(--ink);
  background: transparent;
  border: 0;
  border-bottom: 1px solid var(--rule-strong);
  padding: 9px 2px;
  border-radius: 0;
  transition: border-color 0.18s ease;
}
.field input:focus,
.field textarea:focus {
  outline: none;
  border-bottom-color: var(--accent-deep);
}
.field textarea {
  resize: vertical;
  min-height: 128px;
}

.field--error input,
.field--error textarea {
  border-bottom-color: #9d4530;
}
.field__note {
  margin: 8px 0 0;
  font-size: 12.5px;
  color: #9d4530;
}

/* Off-screen rather than display:none — some bots skip anything hidden that
   way, and this one is meant to be filled in by them. */
.trap {
  position: absolute;
  left: -9999px;
  width: 1px;
  height: 1px;
  overflow: hidden;
}

.button {
  display: inline-block;
  font-family: var(--sans);
  font-size: 11.5px;
  letter-spacing: 0.2em;
  text-transform: uppercase;
  color: var(--paper);
  background: var(--ink);
  border: 1px solid var(--ink);
  padding: 16px 40px;
  margin-top: 6px;
  cursor: pointer;
  transition: background 0.18s ease, border-color 0.18s ease;
}
.button:hover {
  background: var(--accent-deep);
  border-color: var(--accent-deep);
}

.notice {
  border-left: 2px solid var(--accent);
  background: var(--accent-wash);
  padding: 18px 22px;
  margin-bottom: 32px;
  font-size: 15px;
  color: var(--ink-2);
}
.notice--warn {
  border-left-color: #9d4530;
  background: #f4ebe8;
}

/* ------------------------------------------------------------ legal pages */

.legal {
  max-width: 760px;
}

.legal__title {
  font-size: clamp(38px, 6vw, 66px);
  line-height: 1.04;
}

.legal__updated {
  margin: 26px 0 0;
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-3);
}

.legal__body {
  margin-top: clamp(36px, 5vw, 56px);
  max-width: 68ch;
}

.legal__h {
  font-size: clamp(21px, 2.3vw, 27px);
  line-height: 1.25;
  margin: 2.2em 0 0.8em;
}
.legal__h:first-child {
  margin-top: 0;
}

/* ------------------------------------------------------------------ inbox */

/* The enquiry list. Same typography as the rest of the site, because there is
   no reason for an internal page to look like a different company. */

.inbox {
  margin-top: clamp(30px, 4vw, 46px);
}

.enquiry {
  padding-block: clamp(24px, 3.2vw, 36px);
  border-top: 1px solid var(--rule);
}
.enquiry:first-of-type {
  border-top: 0;
  padding-top: 0;
}

.enquiry__when {
  margin: 0 0 10px;
  font-size: 10.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-3);
}

.enquiry__who {
  font-family: var(--serif);
  font-size: clamp(21px, 2.3vw, 27px);
  font-weight: 400;
  line-height: 1.2;
  margin: 0 0 8px;
  color: var(--ink);
}

.enquiry__org {
  color: var(--ink-3);
  font-size: 0.72em;
}

.enquiry__contact {
  margin: 0 0 16px;
  font-size: 14px;
  color: var(--ink-2);
}
.enquiry__contact a {
  color: var(--accent-deep);
  text-decoration: none;
  border-bottom: 1px solid var(--rule-strong);
  padding-bottom: 1px;
}
.enquiry__contact a:hover {
  border-bottom-color: var(--accent);
}

.enquiry__actions {
  margin: 20px 0 0;
  display: flex;
  gap: 26px;
  align-items: center;
  flex-wrap: wrap;
}

.enquiry__reply,
.enquiry__delete {
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  text-decoration: none;
  padding-bottom: 3px;
  border-bottom: 1px solid transparent;
  transition: border-color 0.18s ease, color 0.18s ease;
}

.enquiry__reply {
  color: var(--ink);
}
.enquiry__reply:hover {
  color: var(--accent-deep);
  border-bottom-color: var(--accent);
}

/* Deliberately quiet. Destructive actions should not be the brightest thing
   on the page, and this one sits beside a client's message. */
.enquiry__delete {
  color: var(--ink-3);
}
.enquiry__delete:hover {
  color: #9d4530;
  border-bottom-color: #9d4530;
}

.confirm {
  margin-top: 36px;
  display: flex;
  align-items: center;
  gap: 28px;
  flex-wrap: wrap;
}

.confirm__cancel {
  font-size: 11px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-2);
  text-decoration: none;
  border-bottom: 1px solid var(--rule-strong);
  padding-bottom: 3px;
}
.confirm__cancel:hover {
  color: var(--ink);
}

.button--danger {
  background: #9d4530;
  border-color: #9d4530;
}
.button--danger:hover {
  background: #833826;
  border-color: #833826;
}

/* Enquiries arrive as typed, newlines and all. */
.enquiry__message {
  margin: 0;
  color: var(--ink-2);
  white-space: pre-wrap;
  max-width: 66ch;
}

.notice code {
  font-family: ui-monospace, SFMono-Regular, Menlo, monospace;
  font-size: 0.9em;
  background: rgba(0, 0, 0, 0.05);
  padding: 1px 5px;
  border-radius: 2px;
}

/* ============================================================ on scrolling

   Everything below is scroll-driven: the browser scrubs these animations
   against the scroll position rather than against a clock, so the page
   responds to the reader instead of performing at them. No JavaScript is
   involved, which matters here — the Content-Security-Policy forbids script
   outright, and a page whose content depends on script is a page that is
   blank until the script runs.

   TWO GUARDS, AND THEY ARE NOT OPTIONAL.

   The @supports test means a browser without scroll-driven animations never
   sees the opacity:0 starting state at all — it gets the plain, finished page.
   The reduced-motion test means someone who has asked their machine for less
   movement gets the same. Content that is hidden by default and revealed by
   motion is content that disappears entirely wherever the motion does not run,
   so the hiding has to live inside the same guard as the revealing.

   The distances are deliberately small: 20px and a fade. Reveals that travel
   far enough to notice are reveals you notice instead of the words.
   ========================================================================= */

/*
  EVERY RULE HERE IS SCOPED TO .js-reveal, AND THAT IS THE WHOLE SAFETY MODEL.

  That class is put on <html> by /assets/reveal.js and by nothing else. If the
  script is blocked, fails, or the visitor has asked for reduced motion, the
  class never appears, none of these rules match, and the page renders finished
  — every word visible. Nothing on this site is hidden by the stylesheet alone.

  The distances are small on purpose: 20px and a fade. A reveal that travels
  far enough to notice is a reveal you notice instead of the words.
*/

.js-reveal .rail__number,
.js-reveal .rail__label,
.js-reveal .section__heading,
.js-reveal .section__prose,
.js-reveal .holding,
.js-reveal .tenet,
.js-reveal .particulars,
.js-reveal .systems,
.js-reveal .legal__body > * {
  opacity: 0;
  transform: translateY(20px);
  transition: opacity 1700ms var(--ease-glide), transform 1700ms var(--ease-glide);
}

.js-reveal .rail__number.is-in,
.js-reveal .rail__label.is-in,
.js-reveal .section__heading.is-in,
.js-reveal .section__prose.is-in,
.js-reveal .holding.is-in,
.js-reveal .tenet.is-in,
.js-reveal .particulars.is-in,
.js-reveal .systems.is-in,
.js-reveal .legal__body > .is-in {
  opacity: 1;
  transform: translateY(0);
}

/* The rail numeral is the largest thing in a section and the first the eye
   lands on, so it travels a little further and takes a little longer. */
.js-reveal .rail__number {
  transform: translateY(30px);
  transition-duration: 2100ms;
}

/* Within a company entry or a tenet the children arrive together, but the
   entries themselves are separated in time by where they sit on the page —
   which is a truer stagger than any fixed delay, because it follows the
   reader rather than a clock. */

/*
  The masthead recedes rather than scrolling away. --recede runs 0 to 1 as the
  hero leaves the viewport, written by the script; the numbers that decide how
  it looks stay here, with the rest of the design.
*/
.js-reveal .hero__title {
  --recede: 0;
  opacity: calc(1 - (var(--recede) * 0.88));
  transform: translateY(calc(var(--recede) * -46px));
}

/* ======================================================= on choosing a menu

   Clicking GROUP or COMPANIES scrolls the page — but a scroll alone leaves the
   reader to work out where they have landed. So the section they asked for
   announces itself once: its numeral warms to the accent colour and settles
   back to grey over two seconds.

   :target is a CSS selector for "the element the current URL points at", which
   is how this happens with no script. It re-runs on every visit to the anchor,
   including a second click on the same menu item.
   ========================================================================= */

@media (prefers-reduced-motion: no-preference) {
  /* The numeral darkens to full ink and settles back to the accent. Going to
     ink rather than a deeper green is what makes it legible at a glance — the
     first version moved between two greens four percent apart and was, quite
     rightly, invisible. */
  .section:target .rail__number,
  .section.is-arrived .rail__number {
    animation: arrived 2600ms var(--ease-glide) both;
  }

  @keyframes arrived {
    0% {
      color: var(--ink);
      transform: scale(1.07);
    }
    30% {
      color: var(--ink);
      transform: scale(1);
    }
    100% {
      color: var(--accent);
      transform: scale(1);
    }
  }

  /* A hairline draws itself under the section's heading and then withdraws —
     the page underlining the thing you asked for and then getting out of the
     way. */
  .section__heading {
    position: relative;
  }
  .section:target .section__heading::after,
  .section.is-arrived .section__heading::after {
    content: '';
    position: absolute;
    left: 0;
    bottom: -14px;
    height: 1px;
    width: 0;
    background: var(--accent);
    animation: underline-sweep 2600ms var(--ease-glide) both;
  }

  @keyframes underline-sweep {
    0% {
      width: 0;
      opacity: 1;
    }
    38% {
      width: 100%;
      opacity: 1;
    }
    100% {
      width: 100%;
      opacity: 0;
    }
  }

  /* On the dark band, ink would be a hole. It warms to white instead — same
     gesture, legible ground. */
  .section--dark:target .rail__number,
  .section--dark.is-arrived .rail__number {
    animation-name: arrived-dark;
  }
  .section--dark:target .section__heading::after,
  .section--dark.is-arrived .section__heading::after {
    background: var(--night-ink);
  }

  @keyframes arrived-dark {
    0% {
      color: var(--night-ink);
      transform: scale(1.07);
    }
    30% {
      color: var(--night-ink);
      transform: scale(1);
    }
    100% {
      color: var(--night-ink-2);
      transform: scale(1);
    }
  }
}

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

.site-footer {
  border-top: 1px solid var(--rule);
  padding-block: clamp(42px, 5vw, 66px);
}

.site-footer__grid {
  display: grid;
  grid-template-columns: minmax(0, 1fr) auto;
  gap: 32px;
  align-items: start;
}

.site-footer__mark {
  font-family: var(--serif);
  font-size: 19px;
  margin: 0 0 12px;
}

.site-footer__small {
  font-size: 13px;
  color: var(--ink-3);
  margin: 0;
  max-width: 54ch;
  line-height: 1.75;
}

.site-footer__links {
  display: flex;
  gap: 26px;
  flex-wrap: wrap;
  justify-content: flex-end;
}
.site-footer__links a {
  font-size: 10.5px;
  letter-spacing: 0.18em;
  text-transform: uppercase;
  color: var(--ink-3);
  text-decoration: none;
}
.site-footer__links a:hover {
  color: var(--ink);
}

/* ------------------------------------------------------------ narrow view */

@media (max-width: 940px) {
  .section__grid {
    grid-template-columns: 1fr;
    gap: clamp(18px, 4vw, 28px);
  }
  .rail {
    position: static;
    display: flex;
    align-items: baseline;
    gap: 16px;
  }
  .rail__number {
    font-size: 40px;
  }
  .rail__label {
    margin-top: 0;
  }
  .holding {
    grid-template-columns: 1fr;
  }
  .holding__aside {
    text-align: left;
    padding-top: 14px;
  }
  .particulars {
    grid-template-columns: 1fr;
  }
  .particulars__row {
    border-left: 0;
    border-top: 1px solid var(--rule);
  }
  .particulars__row:first-child {
    border-top: 0;
  }
  .site-footer__grid {
    grid-template-columns: 1fr;
  }
  .site-footer__links {
    justify-content: flex-start;
  }
}

@media (max-width: 620px) {
  body {
    font-size: 16px;
  }
  .monogram__name {
    display: none;
  }
  .nav {
    gap: 8px;
  }
  .nav a {
    font-size: 10px;
    letter-spacing: 0.14em;
  }
  .hero__foot {
    align-items: flex-start;
    flex-direction: column;
    gap: 22px;
  }
  .hero__lede {
    max-width: none;
  }
  .tenet {
    grid-template-columns: 1fr;
    gap: 8px;
  }
}

/* ------------------------------------------------------------------ print */

@media print {
  .nav,
  .site-footer__links,
  .skip,
  .form {
    display: none;
  }
  .site-header {
    position: static;
  }
  body {
    background: #fff;
    color: #000;
    font-size: 11pt;
  }
  .section {
    padding-block: 18pt;
    break-inside: avoid;
  }
  .section--dark {
    background: #fff;
    color: #000;
  }
  .section--dark .section__heading,
  .section--dark .tenet__term {
    color: #000;
  }
  .section--dark .prose p,
  .section--dark .tenet__gloss,
  .section--dark .rail__label {
    color: #333;
  }
}

@media (prefers-reduced-motion: reduce) {
  *,
  *::before,
  *::after {
    transition-duration: 0.01ms !important;
    animation-duration: 0.01ms !important;
    animation-delay: 0ms !important;
  }

  /* The hero's resting state is invisible, because the animation brings it in.
     Cancelling the animation without this would cancel the hero. Anything that
     starts hidden and is revealed by motion needs its visible state restored
     here explicitly — a blank page is a worse outcome than a moving one. */
  .hero__line,
  .hero__lede,
  .hero__aside,
  .hero__title {
    opacity: 1;
    transform: none;
    letter-spacing: inherit;
  }

  /* No light pass either. Paint the name flat and be done — this is also the
     belt to the braces on the background-clip trick, since a flat colour here
     cannot be transparent whatever the gradient is doing. */
  .hero__line {
    background-image: none;
    color: var(--ink);
    -webkit-text-fill-color: var(--ink);
  }
  .hero__rule {
    transform: scaleX(1);
  }

  /* The scroll reveals are already inside a no-preference guard and so never
     apply here. This is belt and braces: if one is ever added outside that
     guard by mistake, this stops it hiding anything. */
  .rail__number,
  .rail__label,
  .section__heading,
  .section__prose,
  .holding,
  .tenet,
  .particulars,
  .systems,
  .legal__body > * {
    opacity: 1;
    transform: none;
  }
}
