/* =============================================================================
   PRISM II — SHARED CHROME. The header, the mobile sheet, the contact band and
   the footer. Every page carries all four.

   Two of these are new rather than restyled, and both were P0 findings against
   the previous mock: below 920px the five stream links were hidden with nothing
   in their place, and only the home page had a footer at all. From the software
   page there was no route to any other page on the site. The whole premise of
   this restructure is that one page became six, and it failed on the majority
   device.
   ============================================================================= */

/* -----------------------------------------------------------------------------
   THE BODY MODEL, and the distinction that keeps getting lost.

   The footer used to be position: fixed at the viewport bottom, which made body a
   flex column with min-height 100dvh AND a padding-bottom reserving the footer's
   measured height — a figure application.js wrote to --footer-h on every resize.
   All of that went when the footer became an ordinary block, correctly: it was
   76px of dead space under every page.

   What is below is NOT that scaffolding coming back. It is the ordinary
   sticky-footer model, and the difference is the reserve. The footer stays in
   flow, nothing measures it, no custom property is published, and no space is set
   aside for it. `main` grows into whatever is left of the viewport, and that is
   the only reason the footer lands at the bottom of a page shorter than the
   window — where it otherwise floated mid-screen with blank ground beneath it
   (437px of it on /labs/listening-post, measured). Delete these three rules and
   that comes straight back.

   Scoped to .site-shell rather than declared on bare body because layouts/admin
   renders the same stylesheet bundle and has no footer to push down.
   -------------------------------------------------------------------------- */
body.site-shell {
  display: flex;
  flex-direction: column;
  min-height: 100dvh;
}
/* Every other child of <body> — the icon sprite, the sheet, the Front Desk
   overlay, Intercom's injected trio — is absolute, fixed or display:none, so
   these two are the whole in-flow column alongside the sticky header. */
body.site-shell > main { flex: 1 0 auto; }
body.site-shell > .ft-band { flex-shrink: 0; }

/* -----------------------------------------------------------------------------
   HEADER

   Three parts: the wordmark, the five streams, and the two asks. The asks are
   ordered by commitment — Contact is the open-ended one and sits as plain text,
   the free audit is the specific one and takes a pill, because a pill is what a
   first-time visitor presses and "a free audit" is the easier thing to say yes
   to than "get in touch". That pill is .btn.soft rather than .btn.primary:
   the full fill belongs to the contact band, which is a page's one full-weight
   ask, and a sticky header wearing the same fill outranked the page beneath it.

   `top: 0`, not the 49px the mockup carries: that figure is the height of the
   mockup's own tab bar, which is proposal chrome and does not exist here.
   -------------------------------------------------------------------------- */
.nav {
  position: sticky;
  top: 0;
  z-index: 20;
  background: var(--bg);
  border-bottom: 1px solid var(--line);
}
.nav .in {
  display: flex;
  align-items: center;
  justify-content: space-between;
  gap: 20px;
  max-width: var(--maxw);
  margin: 0 auto;
  padding: 12px var(--gutter);
}
.nav .mark {
  display: inline-flex;
  align-items: center;
  min-height: 44px;
  padding-right: 8px;
  font-family: var(--mono);
  font-size: 13px;
  line-height: 1;
  letter-spacing: .16em;
  text-transform: uppercase;
  white-space: nowrap;
  font-weight: 500;
}
.nav .links { display: flex; gap: 4px; }
.nav .links a {
  font-size: 15px;
  color: var(--ink2);
  padding: 11px 10px;
  white-space: nowrap;
  transition: color 150ms ease;
}
/* The current page, marked. Colour alone would be the only signal, so it also
   takes the ink weight of a heading rather than body text. */
.nav .links a.on { color: var(--ink); }
.nav .links a:hover { color: var(--ink); }

.nav .end { display: flex; align-items: center; gap: 8px; white-space: nowrap; }
.nav .end a.text { font-size: 15px; padding: 11px 10px; }

