/* ═══════════════════════════════════════════════════════════════════
   Staggered menu — the drawer behind the burger.

   The burger in the header has been sitting there since the first build
   with nothing behind it. This is what it opens.

   The staggered part is the entrance: two plain colour layers sweep in
   from the right one after the other, the panel follows, and only then
   do the items arrive one at a time. Three sheets of paper being laid
   down, not a box appearing. Motion lives in js/menu.js.

   Every colour, face and curve here is a token from style.css — there is
   no palette of its own. The one new value is the layer it sits on.
   ═══════════════════════════════════════════════════════════════════ */

:root{
  /* above the nav (100) and the contact button (90): the drawer covers
     the whole chrome while it is open */
  --z-menu: 200;
}

.menu{
  position:fixed; inset:0; z-index:var(--z-menu);
  /* Parked: no paint, no hit area, and out of the tab order. Not the
     hidden attribute — that cannot be transitioned, and the panel has to
     be able to slide rather than blink. */
  visibility:hidden;
  pointer-events:none;
}
.menu.is-open{
  visibility:visible;
  pointer-events:auto;
}

/* The scrim is only there to catch a click outside the panel and to sink
   the page a little. Deliberately not a blur: the hero behind it is a
   video, and blurring a playing video costs more than the effect is
   worth. */
.menu__scrim{
  position:absolute; inset:0;
  background:rgba(45,45,45,.42);
  opacity:0;
}

/* ── the layers ───────────────────────────────────────────────────── */
/* Two sheets that arrive before the panel does. They carry the brand
   accent, which is the only place colour appears in the drawer — the
   panel itself is the page's own black.

   The leading sheet is the logo's green now, not the deepest bronze.
   It is on screen for about a fifth of a second and then gone, which is
   the one condition under which this colour can be used at full
   strength and at full height: nothing is read against it, so none of
   the contrast that rules it out everywhere else applies. Opening the
   menu is a flash of the brand and then the page's own paper.

   --olive-hi rather than --olive, and that is a value decision, not a
   contrast one: the sheet behind it is --ink, and the sequence only
   reads as depth if the first sheet is darker than the second. The
   brand green at full brightness is lighter than the beige it hands
   over to, which turns the arrival inside out. */
.menu__pre{
  position:absolute; top:0; bottom:0; right:0;
  width:min(94vw,34rem);
  transform:translate3d(101%,0,0);
  will-change:transform;
}
/* Three sheets, and they are a gradation rather than three colours that
   happen to arrive together. Each is lighter than the one before it, so
   the drawer opens out of the dark and hands over to the panel instead
   of flashing at it:

     brown  #4A2E14  the deepest thing on the site
     green  #54661F  the logo's, one step up in value
     beige  #F5F2EE  the bridge to the panel's off-white

   The order is doing the work. Two dark sheets and then a light one is
   an arrival; the same three shuffled is a strobe. Value is what has to
   climb — the hues can be whatever the brand says. */
.menu__pre--1{ background:var(--bronze-dp) }
.menu__pre--2{ background:var(--olive-hi) }
.menu__pre--3{ background:var(--ink) }

/* ── the panel ────────────────────────────────────────────────────── */
.menu__panel{
  position:absolute; top:0; bottom:0; right:0;
  width:min(94vw,34rem);
  background:var(--black);
  /* the only edge in the whole drawer, and it is a hairline */
  border-left:1px solid var(--hair);

  display:flex; flex-direction:column; justify-content:center;
  gap:clamp(2.5rem,7vh,4.5rem);
  padding:calc(var(--navh) + clamp(1.5rem,5vh,3rem))
          clamp(1.75rem,6vw,4rem)
          calc(clamp(1.5rem,5vh,3rem) + env(safe-area-inset-bottom));

  transform:translate3d(101%,0,0);
  will-change:transform;
  overflow-y:auto;
  overscroll-behavior:contain;
}

/* ── navigation ───────────────────────────────────────────────────── */
.menu__list{
  list-style:none; margin:0; padding:0;
}
.menu__list li{
  will-change:transform,opacity;
}
.menu__list li + li{ margin-top:clamp(.4rem,1.6vh,1rem) }

.menu__link{
  position:relative;
  display:flex; align-items:baseline;
  /* comfortably past the 44px minimum at every size */
  padding:clamp(.45rem,1.4vh,.8rem) 0;
  font-family:var(--display); font-weight:400;
  font-size:var(--fs-major);
  line-height:1.05;
  letter-spacing:.02em;
  text-transform:uppercase;
  color:var(--bone);
  transition:color .3s var(--ease-out);
}
/* The numerals are gone. They came from a CSS counter on the list, so
   removing them was removing the counter rather than editing markup —
   and nothing in the HTML has to change, which is why five pages did
   not need touching.

   What they were for: an ordered list of five items reads as a
   sequence, and the leading zeros made that sequence look considered.
   What they cost: a second column of type in a panel with five words in
   it, and five words do not need an index. The order is still an
   argument — who the firm is, then what it has built, then what it
   sells — but that argument is made by the order itself, not by
   printing a number in front of it.

   The list stays an <ol> in the markup. It is still ordered; it just no
   longer says so out loud.

   The base hover, and the fallback for every case where the roll below
   does not exist: no JavaScript, and reduced motion — js/textroll.js
   returns before splitting anything in both. */
@media (hover:hover) and (pointer:fine){
  .menu__link:hover{ color:var(--bronze-lt) }
}
.menu__link:active{ color:var(--bronze) }

/* ═══ text roll ═══════════════════════════════════════════════════ */
/* Ported from Skiper58 / TextRoll. Two copies of the word, split into
   letters by js/textroll.js and stacked exactly on top of each other;
   on hover the top copy rolls up out of the box and the second rolls up
   into it. The letters do not leave together — each carries its own
   --d, measured from the middle of the word outwards, so the centre
   goes first and the two ends go last.

   The classes are generic because the effect is: put [data-roll] on
   anything and it works. It lives in this file because the menu is the
   only place using it — if a second place appears, this block is what
   moves out.

   Everything here is transform-only. The source ran it on framer-motion
   variants; transitions do the same job without a library and without
   touching the main thread. */
/* Two measurements, and they are deliberately different.

   The CROP was .8em, on the reasoning that .8em is a little over the cap
   height so the capitals sit in it whole. Measured, that is wrong for
   this face. Against an em of 100 and a line-height of 1, Marcellus puts
   its baseline at 84.5 — already 4.5 below a crop of 80 — and its ink
   runs to 86, because the capitals overshoot the baseline by 1.5. Every
   word in the menu was losing 6 hundredths of an em off its feet.

   PROJECTS lost far more: Marcellus draws a descending uppercase J, so
   that word's ink reaches 106.8 and the whole tail of the J fell outside
   the window.

   So the crop is the full ink extent — 106.8 — plus a little air: 1.12em.

   The TRAVEL has to clear that crop, and it can no longer be the letter's
   own box. It used to be translateY(100%), which resolves against
   line-height:1 and so travels exactly 1em — less than the 1.06em of ink
   it now has to carry off screen, which would leave the foot of the J
   showing at the top of the window. It is written out as a length
   instead, one notch above the crop, and the two are declared together
   here so they cannot drift apart again.

   The rule the old note was reaching for still holds: travel must exceed
   the ink, and the crop must not exceed the travel. Set any two of these
   equal and a duplicate shows inside the window. */
.roll{
  position:relative;
  display:block;
  /* the box the letters roll out of and into — without this they simply
     slide past the reader */
  overflow:hidden;
  height:1.12em;
  --roll-travel:1.18em;
}
.roll__row{
  display:block;
  white-space:nowrap;
  line-height:1;
}
/* Sat exactly over the first, so the two are one object rather than two
   lines of type. */
.roll__row--b{ position:absolute; inset:0 }

.roll__c{
  display:inline-block;
  /* ease-in-out and not the site's usual ease-out: this is movement
     across the screen rather than something entering it, and the letter
     leaving and the letter arriving are one continuous motion. The
     duration is the source's 0.3s, nudged up because this curve holds
     longer at both ends. */
  transition:transform .34s var(--ease-in-out);
  transition-delay:var(--d,0ms);
}
/* Parked. Copy A is in the box, copy B is one box height below it. */
.roll__row--a .roll__c{ transform:translateY(0) }
.roll__row--b .roll__c{ transform:translateY(var(--roll-travel)) }