/* 48px square, clear of the 44px tap-target floor. No border or background —
   the icon renders on its own, unboxed. Hidden above 920 by default and shown
   by the breakpoint at the foot of this file, so a desktop reader never meets
   it.

   THE BOX WAS NEVER THE PROBLEM; THE GLYPH WAS. At 46px this already cleared
   every tap-target rule and still read as small, because svg.ico is 18px at
   1.5 stroke — a size chosen for icons sitting inline beside 15px type — so
   the drawn mark occupied 39% of a control that is the only navigation on a
   phone. It is now 24px at 1.75, which is what the rest of the row is weighted
   for: the wordmark beside it is 13px mono at .16em tracking, and an 18px hairline
   burger read lighter than the letters it sits opposite. The -8px margin pulls
   the box's optical edge back out to the gutter, since the extra width would
   otherwise inset the icon from the page edge the mark aligns to. */
.burger {
  display: none;
  align-items: center;
  justify-content: center;
  width: 48px;
  height: 48px;
  margin-right: -8px;
  background: none;
  color: var(--ink);
  cursor: pointer;
}
.burger svg.ico { width: 24px; height: 24px; stroke-width: 1.75; }

/* Hidden until the breakpoint claims it. Both halves are needed: the base rule
   keeps the markup out of the way on desktop, and the min-width rule below the
   breakpoint block stops a sheet left open on a rotating phone from covering a
   desktop-width layout. */
.sheet { display: none; }

/* -----------------------------------------------------------------------------
   CONTACT BAND

   The last thing on five of the six pages, and the only full-weight ask on any of
   them. Its second button is always the free audit, because the audit costs a
   visitor nothing and every page should offer the smaller step beside the larger
   one. The 160deg wash runs warm to cool: a person, then the machine.
   -------------------------------------------------------------------------- */
/* No border-bottom: the footer's own .ft-band rule draws the line at exactly this
   position and draws it on every page, including the ones with no contact band.
   Two full-bleed hairlines flush against each other read as a 2px line. */
.contact {
  padding: 104px 0;
  background: linear-gradient(160deg,
    color-mix(in srgb, var(--apricot) 40%, var(--bg)),
    color-mix(in srgb, var(--lilac) 22%, var(--bg)) 70%,
    color-mix(in srgb, var(--ice) 40%, var(--bg)));
}
.contact h2 { max-width: 13ch; }
.contact .reassure {
  margin-top: 22px;
  font-family: var(--mono);
  font-size: 13px;
  letter-spacing: .02em;
  color: var(--ink2);
}

/* -----------------------------------------------------------------------------
   FOOTER

   Four columns, and on every page. The heading of each column is the label voice.
   Links are a compact plain-text list, not CTAs, so they don't carry the site's
   44px pill tap target — that rule is for actions, and a footer link list taking
   the same vertical real estate as body copy read as bloated.

   .ft-band is full-bleed and footer.ft is inside .wrap, which is why the hairline
   that delineates the footer as its own section lives on the wrapper: on .ft it
   would stop at the column edge and read as a shorter second line under the
   contact band's. It is the site's ordinary hairline — .essay, .ladder, .labs
   and .cv all draw the same one. (.proof was in this list until 2026-09-04,
   when the row component was deleted; the board that replaced it draws its
   panel borders from --line2 instead.)
   -------------------------------------------------------------------------- */
.ft-band { border-top: 1px solid var(--line); }

footer.ft {
  padding: 40px 0 48px;
  display: grid;
  grid-template-columns: 1.5fr 1fr 1fr 1fr;
  gap: 24px;
  font-size: 13px;
  color: var(--ink2);
}
footer.ft > div { min-width: 0; }
footer.ft b {
  display: block;
  font-family: var(--mono);
  font-size: 12px;
  letter-spacing: .11em;
  text-transform: uppercase;
  color: var(--ink3);
  margin-bottom: 14px;
}
footer.ft a,
footer.ft .ft-line { display: block; padding: 3px 0; line-height: 1.5; }
footer.ft a { transition: color 150ms ease; }
footer.ft a:hover { color: var(--ink); }