/* The colour change the plain :hover rule above used to do on its own,
   handed to the roll instead: the word that leaves is bone, the word
   that arrives is bronze. Two feedbacks for one hover would be one too
   many, so A is pinned rather than left to inherit the hovered colour
   on its way out. */
.roll__row--a{ color:var(--bone) }

@media (hover:hover) and (pointer:fine){
  .menu__link:hover .roll__row--a .roll__c{ transform:translateY(calc(var(--roll-travel) * -1)) }
  .menu__link:hover .roll__row--b .roll__c{ transform:translateY(0) }
}
/* Keyboard gets it too. The focus ring already says where you are; this
   says the same thing in the same language the pointer is answered in. */
.menu__link:focus-visible .roll__row--a .roll__c{ transform:translateY(calc(var(--roll-travel) * -1)) }
.menu__link:focus-visible .roll__row--b .roll__c{ transform:translateY(0) }

/* ── socials ──────────────────────────────────────────────────────── */
/* Secondary by construction: the same micro-caps the roles and labels
   elsewhere on the page use, at a fraction of the navigation's size. */
.menu__socials{
  will-change:transform,opacity;
}
.menu__socials-t{
  font-size:var(--fs-micro); letter-spacing:.22em; text-transform:uppercase;
  color:var(--bone-dim);
  margin:0 0 clamp(.75rem,2.5vh,1.1rem);
}
/* Stacked, not in a row: one per line reads as a short list rather than
   as a strip of chips, and it leaves the block quiet enough to stay
   secondary to the navigation above it. */
.menu__socials ul{
  list-style:none; margin:0; padding:0;
  display:flex; flex-direction:column;
}
/* The mark, then the name. Both were words on their own here while the
   footer and the contact dock drew the official glyphs — three places
   listing the same two accounts, one of them in a different language
   from the other two.

   The paths are the footer's, copied rather than redrawn, so the site
   has one Instagram and one Facebook rather than two of each. The word
   stays beside the mark: this is a list in a drawer, not a row of icon
   buttons, and a bare glyph in a text list reads as decoration. */
.menu__social{
  display:inline-flex; align-items:center;
  gap:.6rem;
  min-height:44px;
  font-size:var(--fs-fine); letter-spacing:.06em;
  color:var(--bone-dim);
  transition:color .3s var(--ease-out);
}
.menu__glyph{
  /* Sized off the label rather than in pixels, so the two stay in
     proportion if the list is ever set larger. */
  width:1.05em; height:1.05em;
  flex:0 0 auto;
  fill:currentColor;
  /* The marks are drawn heavier than the text they sit beside — solid
     shapes against a light letterform — so they are held back a step to
     land at the same weight on the page. */
  opacity:.72;
  transition:opacity .3s var(--ease-out);
}
@media (hover:hover) and (pointer:fine){
  .menu__social:hover{ color:var(--bone) }
  /* the mark comes up with the word, so the row answers as one thing */
  .menu__social:hover .menu__glyph{ opacity:1 }
}
/* Until a URL is filled in, the anchor has no href — so it is correctly
   not a link, not focusable, and not announced as one. */
.menu__social:not([href]){ cursor:default }

/* ── the burger, once it has something to open ────────────────────── */
/* Its two hairlines cross into a mark. Same widths and the same bronze
   the hover state already uses — this is a state on the existing
   control, not a second control. */
.burger__lines span{
  transition:width .3s var(--ease-out), background .3s var(--ease-out),
             transform .34s var(--ease-drawer), opacity .2s linear;
}
.burger.is-open .burger__label,
.burger.is-open .burger__lines span{ background:var(--bronze-lt); color:var(--bronze-lt) }
.burger.is-open .burger__lines span:first-child{
  width:clamp(34px,3.6vw,54px);
  transform:translateY(4.5px) rotate(11deg);
}
.burger.is-open .burger__lines span:last-child{
  transform:translateY(-4.5px) rotate(-11deg);
}