/* THE ROWS TOUCHED EXACTLY, WHICH IS DIFFERENT FROM BEING COMPACT.

   The note above is right that a footer link list is not a CTA and should not
   carry the 44px pill target — but at 390px these measured 342 x 25.5px with a
   vertical gap between adjacent links of 0.0px, three pairs running. Boxes that
   share an edge have no separation at all: a thumb landing on the boundary opens
   whichever link the browser rounds to, and on a phone that is most of them.

   25.5px clears WCAG 2.5.8 AA, whose floor is 24px, so this is not a conformance
   fix. It is the one place on the site where two different destinations are
   pressable with nothing between them, and the cheapest correction is spacing
   rather than height: 4px of separation costs 36px down a nine-row column and
   makes every row its own object.

   KEYED ON WIDTH, NOT ON `pointer: coarse`, and the first attempt used the
   pointer query because it describes the actual problem — a thumb, not a cursor.
   It is the wrong lever twice: a desktop browser at a narrow window reports a
   fine pointer, which is how this page is most often looked at while it is being
   built, so the rule would be invisible to the person checking it AND absent for
   a real reader on a laptop. Every other breakpoint in this project is a width,
   and a rule nobody can verify is a rule that quietly stops working. */
@media (max-width: 620px) {
  footer.ft a,
  footer.ft .ft-line { padding: 6px 0; }
  footer.ft a + a,
  footer.ft .ft-line + .ft-line { margin-top: 4px; }
}

/* -----------------------------------------------------------------------------
   RESPONSIVE
   -------------------------------------------------------------------------- */
/* THE ROW STOPS FITTING BEFORE THE BREAKPOINT DOES, and the document scrolled
   sideways for it. Measured: the mark, five links, "Book a conversation" and the
   Free audit pill have a minimum right edge of 1036px, while the desktop nav runs
   from 921px up — so every viewport from 921 to 1055 (1024 among them, which is an
   iPad in landscape) overflowed by up to 12px, on every page of the site.
   The secondary text CTA is what goes: it is already hidden below 920, it is in the
   footer and the sheet, and the pill beside it makes the same offer. Dropping it
   takes ~200px off the row's minimum, which clears 921 with room. */
/* 960, DOWN FROM 1059 (2026-09-04), AND THE NUMBER IS MEASURED RATHER THAN
   ESTIMATED.

   The rule exists because the desktop row stops fitting before its own
   breakpoint does: the nav runs from 921px up, and the row's minimum right edge
   was 1036px, so every viewport from 921 to 1055 - an iPad in landscape among
   them - scrolled the document sideways by up to 12px. Hiding the secondary text
   ask below 1059 was the fix.

   Replacing "Book a conversation" with "Contact" took 76px off that item and 88px
   off the row. Measured live on /about at 1440: mark 107, links 580, end 189,
   two 20px gaps, 64px of gutter, and the row now overflows at 921 (by 27px) and
   fits exactly at 948. So the rule moves to 960, which is the measured minimum
   plus 12px of slack for font rendering that differs by platform.

   The payoff is not tidiness: a 1024px iPad in landscape now keeps the text ask,
   where before it lost it. Re-measure rather than adjust by eye if any item in
   the row changes its label - both failure modes here are silent, and a
   screenshot at 1440 shows nothing wrong either way. */
@media (max-width: 959px) {
  .nav .end a.text { display: none; }
}