/* the drawer sits over the header, so the burger has to sit over the
   drawer to stay reachable */
.nav{ z-index:var(--z-nav) }
.burger{ position:relative; z-index:calc(var(--z-menu) + 1) }

/* ═══ responsive ══════════════════════════════════════════════════ */

/* Phone — the drawer takes the screen. A 34rem panel on a 390px display
   would leave a sliver of page beside it that reads as a mistake. */
@media (max-width:640px){
  .menu__pre,
  .menu__panel{ width:100% }
  .menu__panel{ border-left:0 }
  .menu__link{ font-size:var(--fs-head) }
}

/* Landscape phones have almost no height: stop centring and let the
   panel scroll, so nothing is clipped off the top. */
@media (max-height:520px) and (orientation:landscape){
  .menu__panel{
    justify-content:flex-start;
    gap:clamp(1.25rem,4vh,2rem);
    padding-top:calc(var(--navh) + 1rem);
  }
  .menu__link{ font-size:var(--fs-title) }
}

/* ═══ reduced motion ══════════════════════════════════════════════ */
/* The drawer still opens and closes — it simply arrives rather than
   sliding. js/menu.js composes the end state directly. */
@media (prefers-reduced-motion:reduce){
  .menu__pre{ display:none }
  .burger.is-open .burger__lines span{ transform:none }
  /* No rule needed for the roll: js/textroll.js never splits the words
     under reduced motion, so .roll and its rows do not exist and the
     plain colour hover further up is what answers the pointer. */
}

/* ── the way out ──────────────────────────────────────────────────── */
/* The panel covers the burger, so the drawer needs its own close. Set
   like the burger opposite — word, then rule — so it reads as the same
   control in its other state.

   Absolutely placed, not part of the flex stack: the panel centres its
   contents, and a button in that column would push the navigation off
   centre to make room for itself. Sat on the nav's own height band, so
   it lands where the burger was rather than somewhere new. */
.menu__close{
  position:absolute;
  top:calc(var(--navh) / 2);
  right:clamp(1.75rem,6vw,4rem);
  transform:translateY(-50%);

  display:flex; align-items:center;
  gap:clamp(.65rem,1.4vw,1.05rem);
  /* past the 44px minimum in both directions without a visible box */
  min-width:44px; min-height:44px;
  justify-content:flex-end;
  cursor:pointer;
}
.menu__close-label{
  font-family:var(--sans);
  font-size:var(--fs-micro);
  letter-spacing:.24em; text-transform:uppercase;
  white-space:nowrap;
  color:var(--bone);
  transition:color .3s var(--ease-out);
}
/* Two hairlines crossed on the same centre — the same 1px rule the
   burger is drawn with, turned. */
.menu__close-x{
  position:relative;
  display:block;
  width:clamp(18px,2vw,22px); height:clamp(18px,2vw,22px);
  flex:0 0 auto;
}
.menu__close-x span{
  position:absolute; left:0; top:50%;
  display:block; width:100%; height:1px;
  background:var(--bone);
  transition:background .3s var(--ease-out), transform .3s var(--ease-out);
}
.menu__close-x span:first-child{ transform:translateY(-50%) rotate(45deg) }
.menu__close-x span:last-child { transform:translateY(-50%) rotate(-45deg) }

@media (hover:hover) and (pointer:fine){
  .menu__close:hover .menu__close-label{ color:var(--bronze-lt) }
  .menu__close:hover .menu__close-x span{ background:var(--bronze-lt) }
  /* The cross opens a little under the cursor — the same idea as the
     burger's top rule shortening, which is the site's one hover trick. */
  .menu__close:hover .menu__close-x span:first-child{ transform:translateY(-50%) rotate(135deg) }
  .menu__close:hover .menu__close-x span:last-child { transform:translateY(-50%) rotate(45deg) }
}
.menu__close:active{ transform:translateY(-50%) scale(.94) }

.menu__close:focus-visible{
  outline:2px solid var(--bronze-hi);
  outline-offset:8px;
}

/* Narrow phones: the cross alone. The same call the burger makes — an X
   at this size is understood without the word, and the panel is tight. */
@media (max-width:420px){
  .menu__close-label{ display:none }
}