@media (max-width: 920px) {
  .nav .links { display: none; }
  .burger { display: inline-flex; }

  footer.ft { grid-template-columns: 1fr 1fr; }
  .contact { padding: 72px 0; }

  /* THE MOBILE SHEET. A full-height takeover rather than a dropdown, because the
     thing it has to communicate is that this site has six pages — a cramped
     dropdown under a 46px button says "a few more links" instead.

     overscroll-behavior contains the scroll chain so a flick at the end of the
     sheet does not start scrolling the page behind it, which on iOS is what makes
     an overlay feel like a layer rather than a hole. The controller also locks the
     document, so the two together cover both browsers' behaviour. */
  .sheet {
    display: block;
    position: fixed;
    inset: 0;
    z-index: 60;
    background: var(--bg);
    padding: 20px var(--gutter) 32px;
    overflow-y: auto;
    overscroll-behavior: contain;
    /* Closed is the resting state; .is-open is added/removed a frame apart from
       the hidden attribute by nav_sheet_controller.js, so there's always a frame
       to transition from. Opacity carries the motion unconditionally, same split
       the press-states block (application.css) already uses — reduced-motion
       visitors still get a quick fade, just not the lift. */
    opacity: 0;
    transition: opacity 140ms ease;
  }
  .sheet[hidden] { display: none; }
  .sheet.is-open { opacity: 1; }
  @media (prefers-reduced-motion: no-preference) {
    .sheet {
      transform: translateY(-8px);
      transition: opacity 180ms cubic-bezier(0.2, 0, 0.2, 1), transform 180ms cubic-bezier(0.2, 0, 0.2, 1);
    }
    .sheet.is-open { transform: translateY(0); }
  }
  .sheet .top {
    display: flex;
    align-items: center;
    justify-content: space-between;
    padding-bottom: 12px;
    border-bottom: 1px solid var(--line);
  }
  /* The same wordmark as the header's, at the same size, so the sheet reads as
     the header expanded rather than as a second thing. min-height carries the
     tap target: it is a link now, and a 13px line of type is not pressable. */
  .sheet .mark {
    display: inline-flex;
    align-items: center;
    min-height: 44px;
    font-family: var(--mono);
    font-size: 13px;
    line-height: 1;
    letter-spacing: .16em;
    text-transform: uppercase;
    font-weight: 500;
    white-space: nowrap;
  }
  /* 56px rows in the display face at 21px. A phone menu is the one place this
     site sets navigation at heading weight, and it earns it: these six rows are
     the entire site. */
  .sheet a.item {
    display: flex;
    align-items: center;
    min-height: 56px;
    font-size: 21px;
    font-family: var(--display);
    font-weight: 500;
    letter-spacing: -.018em;
    border-bottom: 1px solid var(--line);
  }
  .sheet a.item.on { color: var(--accent, var(--cobalt)); }
  .sheet .asks { display: grid; gap: 12px; margin-top: 24px; }
  .sheet .btn { width: 100%; }
  /* THE SECONDARY ASK MATCHES THE HERO'S, AND THE FIGURE IS MEASURED.

     .btn's own fill is `color-mix(--bg 55%, transparent)` — a translucent wash
     that takes its colour from whatever it sits on. On the hero that is the page
     gradient, so it renders as a pale lavender pill; the sheet's own ground IS
     --bg, and --bg mixed into --bg at any opacity is just --bg, so here the same
     rule gave the button no visible fill at all, only its border.

     --bg2 was the first attempt and is wrong for a reason worth recording: it is
     a WARM cream (#F5F1E9) against a hero button that renders cool, so the two
     matched in lightness and opposed in temperature — the mismatch reads instantly
     side by side. The hero gradient's own middle stop is `--lilac 40%` mixed
     against --bg, and 55% white over that composites to srgb(.9548 .9598 .9965).
     `--lilac 18%` against --bg resolves to exactly that, to four decimal places,
     so this is the hero's rendered colour restated rather than approximated.

     A light hue as a wash, never as type — the rule colour_vocabulary_test pins.
     Hover lightens to solid --bg, which is where the base .btn:hover already
     travels; that rule loses to this selector's specificity, so it is restated. */
  .sheet .btn:not(.primary) { background: color-mix(in srgb, var(--lilac) 18%, var(--bg)); }
  .sheet .btn:not(.primary):hover { background: var(--bg); }
}

/* Not merely tidy: a sheet opened on a phone and then rotated, or a window
   dragged wider mid-session, would otherwise leave a fixed full-screen panel over
   a desktop layout with its close button hidden by the rule above. */
@media (min-width: 921px) {
  .sheet { display: none !important; }
}

@media (max-width: 560px) {
  footer.ft { grid-template-columns: 1fr; }
}
